From 5bd42d509f33ae08bb6ef5cd21546cf3943bdaca Mon Sep 17 00:00:00 2001 From: adelnizamutdinov Date: Thu, 24 Sep 2026 15:21:51 +0000 Subject: [PATCH 1/6] Rewrite the CLI in Rust with embedded YouTube.js and native FFmpeg --- .cargo/config.toml | 5 + .github/workflows/ci.yaml | 58 +- .gitignore | 19 +- .golangci.yaml | 68 - .moon/tasks/all.yml | 5 +- Cargo.lock | 3799 +++++++++++++++++++++ Cargo.toml | 38 + README.md | 85 +- THIRD-PARTY-NOTICES.txt | 23 + bench/startup.c | 68 + cmd/listenbox/main.go | 8 - ffmpeg-env.sh | 4 - go.mod | 35 - go.sum | 66 - internal/cli/arguments.go | 131 - internal/cli/arguments_test.go | 159 - internal/cli/auth.go | 122 - internal/cli/cli.dev.yaml | 3 - internal/cli/cli.prod.yaml | 3 - internal/cli/command.go | 721 ---- internal/cli/command_test.go | 259 -- internal/cli/config.go | 132 - internal/cli/config_dev.go | 8 - internal/cli/config_dev_test.go | 18 - internal/cli/config_prod.go | 8 - internal/cli/config_prod_test.go | 18 - internal/cli/config_test.go | 80 - internal/cli/doc.go | 2 - internal/cli/episode_delete.go | 149 - internal/cli/episodes.go | 906 ----- internal/cli/episodes_list_test.go | 154 - internal/cli/episodes_resume_test.go | 98 - internal/cli/episodes_source_test.go | 27 - internal/cli/execute.go | 49 - internal/cli/import.go | 403 --- internal/cli/import_test.go | 445 --- internal/cli/login.go | 310 -- internal/cli/login_test.go | 338 -- internal/cli/main_test.go | 36 - internal/cli/media.go | 279 -- internal/cli/media_audio.go | 291 -- internal/cli/media_test.go | 78 - internal/cli/members.go | 576 ---- internal/cli/show_delete.go | 184 - internal/cli/show_delete_command.go | 58 - internal/cli/shows.go | 305 -- internal/cli/shows_test.go | 114 - internal/cli/testdata/media/README.md | 10 - internal/cli/testdata/media/aac.m4a | Bin 33993 -> 0 bytes internal/cli/testdata/media/opus.webm | Bin 44326 -> 0 bytes internal/cli/testdata/media/video.mp4 | Bin 773076 -> 0 bytes internal/cli/whoami.go | 72 - internal/cli/whoami_test.go | 133 - internal/cli/youtube.go | 459 --- internal/cliconfig/models.go | 7 - js/youtube.ts | 211 ++ moon.yml | 148 +- nub.lock | 770 +++++ package.json | 14 + prepare-ffmpeg.ts | 8 +- publicapi/client.go | 4544 ------------------------- publicapi/models.go | 1332 -------- rust-toolchain.toml | 4 + src/api.rs | 153 + src/audio.rs | 287 ++ src/auth.rs | 126 + src/cliconfig/mod.rs | 15 + src/commands.rs | 331 ++ src/config.rs | 156 + src/episodes.rs | 369 ++ src/events.rs | 83 + src/innertube.rs | 144 + src/main.rs | 282 ++ src/media.rs | 218 ++ src/publicapi/mod.rs | 2940 ++++++++++++++++ src/youtube.rs | 342 ++ tsconfig.json | 13 + 77 files changed, 10533 insertions(+), 13383 deletions(-) create mode 100644 .cargo/config.toml delete mode 100644 .golangci.yaml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 THIRD-PARTY-NOTICES.txt create mode 100644 bench/startup.c delete mode 100644 cmd/listenbox/main.go delete mode 100644 ffmpeg-env.sh delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 internal/cli/arguments.go delete mode 100644 internal/cli/arguments_test.go delete mode 100644 internal/cli/auth.go delete mode 100644 internal/cli/cli.dev.yaml delete mode 100644 internal/cli/cli.prod.yaml delete mode 100644 internal/cli/command.go delete mode 100644 internal/cli/command_test.go delete mode 100644 internal/cli/config.go delete mode 100644 internal/cli/config_dev.go delete mode 100644 internal/cli/config_dev_test.go delete mode 100644 internal/cli/config_prod.go delete mode 100644 internal/cli/config_prod_test.go delete mode 100644 internal/cli/config_test.go delete mode 100644 internal/cli/doc.go delete mode 100644 internal/cli/episode_delete.go delete mode 100644 internal/cli/episodes.go delete mode 100644 internal/cli/episodes_list_test.go delete mode 100644 internal/cli/episodes_resume_test.go delete mode 100644 internal/cli/episodes_source_test.go delete mode 100644 internal/cli/execute.go delete mode 100644 internal/cli/import.go delete mode 100644 internal/cli/import_test.go delete mode 100644 internal/cli/login.go delete mode 100644 internal/cli/login_test.go delete mode 100644 internal/cli/main_test.go delete mode 100644 internal/cli/media.go delete mode 100644 internal/cli/media_audio.go delete mode 100644 internal/cli/media_test.go delete mode 100644 internal/cli/members.go delete mode 100644 internal/cli/show_delete.go delete mode 100644 internal/cli/show_delete_command.go delete mode 100644 internal/cli/shows.go delete mode 100644 internal/cli/shows_test.go delete mode 100644 internal/cli/testdata/media/README.md delete mode 100644 internal/cli/testdata/media/aac.m4a delete mode 100644 internal/cli/testdata/media/opus.webm delete mode 100644 internal/cli/testdata/media/video.mp4 delete mode 100644 internal/cli/whoami.go delete mode 100644 internal/cli/whoami_test.go delete mode 100644 internal/cli/youtube.go delete mode 100644 internal/cliconfig/models.go create mode 100644 js/youtube.ts create mode 100644 nub.lock create mode 100644 package.json delete mode 100644 publicapi/client.go delete mode 100644 publicapi/models.go create mode 100644 rust-toolchain.toml create mode 100644 src/api.rs create mode 100644 src/audio.rs create mode 100644 src/auth.rs create mode 100644 src/cliconfig/mod.rs create mode 100644 src/commands.rs create mode 100644 src/config.rs create mode 100644 src/episodes.rs create mode 100644 src/events.rs create mode 100644 src/innertube.rs create mode 100644 src/main.rs create mode 100644 src/media.rs create mode 100644 src/publicapi/mod.rs create mode 100644 src/youtube.rs create mode 100644 tsconfig.json diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e250816 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +jobs = 8 + +[env] +FFMPEG_DIR = { value = ".cache/ffmpeg", relative = true } diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dbfcaae..a99ff3c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,14 +1,10 @@ name: CI - on: push: - branches: - - master + branches: [master] pull_request: - permissions: contents: read - jobs: check: runs-on: ubuntu-24.04 @@ -16,48 +12,26 @@ jobs: - uses: actions/checkout@v7 with: fetch-depth: 0 - filter: blob:none - - uses: actions/setup-go@v7 - id: go - with: - go-version-file: go.mod - cache: false - - name: Restore Go modules, compiler, and linter caches - id: go-cache - uses: actions/cache@v6 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - .cache/golangci-lint - key: go-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.go.outputs.go-version }}-${{ hashFiles('go.mod', 'go.sum') }}-${{ github.sha }} - restore-keys: | - go-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.go.outputs.go-version }}-${{ hashFiles('go.mod', 'go.sum') }}- - go-v1-${{ runner.os }}-${{ runner.arch }}-${{ steps.go.outputs.go-version }}- - uses: actions/setup-node@v6 with: node-version: "26" + - uses: nubjs/setup-nub@v0 + with: + nub-version: ^0.9.0 + cache: true + - uses: pkgxdev/setup@v5 - uses: moonrepo/setup-toolchain@v0 with: moon-version: "2.5.5" - - run: mkdir -p "$HOME/.cache/go-build" - - name: Check affected tasks - id: ci - run: moon ci - - name: Report Moon CI results - if: "!cancelled() && steps.ci.outcome != 'skipped'" - uses: moonrepo/run-report-action@v1 - with: - access-token: ${{ secrets.GITHUB_TOKEN }} - skip-comment: true - - name: Upload Moon reports - if: always() - uses: actions/upload-artifact@v7 + - run: rustup toolchain install 1.98.1 --profile minimal --component clippy,rustfmt + - uses: actions/cache@v6 with: - name: moon-reports-${{ github.run_attempt }} path: | - .moon/cache/ciReport.json - .moon/cache/runReport.json - if-no-files-found: ignore - include-hidden-files: true - retention-days: 14 + ~/.cargo/registry + ~/.cargo/git + target + .cache/ffmpeg + ~/.pkgx + key: rust-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml', 'prepare-ffmpeg.ts') }} + - run: nub i --frozen-lockfile + - run: moon run cli:check diff --git a/.gitignore b/.gitignore index 0c1fa3d..a034d98 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,5 @@ -/listenbox - -.task/ -.cache/ -dist/ -build/ -coverage/ -.test-results/ -test-results/ -*.test -*.prof -*.pprof -dump-*.json -**/.moon/cache/ +/target/ +/.cache/ +/dist/ +/node_modules/ +/.moon/cache/ diff --git a/.golangci.yaml b/.golangci.yaml deleted file mode 100644 index 5007950..0000000 --- a/.golangci.yaml +++ /dev/null @@ -1,68 +0,0 @@ -version: "2" - -run: - timeout: 5m - tests: true - relative-path-mode: gomod - -linters: - default: all - disable: - - arangolint - - clickhouselint - - depguard - - err113 - - exhaustruct - - exhaustruct_v5 - - forbidigo - - ginkgolinter - - gochecknoglobals - - goheader - - gomodguard - - gomodguard_v2 - - loggercheck - - nilnil - - nlreturn - - noinlineerr - - protogetter - - rowserrcheck - - sloglint - - spancheck - - sqlclosecheck - - tagliatelle - - testifylint - - testpackage - - unqueryvet - - varnamelen - - wrapcheck - - wsl - - wsl_v5 - - zerologlint - settings: - cyclop: - max-complexity: 12 - package-average: 8 - exclusions: - generated: lax - warn-unused: true - paths: - - internal/cliconfig/.* - - publicapi/.* - presets: - - comments - - common-false-positives - - legacy - - std-error-handling - -issues: - max-issues-per-linter: 0 - max-same-issues: 0 - -formatters: - enable: - - gci - - gofumpt - - goimports - exclusions: - generated: strict - warn-unused: true diff --git a/.moon/tasks/all.yml b/.moon/tasks/all.yml index 4dc1d06..54bbc2e 100644 --- a/.moon/tasks/all.yml +++ b/.moon/tasks/all.yml @@ -1,8 +1,5 @@ -env: - GOCACHE: ${HOME}/.cache/go-build - GOTMPDIR: ${HOME}/.cache/go-build taskOptions: - cache: false + cache: true unixShell: bash outputStyle: stream implicitInputs: diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..323304c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3799 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99" +dependencies = [ + "crypto-common 0.2.2", + "inout", +] + +[[package]] +name = "aes" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32" +dependencies = [ + "cipher", + "cpubits", + "cpufeatures 0.3.1", +] + +[[package]] +name = "aes-gcm" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2b8006a0c83f52b62ba44a97b58bf76fe2f70a329e588f67f89691d93d498f" +dependencies = [ + "aead", + "cipher", + "ctr", + "ctutils", + "ghash", +] + +[[package]] +name = "aes-kw" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ac571010bd60765c56085a4f1d412012a9be2663b1a2f2b19b49318653fd0d" +dependencies = [ + "aes", + "const-oid", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.13.2", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn 2.0.119", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block-padding" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "brotlic" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f552f56f302af0006c32b50bfa2bdb4696fd6ba33c3ab9f6225fefdb1efdc680" +dependencies = [ + "brotlic-sys", +] + +[[package]] +name = "brotlic-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afdec5c62bc97b56349053cf66ba503af5c2448591be61c3ad70a5f11b57e574" +dependencies = [ + "cc", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cbc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", + "inout", +] + +[[package]] +name = "clang" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ed30b30e1e17966b55ddd0b9dd2758fd824f0a6da6875c8abb17e6aba47c28" +dependencies = [ + "clang-sys", + "libc", +] + +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "clap_lex" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" +dependencies = [ + "cpubits", + "ctutils", + "hybrid-array", + "num-traits", + "rand_core", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", + "rand_core", +] + +[[package]] +name = "crypto-primes" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8f" +dependencies = [ + "crypto-bigint", + "rand_core", +] + +[[package]] +name = "ctr" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", + "subtle", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto", + "rand_core", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + +[[package]] +name = "der" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a878c850e9e421b20262e9b41f9c860e4785fa07541c266b62ff9d1ef998a80a" +dependencies = [ + "const-oid", + "der_derive", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59600e2c2d636fde9b65e99cc6445ac770c63d3628195ff39932b8d6d7409903" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid", + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ecdsa" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0" +dependencies = [ + "der", + "digest 0.11.3", + "elliptic-curve", + "rfc6979", + "signature", + "zeroize", +] + +[[package]] +name = "ed25519" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2 0.11.0", + "signature", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "elliptic-curve" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65" +dependencies = [ + "base16ct", + "crypto-bigint", + "crypto-common 0.2.2", + "digest 0.11.3", + "ff", + "group", + "hkdf", + "hybrid-array", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "ffmpeg-sys-the-third" +version = "6.0.0+ffmpeg-9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8beaa5d31a4dee3c5a93caf6c0dd3e9d49fb9849c96354f8d619ec6b062036f2" +dependencies = [ + "bindgen", + "cc", + "clang", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ffmpeg-the-third" +version = "6.0.0+ffmpeg-9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf29dd4474510215c218b491acf6c06d27c42cf3579abdc20936290a3817ca31" +dependencies = [ + "bitflags 2.13.2", + "ffmpeg-sys-the-third", + "libc", +] + +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + +[[package]] +name = "find-msvc-tools" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "libz-ng-sys", + "miniz_oxide 0.9.1", + "zlib-rs", +] + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "rand_core", + "wasm-bindgen", +] + +[[package]] +name = "ghash" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" +dependencies = [ + "polyval", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "group" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "halfbrown" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed2f2edad8a14c8186b847909a41fbb9c3eafa44f88bd891114ed5019da09" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7685beb53fc20efc2605f32f5d51e9ba18b8ef237961d1760169d2290d3bee" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "subtle", + "typenum", + "zeroize", +] + +[[package]] +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "block-padding", + "hybrid-array", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom 0.4.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libz-ng-sys" +version = "1.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879917b256f6317769b9f374b435805a8697013098aacce5a38ac106cd6a9469" +dependencies = [ + "cmake", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "listenbox" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "chrono", + "clap", + "ffmpeg-the-third", + "futures-util", + "hex", + "image", + "llrt_modules", + "llrt_utils_watts", + "reqwest", + "rquickjs", + "serde", + "serde_json", + "serde_yaml", + "sha2 0.10.9", + "tempfile", + "tokio", + "tokio-util", + "url", + "uuid", +] + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "llrt_abort" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96f3b00def04dc42f1f16e6b7f09993e007e39aaaf15e1cccd7a78f4de6f0d07" +dependencies = [ + "llrt_async_hooks", + "llrt_events", + "llrt_exceptions", + "llrt_timers", + "llrt_utils_watts", + "rquickjs", +] + +[[package]] +name = "llrt_async_hooks" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b198a6271ad9140062133a4a6633890f2f21158606e60ea0669e3de1a69ba74" +dependencies = [ + "llrt_hooking", + "llrt_utils_watts", + "rquickjs", + "tracing", +] + +[[package]] +name = "llrt_buffer" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73954ae0a8202982b83797ba4a982cdbb6f7bd9d99aafcc0aa45e98026fd7ab8" +dependencies = [ + "itoa", + "llrt_encoding", + "llrt_utils_watts", + "rquickjs", + "ryu", +] + +[[package]] +name = "llrt_build" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e770e9f9d0031d5788ab2f73254cdc9c516042b55db8bddf64d2f409b7015f4a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "llrt_compression" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711835a1111f82abca9eb14c41e47b579f40e82e649405df43c1c559feccab45" +dependencies = [ + "brotlic", + "flate2", + "zstd", +] + +[[package]] +name = "llrt_context" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb4df06dfbfe41aee465176a0fce1823982247230054053642f2f3df76a7449" +dependencies = [ + "llrt_build", + "llrt_utils_watts", + "rquickjs", + "tokio", + "tracing", +] + +[[package]] +name = "llrt_crypto" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53427a112c52b67ccff06c3f09d66c76058784a9b30a4842a71ac968dc81391d" +dependencies = [ + "aes", + "aes-gcm", + "aes-kw", + "cbc", + "const-oid", + "crc32c", + "crc32fast", + "ctr", + "der", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "hkdf", + "hmac", + "llrt_buffer", + "llrt_context", + "llrt_encoding", + "llrt_json", + "llrt_utils_watts", + "md-5", + "once_cell", + "p256", + "p384", + "p521", + "pbkdf2", + "pkcs8", + "rand", + "rquickjs", + "rsa", + "sha1", + "spki", + "x25519-dalek", +] + +[[package]] +name = "llrt_dns_cache" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97794e08c2219ac370739b5a14b50ba151058b13c9c7c2b58cc31fbddf8e9b40" +dependencies = [ + "hyper-util", + "llrt_context", + "llrt_utils_watts", + "quick_cache", + "tokio", + "tower-service", +] + +[[package]] +name = "llrt_encoding" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb7748e8a3cfe2f8091fd6428c604c9bbf636a6972e5480e35fa6c245f92ea5" +dependencies = [ + "base64-simd", + "hex-simd", + "llrt_build", + "memchr", + "phf", +] + +[[package]] +name = "llrt_events" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c2d4a84603ed26e4ab681a0f5dd6561d59d4a99d581e6dba63dbde4c05675f6" +dependencies = [ + "llrt_utils_watts", + "rquickjs", + "tracing", +] + +[[package]] +name = "llrt_exceptions" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70409346fb3393fcb0c99dc7290ed28f3ca975fc8e587b8a52c7ab289efc8a" +dependencies = [ + "llrt_utils_watts", + "rquickjs", +] + +[[package]] +name = "llrt_fetch" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d789946fa9bbbe074e203292f28fcb43d04a79c5cf8a11cf7b9e8c6fe8361b8a" +dependencies = [ + "bytes", + "either", + "http-body-util", + "hyper", + "itoa", + "llrt_abort", + "llrt_buffer", + "llrt_compression", + "llrt_context", + "llrt_encoding", + "llrt_http", + "llrt_json", + "llrt_url", + "llrt_utils_watts", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rand", + "rquickjs", + "tokio", + "tracing", +] + +[[package]] +name = "llrt_hooking" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7cd1f745876fa47c2ea9e14edc44c4ba6dc68154e09a3eb9c834adde793c3f" +dependencies = [ + "llrt_utils_watts", + "once_cell", + "rquickjs", +] + +[[package]] +name = "llrt_http" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f151f67488a38c50d94c9c7bf7411b3caab1a3fe4732e02bb352bcef59f945fe" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "llrt_dns_cache", + "llrt_tls", + "llrt_utils_watts", + "once_cell", + "rquickjs", + "rustls", +] + +[[package]] +name = "llrt_intl" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320916bc3d8ac4b74674110a0f9e6b48704dc217502022367ef1f31efb3fabd2" +dependencies = [ + "itoa", + "jiff", + "llrt_utils_watts", + "rquickjs", +] + +[[package]] +name = "llrt_json" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad9ed9d3126d28a85ba63cbec88597774a2cf60fe53a0f38ac19b47140a8385" +dependencies = [ + "itoa", + "llrt_build", + "llrt_utils_watts", + "rquickjs", + "ryu", + "simd-json", +] + +[[package]] +name = "llrt_logging" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196dc2fa18cccb79273d2aaf0c1236e5ef341ec2a3f5114c0c08ab022910f01f" +dependencies = [ + "itoa", + "llrt_context", + "llrt_encoding", + "llrt_json", + "llrt_numbers", + "llrt_utils_watts", + "rquickjs", + "ryu", +] + +[[package]] +name = "llrt_modules" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ab0cb00684271451cc23380b8114a34f0ba6bee5182ac918a59a33eabc0882" +dependencies = [ + "home", + "llrt_abort", + "llrt_buffer", + "llrt_crypto", + "llrt_events", + "llrt_exceptions", + "llrt_fetch", + "llrt_hooking", + "llrt_http", + "llrt_intl", + "llrt_json", + "llrt_path", + "llrt_stream_web", + "llrt_timers", + "llrt_tls", + "llrt_url", + "llrt_util", + "llrt_utils_watts", + "once_cell", + "rquickjs", + "simd-json", + "tokio", + "tracing", +] + +[[package]] +name = "llrt_numbers" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e60c7403a79a173962c7db3259fffcce3ef21da54a43dd469ecd1ba059fb3dc7" +dependencies = [ + "itoa", + "llrt_utils_watts", + "rquickjs", + "ryu", +] + +[[package]] +name = "llrt_path" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20986d84f115bbcd8231c2a4c9af8de7e801bcff68f3819b7802d70ce2557815" +dependencies = [ + "llrt_utils_watts", + "memchr", + "rquickjs", +] + +[[package]] +name = "llrt_stream_web" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f53bc6376bb38daefecd18c7af425fb443c3c061ea5e8b81d9c96a1b5c709c3f" +dependencies = [ + "llrt_abort", + "llrt_utils_watts", + "rquickjs", +] + +[[package]] +name = "llrt_timers" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "371aa29ecc3ff576b5dbab7380eb1e399f1139a101ba378c3f6270b74c799a5d" +dependencies = [ + "llrt_context", + "llrt_hooking", + "llrt_utils_watts", + "once_cell", + "rquickjs", + "tokio", +] + +[[package]] +name = "llrt_tls" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b564e349bce175d31e04266d01b67d5b11e86d1b36868c391ee08605461b80f" +dependencies = [ + "once_cell", + "rustls", + "tracing", + "webpki-roots", +] + +[[package]] +name = "llrt_url" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c194b668c0dba8c3d2b877b2f8589a337dc214f5c00c4dfdc83088cdb96c6cbb" +dependencies = [ + "llrt_utils_watts", + "rquickjs", + "url", +] + +[[package]] +name = "llrt_util" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6fcda9ff20b1683d5a1f6bb49f033c7891891a31d468c4bd6306d61e60e7380" +dependencies = [ + "llrt_context", + "llrt_encoding", + "llrt_logging", + "llrt_utils_watts", + "rquickjs", +] + +[[package]] +name = "llrt_utils_watts" +version = "0.8.1-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ace6f63a6b60a3bf738bf19f393c834c83a06d9e52c31d1c30a526584519569" +dependencies = [ + "libc", + "llrt_build", + "rquickjs", + "tokio", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + +[[package]] +name = "md-5" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +dependencies = [ + "cfg-if", + "digest 0.11.3", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "p256" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p384" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17b851e6b3e378ab4ecb07fa2ed23f4d15f075735f8fec9fa1e7bdce5f8301f" +dependencies = [ + "ecdsa", + "elliptic-curve", + "fiat-crypto", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "p521" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad64cc32c2dc466317c12ee5853e61f159f9eab1fe7efade0395dc2e7b43449" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2 0.11.0", +] + +[[package]] +name = "pbkdf2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "polyval" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd" +dependencies = [ + "cpubits", + "cpufeatures 0.3.1", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "primefield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4" +dependencies = [ + "crypto-bigint", + "crypto-common 0.2.2", + "ff", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06" +dependencies = [ + "elliptic-curve", + "primefield", + "wnaf", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick_cache" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c6658afe513a3b484e3abfdaa0d03ef3c0bbf017542c178dd55f94eb3051f9" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9746dbde176634f4f2f1faf2404e30a31b2bc1e9cafb5329c95d8177a18c9fc" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "relative-path" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca40a312222d8ba74837cb474edef44b37f561da5f773981007a10bbaa992b0" +dependencies = [ + "serde", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b" +dependencies = [ + "crypto-bigint", + "hmac", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rquickjs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50dc6d6c587c339edb4769cf705867497a2baf0eca8b4645fa6ecd22f02c77a" +dependencies = [ + "either", + "indexmap", + "rquickjs-core", + "rquickjs-macro", +] + +[[package]] +name = "rquickjs-core" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bf7840285c321c3ab20e752a9afb95548c75cd7f4632a0627cea3507e310c1" +dependencies = [ + "async-lock", + "chrono", + "dlopen2", + "either", + "hashbrown 0.16.1", + "indexmap", + "phf", + "relative-path", + "rquickjs-sys", +] + +[[package]] +name = "rquickjs-macro" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7106215ff41a5677b104906a13e1a440b880f4b6362b5dc4f3978c267fad2b80" +dependencies = [ + "convert_case", + "fnv", + "ident_case", + "indexmap", + "phf_generator", + "phf_shared", + "proc-macro-crate", + "proc-macro2", + "quote", + "rquickjs-core", + "syn 2.0.119", +] + +[[package]] +name = "rquickjs-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27344601ef27460e82d6a4e1ecb9e7e99f518122095f3c51296da8e9be2b9d83" +dependencies = [ + "cc", +] + +[[package]] +name = "rsa" +version = "0.10.0-rc.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3caf" +dependencies = [ + "const-oid", + "crypto-bigint", + "crypto-primes", + "digest 0.11.3", + "pkcs1", + "pkcs8", + "rand_core", + "sha2 0.11.0", + "signature", + "spki", + "zeroize", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" +dependencies = [ + "bitflags 2.13.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "sec1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" +dependencies = [ + "base16ct", + "ctutils", + "der", + "hybrid-array", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest 0.11.3", + "rand_core", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd-json" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32d7ab2678282d21e53374fbead7119b7eacbede73685dcaac472870a29a11c" +dependencies = [ + "halfbrown", + "ref-cast", + "simdutf8", + "value-trait", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spki" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "futures-util", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.15+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.2", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "universal-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" +dependencies = [ + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "value-trait" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f4b4a98dfe54bc9ed3641af7ffcb837240269627dbd5cb047d13daa39736cc" +dependencies = [ + "float-cmp", + "halfbrown", + "itoa", + "ryu", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.6", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + +[[package]] +name = "wnaf" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795ca18b3fdb5e62bf982199278341ddcf7ebf7d32e25e212ad05d496e95f6fa" +dependencies = [ + "ff", + "group", + "hybrid-array", + "primefield", +] + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "x25519-dalek" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e8131a03190127fb2263afc72b322ecadae46b6ff8c6f399ff5d02f5559af6" +dependencies = [ + "curve25519-dalek", + "rand_core", + "zeroize", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.1.0+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "zune-core" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b767a6c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "listenbox" +version = "0.1.0" +edition = "2024" +rust-version = "1.98" +description = "Publish and manage podcasts with Listenbox" +license = "MIT" +publish = false + +[dependencies] +anyhow = "1" +base64 = "0.22" +ffmpeg = { package = "ffmpeg-the-third", version = "=6.0.0", default-features = false, features = ["static", "format", "software-resampling"] } +llrt_modules = { version = "=0.8.1-beta", default-features = false, features = ["abort", "buffer", "crypto", "crypto-rust", "events", "exceptions", "fetch", "stream-web", "timers", "url", "util", "path", "intl", "tls-ring"] } +llrt_utils = { package = "llrt_utils_watts", version = "=0.8.1-beta" } +rquickjs = { version = "=0.11.0", features = ["full-async"] } +chrono = { version = "0.4", default-features = false, features = ["std"] } +clap = { version = "4", features = ["derive"] } +futures-util = "0.3" +hex = "0.4" +image = { version = "0.25", default-features = false, features = ["jpeg", "png"] } +reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls-native-roots"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +serde_yaml = "0.9" +sha2 = "0.10" +tempfile = "3" +tokio = { version = "1", features = ["rt", "macros", "signal", "process", "fs", "io-util", "time"] } +tokio-util = { version = "0.7", features = ["io", "rt"] } +url = "2" +uuid = { version = "1", features = ["v4"] } + +[profile.release] +strip = true +lto = "thin" + +[lints.rust] +unsafe_op_in_unsafe_fn = "deny" diff --git a/README.md b/README.md index e507cbb..cbce146 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,74 @@ # Listenbox CLI -Official command-line client for publishing and managing podcasts on -[Listenbox](https://listenbox.app). +Publish podcasts and manage Listenbox shows, episodes, and members from your terminal. -The CLI uses Listenbox's public API. YouTube imports use [kkdai/youtube](https://github.com/kkdai/youtube) to download on your computer and statically linked FFmpeg 9.0.2 through [go-astiav](https://github.com/asticode/go-astiav) to prepare media before uploading it directly to Listenbox's media storage. +## Build and install -## Install - -Build a native distribution with Go 1.27+, Node.js 26, Moon, a C compiler, Make, pkg-config, and tar installed: +Install Rust (rustup), Node.js 26, [Nub](https://nubjs.com), [Moon](https://moonrepo.dev), +[pkgx](https://pkgx.sh), a C compiler, make, and tar. Rust is pinned by `rust-toolchain.toml`. ```sh +git clone https://github.com/listenbox/cli.git +cd cli +nub i --frozen-lockfile moon run cli:package -tar -xzf dist/listenbox.tar.gz -C /your/bin/directory +mkdir -p "$HOME/.local/bin" +cp dist/release/listenbox "$HOME/.local/bin/listenbox" ``` -The package contains one executable and its license notices. FFmpeg is built from a checksum-verified source archive, then linked into the CLI through CGO. No FFmpeg/FFprobe installation or media subprocess is needed at runtime. Linux builds still use the system C library. `prepare-ffmpeg.ts` records the exact codecs and build options; the additional avdevice, avfilter, and swscale libraries satisfy go-astiav's binding requirements. - -Make sure the installation directory is on `PATH`, then authorize the CLI: +Put `$HOME/.local/bin` on your PATH. `dist/listenbox.tar.gz` contains the release +executable and license notices. The executable embeds YouTube.js in QuickJS and +links FFmpeg libraries through `ffmpeg-the-third`. Node and Nub are build tools. The installed CLI needs neither Node nor +FFmpeg/FFprobe executables. -```sh -listenbox login -``` +FFmpeg 9.0.2 is built from a checksum-verified source archive with the codecs needed +for AAC audio and AVC video packages. See `prepare-ffmpeg.ts` for its configuration +and the packaged `FFmpeg-NOTICE.txt` for source and LGPL terms. The JavaScript +bundle is built with esbuild. No Bun runtime or APIs are used. -## Usage +## Use ```sh +listenbox login +listenbox auth status listenbox shows list -listenbox import "https://www.youtube.com/playlist?list=YOUR_PLAYLIST_ID" +listenbox shows create --title "Field notes" --slug field-notes --type video --language en +listenbox episodes create --show field-notes --title "A new episode" --file episode.mp4 +listenbox episodes list --show field-notes +listenbox import --slug my-show https://example.com/feed.xml +listenbox import --slug my-videos 'https://www.youtube.com/playlist?list=PLAYLIST_ID' +listenbox members list listenbox help ``` -Run `listenbox help ` for command-specific usage. +Source uploads default to a draft. Add `--publication publish` to publish after +processing. A local resume record makes interrupted source uploads resumable by +running the same command again. YouTube downloads and media preparation happen +locally; only the finished MP4 and HLS package is uploaded. Failed or interrupted +package uploads request server cleanup, preserving already completed episodes. -## Develop +`login` prints a URL to approve in your browser. Credentials are atomically stored +in `~/.config/listenbox/auth.json` with private permissions. Optional configuration +lives in `~/.config/listenbox/config.yaml`, or at `--config PATH`: -Generated API client and configuration files are committed, so a fresh clone -builds without another repository or a code-generation step. - -```sh -moon run cli:build cli:test +```yaml +api_origin: https://v1.listenbox.app +dashboard_origin: https://web.listenbox.app +print_trace_ids: false ``` -[Moon](https://moonrepo.dev) runs the complete project check: +## Development ```sh moon run cli:check ``` -CI follows [Moon's CI guide](https://moonrepo.dev/docs/guides/ci): full Git -history, source-based affected selection, and plain `moon ci`. It runs the same -project tasks used locally; aggregate checks and maintenance commands are excluded -from automatic selection. The same `moon.yml` also works as a Listenbox submodule. - -Go modules, compiler output, and golangci-lint data are cached. Moon task results -are not restored across CI runs. Sources, module files, fixtures, templates, and -configuration determine affected tasks; `$CI` is not an input. CI verifies -formatting, and selected Go tests bypass Go's test-result cache. Native Moon -reports are attached to the workflow, including on failure. - -## License - -See [LICENSE](LICENSE). +The public API client in `src/publicapi/mod.rs` is generated by +[Oasmith](https://github.com/responsibleapi/oasmith) and committed so this repository +builds independently. In the Listenbox workspace, `openapi:generate-public-rust-client` +regenerates it from the canonical public contract before building. +`openapi:generate-cli-rust-structs` generates `src/cliconfig/mod.rs` from +`cli.responsible.ts`; YAML is decoded into that generated `CLIConfig`, then both +origins are validated. No schema interpreter runs during startup. Integrated +coverage lives in `apps/api/e2e` in that workspace and runs this Rust executable +against deterministic local services, including YouTube responses. diff --git a/THIRD-PARTY-NOTICES.txt b/THIRD-PARTY-NOTICES.txt new file mode 100644 index 0000000..26ae6fe --- /dev/null +++ b/THIRD-PARTY-NOTICES.txt @@ -0,0 +1,23 @@ +QuickJS bridge and AAC preparation adapted from Mazit (https://github.com/meoyawn/mazit). + +MIT License + +Copyright (c) 2026 Mazit contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bench/startup.c b/bench/startup.c new file mode 100644 index 0000000..006dd52 --- /dev/null +++ b/bench/startup.c @@ -0,0 +1,68 @@ +// Linux startup benchmark: no shell, HTTP, JSON, config, or terminal rendering. +// Build: cc -O2 -Wall -Wextra -Werror bench/startup.c -o /tmp/cli-startup +// Run: taskset -c CPU /tmp/cli-startup /absolute/go-cli /absolute/rust-cli > samples.csv +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern char **environ; + +static double micros(struct timespec time) { + return time.tv_sec * 1000000.0 + time.tv_nsec / 1000.0; +} +static double cpu_micros(struct timeval time) { + return time.tv_sec * 1000000.0 + time.tv_usec; +} +static void sample(char *binary, const char *label, int scenario, int iteration) { + char *root[] = {binary, "--help", NULL}; + char *episode[] = {binary, "episodes", "create", "--help", NULL}; + posix_spawn_file_actions_t actions; + if (posix_spawn_file_actions_init(&actions) || + posix_spawn_file_actions_addopen(&actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0) || + posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, "/dev/null", O_WRONLY, 0) || + posix_spawn_file_actions_addopen(&actions, STDERR_FILENO, "/dev/null", O_WRONLY, 0)) { + fprintf(stderr, "cannot configure benchmark descriptors\n"); exit(1); + } + pid_t child; + struct timespec begin, end; + struct rusage usage; + int status; + clock_gettime(CLOCK_MONOTONIC, &begin); + int error = posix_spawn(&child, binary, &actions, NULL, scenario ? episode : root, environ); + if (error) { fprintf(stderr, "spawn %s: %s\n", binary, strerror(error)); exit(1); } + while (wait4(child, &status, 0, &usage) < 0) { + if (errno != EINTR) { perror("wait4"); exit(1); } + } + clock_gettime(CLOCK_MONOTONIC, &end); + posix_spawn_file_actions_destroy(&actions); + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr, "%s scenario %d failed: status %d\n", binary, scenario, status); exit(1); + } + if (iteration >= 0) { + printf("%s,%s,%d,%.3f,%.3f,%.3f,%ld,%ld,%ld,%ld,%ld\n", label, + scenario ? "episodes-create-help" : "root-help", iteration, + micros(end)-micros(begin), cpu_micros(usage.ru_utime), cpu_micros(usage.ru_stime), + usage.ru_maxrss, usage.ru_minflt, usage.ru_majflt, usage.ru_nvcsw, usage.ru_nivcsw); + } +} +int main(int argc, char **argv) { + if (argc != 3) { fprintf(stderr, "usage: %s GO_BINARY RUST_BINARY\n", argv[0]); return 1; } + puts("implementation,command,sample,wall_us,user_us,system_us,peak_rss_kib,minor_faults,major_faults,voluntary_context_switches,involuntary_context_switches"); + for (int scenario = 0; scenario < 2; scenario++) { + // Alternating order counters drift. Both executables get 30 discarded warmups. + for (int i = -30; i < 300; i++) { + for (int order = 0; order < 2; order++) { + int impl = (i + 30 + order) % 2; + sample(argv[impl + 1], impl ? "rust" : "go", scenario, i); + } + } + } + return 0; +} diff --git a/cmd/listenbox/main.go b/cmd/listenbox/main.go deleted file mode 100644 index a937c24..0000000 --- a/cmd/listenbox/main.go +++ /dev/null @@ -1,8 +0,0 @@ -// Command listenbox publishes and manages Listenbox podcasts. -package main - -import "github.com/listenbox/listenbox-cli/internal/cli" - -func main() { - cli.Execute() -} diff --git a/ffmpeg-env.sh b/ffmpeg-env.sh deleted file mode 100644 index ce78a7c..0000000 --- a/ffmpeg-env.sh +++ /dev/null @@ -1,4 +0,0 @@ -# Source from the project root before compiling packages that use go-astiav. -export CGO_ENABLED=1 -export PKG_CONFIG_PATH="$PWD/.cache/ffmpeg/lib/pkgconfig" -export CGO_LDFLAGS="$(pkg-config --static --libs libavcodec libavdevice libavfilter libavformat libswresample libswscale libavutil)" diff --git a/go.mod b/go.mod deleted file mode 100644 index 6c2ddef..0000000 --- a/go.mod +++ /dev/null @@ -1,35 +0,0 @@ -module github.com/listenbox/listenbox-cli - -go 1.27.0 - -require ( - github.com/Oudwins/zog v0.23.0 - github.com/asticode/go-astiav v0.43.0 - github.com/kkdai/youtube/v2 v2.10.6 - github.com/knadh/koanf/parsers/yaml v1.1.1 - github.com/knadh/koanf/providers/file v1.2.1 - github.com/knadh/koanf/providers/rawbytes v1.0.1 - github.com/knadh/koanf/v2 v2.3.6 - github.com/mattn/go-isatty v0.0.24 - go.uber.org/goleak v1.3.0 - gotest.tools/v3 v3.5.2 -) - -require ( - github.com/asticode/go-astikit v0.42.0 // indirect - github.com/bitly/go-simplejson v0.5.1 // indirect - github.com/dlclark/regexp2 v1.11.5 // indirect - github.com/dop251/goja v0.0.0-20260311135729-065cd970411c // indirect - github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect - github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc // indirect - github.com/knadh/koanf/maps v0.1.2 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.35.0 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index bbdf6de..0000000 --- a/go.sum +++ /dev/null @@ -1,66 +0,0 @@ -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Oudwins/zog v0.23.0 h1:aNjOcy7mE5NWOQVR9sFO1L0YCKfEjWMlaUTNquKGRQY= -github.com/Oudwins/zog v0.23.0/go.mod h1:yznncrIHC5tQeOui28FMz67LEGZV3nAoCdWL5t8QsNM= -github.com/asticode/go-astiav v0.43.0 h1:Pn+lQSdDs9ifSh5KcmesyUAjCC0/qJwAWFa1TYnGxBw= -github.com/asticode/go-astiav v0.43.0/go.mod h1:GI0pHw6K2/pl/o8upCtT49P/q4KCwhv/8nGLlCsZLdA= -github.com/asticode/go-astikit v0.42.0 h1:pnir/2KLUSr0527Tv908iAH6EGYYrYta132vvjXsH5w= -github.com/asticode/go-astikit v0.42.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0= -github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= -github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= -github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dop251/goja v0.0.0-20260311135729-065cd970411c h1:OcLmPfx1T1RmZVHHFwWMPaZDdRf0DBMZOFMVWJa7Pdk= -github.com/dop251/goja v0.0.0-20260311135729-065cd970411c/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= -github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= -github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw= -github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= -github.com/kkdai/youtube/v2 v2.10.6 h1:4sKaX6GtjbsDRnPINrf2rtBIxRKz5eXQZ5ccUVPjkyg= -github.com/kkdai/youtube/v2 v2.10.6/go.mod h1:Oj3uSagusCkXuPiripRDgAXyCaKIjAHAY90qC8Sd+m4= -github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo= -github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= -github.com/knadh/koanf/parsers/yaml v1.1.1 h1:u70vV5IyaM0HvONh8HoqBC97oTgO33KcpZbTLiKVinU= -github.com/knadh/koanf/parsers/yaml v1.1.1/go.mod h1:HHmcHXUrp9cOPcuC+2wrr44GTUB0EC+PyfN3HZD9tFg= -github.com/knadh/koanf/providers/file v1.2.1 h1:bEWbtQwYrA+W2DtdBrQWyXqJaJSG3KrP3AESOJYp9wM= -github.com/knadh/koanf/providers/file v1.2.1/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA= -github.com/knadh/koanf/providers/rawbytes v1.0.1 h1:JCQoly+djX23Okr8kqtS19R7UXKleTAp62Vib2VrVYs= -github.com/knadh/koanf/providers/rawbytes v1.0.1/go.mod h1:KxwYJf1uezTKy6PBtfE+m725NGp4GPVA7XoNTJ/PtLo= -github.com/knadh/koanf/v2 v2.3.6 h1:JoQPSJmvS4aP0xNc8xMDr5tcrkSEInL23/Il7pITAKo= -github.com/knadh/koanf/v2 v2.3.6/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= -github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= -github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= -go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= -gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= diff --git a/internal/cli/arguments.go b/internal/cli/arguments.go deleted file mode 100644 index ca9f24f..0000000 --- a/internal/cli/arguments.go +++ /dev/null @@ -1,131 +0,0 @@ -package cli - -import ( - "fmt" - "regexp" - "sort" - "strings" - - z "github.com/Oudwins/zog" - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -type showsCreateArguments struct { - Artwork string - Language string - Title string - Slug string - Type string -} - -type episodesCreateArguments struct { - Show string - Title string - Description *string - File string - Publication publicapi.EpisodePublication -} - -var showSlugPattern = regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`) - -var showsCreateArgumentsSchema = z.Struct(z.Shape{ - titleFlagName: z.String().Trim().Required(z.Message("is required")).Min(1, z.Message("is required")), - slugFlagName: z.String().Trim().Required(z.Message("is required")).Min(1, z.Message("is required")).Match( - showSlugPattern, - z.Message("must use lowercase letters, numbers, and single hyphens"), - ), - typeFlagName: z.String().Trim().Required(z.Message("is required")).Min(1, z.Message("is required")).OneOf( - []string{"audio", "video"}, - z.Message("must be audio or video"), - ), - languageFlagName: z.String().Trim().Required(z.Message("is required")).Min(1, z.Message("is required")).Match( - regexp.MustCompile(`^[a-z]{2}(?:-[A-Z]{2})?$`), - z.Message("must use a language code such as en or en-US"), - ), -}) - -var episodesCreateArgumentsSchema = z.Struct(z.Shape{ - showFlagName: z.String().Trim().Required(z.Message("is required")).Min(1, z.Message("is required")).Match( - showSlugPattern, - z.Message("must be a valid show slug"), - ), - titleFlagName: z.String().Trim().Required(z.Message("is required")).Min(1, z.Message("is required")), - descriptionFlagName: z.Ptr( - z.String().Trim().Required(z.Message("must not be blank when provided")).Min( - 1, - z.Message("must not be blank when provided"), - ), - ), - fileFlagName: z.String().Trim().Required(z.Message("is required")).Min(1, z.Message("is required")), -}) - -type cliArgumentValidationError struct { - command string - issues z.ZogIssueList - order []string -} - -func (err *cliArgumentValidationError) Error() string { - positions := make(map[string]int, len(err.order)) - for index, field := range err.order { - positions[field] = index - } - issues := append(z.ZogIssueList(nil), err.issues...) - sort.SliceStable(issues, func(i, j int) bool { - return issuePosition(issues[i], positions) < issuePosition(issues[j], positions) - }) - var output strings.Builder - _, _ = fmt.Fprintf(&output, "%s: invalid arguments", err.command) - for _, issue := range issues { - _, _ = fmt.Fprintf(&output, "\n --%s %s", issueField(issue), issue.Message) - } - return output.String() -} - -func validateShowsCreateArguments(args *showsCreateArguments) error { - issues := showsCreateArgumentsSchema.Validate(args) - if len(issues) == 0 { - return nil - } - return &cliArgumentValidationError{ - command: "shows create", issues: issues, order: []string{titleFlagName, slugFlagName, typeFlagName, languageFlagName}, - } -} - -func validateEpisodesCreateArguments(args *episodesCreateArguments) error { - issues := episodesCreateArgumentsSchema.Validate(args) - switch args.Publication { - case publicapi.EpisodePublicationDraft, publicapi.EpisodePublicationPublish: - default: - message := "must be draft or publish" - if strings.TrimSpace(string(args.Publication)) == "" { - message = "is required" - } - issues = append(issues, &z.ZogIssue{ - Path: []string{publicationFlagName}, Value: args.Publication, - Message: message, - }) - } - if len(issues) == 0 { - return nil - } - return &cliArgumentValidationError{ - command: "episodes create", issues: issues, - order: []string{showFlagName, titleFlagName, descriptionFlagName, fileFlagName, publicationFlagName}, - } -} - -func issuePosition(issue *z.ZogIssue, positions map[string]int) int { - position, ok := positions[issueField(issue)] - if !ok { - return len(positions) - } - return position -} - -func issueField(issue *z.ZogIssue) string { - if issue == nil || len(issue.Path) == 0 { - return "argument" - } - return issue.Path[0] -} diff --git a/internal/cli/arguments_test.go b/internal/cli/arguments_test.go deleted file mode 100644 index 2e5a50e..0000000 --- a/internal/cli/arguments_test.go +++ /dev/null @@ -1,159 +0,0 @@ -//nolint:lll // Fixtures keep each invocation together. -package cli - -import ( - "bytes" - "context" - "net/http" - "net/http/httptest" - "strings" - "sync/atomic" - "testing" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" - "gotest.tools/v3/assert" -) - -//nolint:funlen // Validation table covers each required and optional field boundary. -func TestEpisodesCreateArgumentValidation(t *testing.T) { - t.Parallel() - - tests := make([]struct { - name string - args episodesCreateArguments - want string - }, 0, 8) - tests = append(tests, - struct { - name string - args episodesCreateArguments - want string - }{name: "show missing", args: episodesCreateArguments{Title: testEpisodeTitle, File: testEpisodeSource}, want: "--show is required"}, - struct { - name string - args episodesCreateArguments - want string - }{name: "title missing", args: episodesCreateArguments{Show: testShowsCreateSlug, File: testEpisodeSource}, want: "--title is required"}, - ) - tests = append(tests, - struct { - name string - args episodesCreateArguments - want string - }{name: "file missing", args: episodesCreateArguments{Show: testShowsCreateSlug, Title: testEpisodeTitle}, want: "--file is required"}, - struct { - name string - args episodesCreateArguments - want string - }{name: "show whitespace", args: episodesCreateArguments{Show: " ", Title: testEpisodeTitle, File: testEpisodeSource}, want: "--show is required"}, - struct { - name string - args episodesCreateArguments - want string - }{name: "title whitespace", args: episodesCreateArguments{Show: testShowsCreateSlug, Title: " ", File: testEpisodeSource}, want: "--title is required"}, - struct { - name string - args episodesCreateArguments - want string - }{name: "file whitespace", args: episodesCreateArguments{Show: testShowsCreateSlug, Title: testEpisodeTitle, File: " "}, want: "--file is required"}, - struct { - name string - args episodesCreateArguments - want string - }{name: "show invalid", args: episodesCreateArguments{Show: "Not A Slug", Title: testEpisodeTitle, File: testEpisodeSource}, want: "--show must be a valid show slug"}, - struct { - name string - args episodesCreateArguments - want string - }{name: "publication invalid", args: episodesCreateArguments{ - Show: testShowsCreateSlug, Title: testEpisodeTitle, File: testEpisodeSource, - Publication: publicapi.EpisodePublication("later"), - }, want: "--publication must be draft or publish"}, - ) - blank := " " - tests = append(tests, struct { - name string - args episodesCreateArguments - want string - }{name: "description blank when present", args: episodesCreateArguments{ - Show: testShowsCreateSlug, Title: testEpisodeTitle, Description: &blank, File: testEpisodeSource, - }, want: "--description must not be blank when provided"}) - for _, testCase := range tests { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - err := validateEpisodesCreateArguments(&testCase.args) - assert.ErrorContains(t, err, testCase.want) - }) - } -} - -func TestEpisodesCreateReportsAllMissingArgumentsDeterministically(t *testing.T) { - t.Parallel() - want := "episodes create: invalid arguments\n" + - " --show is required\n" + - " --title is required\n" + - " --file is required\n" + - " --publication is required" - for range 20 { - args := episodesCreateArguments{} - assert.Equal(t, validateEpisodesCreateArguments(&args).Error(), want) - } -} - -func TestEpisodesCreateDescriptionMayBeOmitted(t *testing.T) { - t.Parallel() - args := episodesCreateArguments{ - Show: testShowsCreateSlug, Title: testEpisodeTitle, File: testEpisodeSource, - Publication: publicapi.EpisodePublicationDraft, - } - assert.NilError(t, validateEpisodesCreateArguments(&args)) -} - -func TestShowsCreateArgumentOrderIsDeterministic(t *testing.T) { - t.Parallel() - want := "shows create: invalid arguments\n" + - " --title is required\n" + - " --slug is required\n" + - " --type is required\n" + - " --language is required" - for range 20 { - args := showsCreateArguments{} - assert.Equal(t, validateShowsCreateArguments(&args).Error(), want) - } -} - -func TestEpisodesCreateMissingFlagValueIsControlled(t *testing.T) { - t.Parallel() - err := runEpisodesCreateCommand( - context.Background(), []string{"--show"}, &bytes.Buffer{}, &bytes.Buffer{}, - t.TempDir(), "", false, loadedCLIConfig{}, - ) - assert.ErrorContains(t, err, "flag needs an argument: -show") -} - -func TestEpisodesCreateValidationMakesNoHTTPRequest(t *testing.T) { - t.Parallel() - var calls atomic.Int64 - server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { - calls.Add(1) - })) - defer server.Close() - home := t.TempDir() - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - err := runEpisodesCreateCommand( - context.Background(), []string{"--show", testShowsCreateSlug}, &bytes.Buffer{}, &bytes.Buffer{}, - home, "", false, - loadedCLIConfig{apiOrigin: server.URL, dashboardOrigin: server.URL}, - ) - assert.ErrorContains(t, err, "--title is required") - assert.ErrorContains(t, err, "--file is required") - assert.Equal(t, calls.Load(), int64(0)) -} - -func TestShowsCreateRejectsWhitespaceAndInvalidSlug(t *testing.T) { - t.Parallel() - arguments := showsCreateArguments{Title: strings.Repeat(" ", 3), Slug: "Bad Slug", Type: "audio", Language: "en"} - err := validateShowsCreateArguments(&arguments) - assert.ErrorContains(t, err, "--title is required") - assert.ErrorContains(t, err, "--slug must use lowercase letters, numbers, and single hyphens") -} diff --git a/internal/cli/auth.go b/internal/cli/auth.go deleted file mode 100644 index c5a2388..0000000 --- a/internal/cli/auth.go +++ /dev/null @@ -1,122 +0,0 @@ -package cli - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" -) - -const ( - authDirectoryMode os.FileMode = 0o700 - authFileMode os.FileMode = 0o600 -) - -type storedAuth struct { - APIKey string `json:"api_key"` -} - -func defaultAuthPath(home string) string { - return filepath.Join(home, ".config", "listenbox", "auth.json") -} - -func readUsableStoredAuth(path string) (storedAuth, bool) { - raw, err := os.ReadFile(path) - if err != nil || len(raw) > 64<<10 { - return storedAuth{}, false - } - var auth storedAuth - if err := json.Unmarshal(raw, &auth); err != nil { - return storedAuth{}, false - } - if !validHeaderCredential(auth.APIKey) { - return storedAuth{}, false - } - return auth, true -} - -func validHeaderCredential(value string) bool { - if value == "" || strings.TrimSpace(value) != value { - return false - } - for _, char := range value { - if char < 0x21 || char > 0x7e { - return false - } - } - return true -} - -func writeStoredAuth(path string, auth storedAuth) error { - directory := filepath.Dir(path) - if err := os.MkdirAll(directory, authDirectoryMode); err != nil { - return fmt.Errorf("create auth directory %q: %w", directory, err) - } - if err := os.Chmod(directory, authDirectoryMode); err != nil { - return fmt.Errorf("set auth directory permissions %q: %w", directory, err) - } - - temporaryPath, err := writeTemporaryAuth(directory, auth) - if err != nil { - return err - } - defer func() { _ = os.Remove(temporaryPath) }() - if err := os.Rename(temporaryPath, path); err != nil { - return fmt.Errorf("replace auth file %q: %w", path, err) - } - if err := os.Chmod(path, authFileMode); err != nil { - return fmt.Errorf("set auth file permissions %q: %w", path, err) - } - return syncAuthDirectory(directory) -} - -func writeTemporaryAuth(directory string, auth storedAuth) (string, error) { - temporary, err := os.CreateTemp(directory, ".auth.json-*") - if err != nil { - return "", fmt.Errorf("create temporary auth file in %q: %w", directory, err) - } - temporaryPath := temporary.Name() - keep := false - defer func() { - if !keep { - _ = os.Remove(temporaryPath) - } - }() - - if err := temporary.Chmod(authFileMode); err != nil { - _ = temporary.Close() - return "", fmt.Errorf("set temporary auth permissions %q: %w", temporaryPath, err) - } - encoder := json.NewEncoder(temporary) - encoder.SetIndent("", " ") - //nolint:gosec // Persisting the approved API key is this function's purpose. - if err := encoder.Encode(auth); err != nil { - _ = temporary.Close() - return "", fmt.Errorf("encode temporary auth file %q: %w", temporaryPath, err) - } - if err := temporary.Sync(); err != nil { - _ = temporary.Close() - return "", fmt.Errorf("sync temporary auth file %q: %w", temporaryPath, err) - } - if err := temporary.Close(); err != nil { - return "", fmt.Errorf("close temporary auth file %q: %w", temporaryPath, err) - } - keep = true - return temporaryPath, nil -} - -func syncAuthDirectory(directory string) error { - dirHandle, err := os.Open(directory) - if err != nil { - return fmt.Errorf("open auth directory %q for sync: %w", directory, err) - } - if err := dirHandle.Sync(); err != nil { - _ = dirHandle.Close() - return fmt.Errorf("sync auth directory %q: %w", directory, err) - } - if err := dirHandle.Close(); err != nil { - return fmt.Errorf("close auth directory %q: %w", directory, err) - } - return nil -} diff --git a/internal/cli/cli.dev.yaml b/internal/cli/cli.dev.yaml deleted file mode 100644 index 6cf01f9..0000000 --- a/internal/cli/cli.dev.yaml +++ /dev/null @@ -1,3 +0,0 @@ -api_origin: http://localhost:8080 -dashboard_origin: http://localhost:5174 -print_trace_ids: true diff --git a/internal/cli/cli.prod.yaml b/internal/cli/cli.prod.yaml deleted file mode 100644 index 87cc557..0000000 --- a/internal/cli/cli.prod.yaml +++ /dev/null @@ -1,3 +0,0 @@ -api_origin: https://v1.listenbox.app -dashboard_origin: https://web.listenbox.app -print_trace_ids: false diff --git a/internal/cli/command.go b/internal/cli/command.go deleted file mode 100644 index 98db2ae..0000000 --- a/internal/cli/command.go +++ /dev/null @@ -1,721 +0,0 @@ -package cli - -import ( - "context" - "errors" - "flag" - "fmt" - "io" - "os" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -const ( - configFlagName = "config" - titleFlagName = "title" - slugFlagName = "slug" - typeFlagName = "type" - showFlagName = "show" - episodeFlagName = "episode" - descriptionFlagName = "description" - fileFlagName = "file" - publicationFlagName = "publication" - artworkFlagName = "artwork" - languageFlagName = "language" - limitFlagName = "limit" - helpCommandName = "help" - shortHelpFlag = "-h" - longHelpFlag = "--help" - importCommandName = "import" - authCommandName = "auth" - loginCommandName = "login" - statusCommandName = "status" - showsCommandName = "shows" - episodesCommandName = "episodes" - membersCommandName = "members" - listCommandName = "list" - createCommandName = "create" - deleteCommandName = "delete" - inviteCommandName = "invite" - roleCommandName = "role" - removeCommandName = "remove" - yesFlagName = "yes" - nestedCommandArgs = 2 -) - -func runCLI(ctx context.Context, args []string, stdout io.Writer, stderr io.Writer) error { - home, err := os.UserHomeDir() - if err != nil { - return fmt.Errorf("resolve user home directory: %w", err) - } - return runCLIWithHome(ctx, args, stdout, stderr, home) -} - -func runCLIWithHome( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, -) error { - defaultConfig, err := loadEmbeddedCLIConfig() - if err != nil { - return err - } - rootFlags := flag.NewFlagSet("listenbox", flag.ContinueOnError) - rootFlags.SetOutput(stderr) - configPath := "" - rootFlags.StringVar(&configPath, configFlagName, "", "path to CLI config file") - rootFlags.Usage = func() { writeRootUsage(stderr) } - if err := rootFlags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse listenbox flags: %w", err) - } - - configExplicit := flagWasSet(rootFlags, configFlagName) - remaining := rootFlags.Args() - if len(remaining) == 0 { - writeRootUsage(stderr) - return fmt.Errorf("command is required in arguments %v", args) - } - return dispatchCLICommand( - ctx, - remaining, - stdout, - stderr, - home, - configPath, - configExplicit, - defaultConfig, - ) -} - -func dispatchCLICommand( - ctx context.Context, - remaining []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - switch remaining[0] { - case helpCommandName: - return runHelp(remaining[1:], stdout) - case loginCommandName: - return runLoginCommand( - ctx, - remaining[1:], - stdout, - stderr, - home, - configPath, - configExplicit, - defaultConfig, - ) - case authCommandName: - return runAuthCommand( - ctx, - remaining[1:], - stdout, - stderr, - home, - configPath, - configExplicit, - defaultConfig, - ) - case importCommandName: - return runImportCommand( - ctx, - remaining[1:], - stdout, - stderr, - home, - configPath, - configExplicit, - defaultConfig, - ) - case showsCommandName, episodesCommandName, membersCommandName: - return dispatchTeamCommand( - ctx, remaining, stdout, stderr, home, configPath, configExplicit, defaultConfig, - ) - default: - writeRootUsage(stderr) - return fmt.Errorf("unknown command %q", remaining[0]) - } -} - -func dispatchTeamCommand( - ctx context.Context, - remaining []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - args := remaining[1:] - switch remaining[0] { - case showsCommandName: - return runShowsCommand(ctx, args, stdout, stderr, home, configPath, configExplicit, defaultConfig) - case episodesCommandName: - return runEpisodesCommand(ctx, args, stdout, stderr, home, configPath, configExplicit, defaultConfig) - case membersCommandName: - return runMembersCommand(ctx, args, stdout, stderr, home, configPath, configExplicit, defaultConfig) - default: - return fmt.Errorf("unknown team command %q", remaining[0]) - } -} - -func runAuthCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - if len(args) == 0 { - writeAuthUsage(stderr) - return errors.New("auth command is required") - } - switch args[0] { - case helpCommandName, shortHelpFlag, longHelpFlag: - if len(args) == 1 { - writeAuthUsage(stdout) - return nil - } - if len(args) == nestedCommandArgs && writeAuthCommandUsage(args[1], stdout) { - return nil - } - return fmt.Errorf("auth help received unknown command %q", args[1]) - case statusCommandName: - return runAuthStatusCommand( - ctx, - args[1:], - stdout, - stderr, - home, - configPath, - configExplicit, - defaultConfig, - ) - default: - writeAuthUsage(stderr) - return fmt.Errorf("unknown auth command %q", args[0]) - } -} - -func runImportCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox import", flag.ContinueOnError) - flags.SetOutput(stderr) - slug := "" - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&slug, slugFlagName, "", "requested global show slug") - flags.Usage = func() { writeImportUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse import flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - slugExplicit := flagWasSet(flags, slugFlagName) - switch flags.NArg() { - case 0: - return errors.New("import requires exactly one source URL") - case 1: - default: - return fmt.Errorf("import received unexpected argument %q", flags.Arg(1)) - } - sourceURL := flags.Arg(0) - return importPodcast( - ctx, - stdout, - stderr, - home, - configPath, - configExplicit, - defaultConfig, - sourceURL, - slug, - slugExplicit, - ) -} - -func runShowsCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - if len(args) == 0 { - writeShowsUsage(stderr) - return errors.New("shows command is required") - } - if isShowsHelpCommand(args[0]) { - return runShowsHelpCommand(args, stdout) - } - switch args[0] { - case listCommandName: - return runShowsListCommand(ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig) - case createCommandName: - return runShowsCreateCommand(ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig) - case deleteCommandName: - return runShowsDeleteCommand(ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig) - default: - writeShowsUsage(stderr) - return fmt.Errorf("unknown shows command %q", args[0]) - } -} - -func isShowsHelpCommand(value string) bool { - return value == helpCommandName || value == shortHelpFlag || value == longHelpFlag -} - -func runShowsHelpCommand(args []string, stdout io.Writer) error { - if len(args) == 1 { - writeShowsUsage(stdout) - return nil - } - if len(args) != nestedCommandArgs { - return fmt.Errorf("shows help received unknown command %q", args[1]) - } - switch args[1] { - case createCommandName: - writeShowsCreateUsage(stdout) - case listCommandName: - writeShowsListUsage(stdout) - case deleteCommandName: - writeShowsDeleteUsage(stdout) - default: - return fmt.Errorf("shows help received unknown command %q", args[1]) - } - return nil -} - -func runShowsListCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox shows list", flag.ContinueOnError) - flags.SetOutput(stderr) - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.Usage = func() { writeShowsListUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse shows list flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - if flags.NArg() != 0 { - return fmt.Errorf("shows list received unexpected argument %q", flags.Arg(0)) - } - return listShows(ctx, stdout, home, configPath, configExplicit, defaultConfig) -} - -func runShowsCreateCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox shows create", flag.ContinueOnError) - flags.SetOutput(stderr) - arguments := showsCreateArguments{} - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&arguments.Title, titleFlagName, "", "show title (required)") - flags.StringVar(&arguments.Slug, slugFlagName, "", "global show slug (required)") - flags.StringVar(&arguments.Type, typeFlagName, "", "kind of podcast: audio or video (required)") - flags.StringVar(&arguments.Artwork, artworkFlagName, "", "path to optional show artwork") - flags.StringVar(&arguments.Language, languageFlagName, "", "podcast language code") - flags.Usage = func() { writeShowsCreateUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse shows create flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - if flags.NArg() != 0 { - return fmt.Errorf("shows create received unexpected argument %q", flags.Arg(0)) - } - if err := validateShowsCreateArguments(&arguments); err != nil { - return err - } - return createShow( - ctx, - stdout, - home, - configPath, - configExplicit, - defaultConfig, - arguments.Title, - arguments.Slug, - publicapi.ShowSourceKind(arguments.Type), - arguments.Artwork, - arguments.Language, - ) -} - -func runLoginCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - loginFlags := flag.NewFlagSet("listenbox login", flag.ContinueOnError) - loginFlags.SetOutput(stderr) - loginFlags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - loginFlags.Usage = func() { writeLoginUsage(stderr) } - if err := loginFlags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse login flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(loginFlags, configFlagName) - if loginFlags.NArg() != 0 { - return fmt.Errorf("login received unexpected argument %q", loginFlags.Arg(0)) - } - - return login(ctx, stdout, home, configPath, configExplicit, defaultConfig) -} - -func runAuthStatusCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox auth status", flag.ContinueOnError) - flags.SetOutput(stderr) - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.Usage = func() { writeAuthStatusUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse auth status flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - if flags.NArg() != 0 { - return fmt.Errorf("auth status received unexpected argument %q", flags.Arg(0)) - } - - return authStatus(ctx, stdout, home, configPath, configExplicit, defaultConfig) -} - -func runHelp(args []string, stdout io.Writer) error { - if len(args) == 0 { - writeRootUsage(stdout) - return nil - } - if len(args) == 1 && writeCommandUsage(args[0], stdout) { - return nil - } - if len(args) == nestedCommandArgs && writeNestedCommandUsage(args[0], args[1], stdout) { - return nil - } - return fmt.Errorf("help received unknown command %q", args[0]) -} - -func writeNestedCommandUsage(command string, nested string, writer io.Writer) bool { - switch command { - case authCommandName: - return writeAuthCommandUsage(nested, writer) - case showsCommandName: - return writeShowsCommandUsage(nested, writer) - case episodesCommandName: - return writeEpisodesCommandUsage(nested, writer) - case membersCommandName: - return writeMembersCommandUsage(nested, writer) - } - return false -} - -func writeShowsCommandUsage(command string, writer io.Writer) bool { - switch command { - case createCommandName: - writeShowsCreateUsage(writer) - case listCommandName: - writeShowsListUsage(writer) - case deleteCommandName: - writeShowsDeleteUsage(writer) - default: - return false - } - return true -} - -func writeEpisodesCommandUsage(command string, writer io.Writer) bool { - switch command { - case createCommandName: - writeEpisodesCreateUsage(writer) - case listCommandName: - writeEpisodesListUsage(writer) - default: - return false - } - return true -} - -func writeMembersCommandUsage(command string, writer io.Writer) bool { - switch command { - case listCommandName: - writeMembersListUsage(writer) - case inviteCommandName: - writeMembersInviteUsage(writer) - case roleCommandName: - writeMembersRoleUsage(writer) - case removeCommandName: - writeMembersRemoveUsage(writer) - default: - return false - } - return true -} - -func writeCommandUsage(command string, writer io.Writer) bool { - switch command { - case loginCommandName: - writeLoginUsage(writer) - case authCommandName: - writeAuthUsage(writer) - case importCommandName: - writeImportUsage(writer) - case showsCommandName: - writeShowsUsage(writer) - case episodesCommandName: - writeEpisodesUsage(writer) - case membersCommandName: - writeMembersUsage(writer) - default: - return false - } - return true -} - -func writeAuthCommandUsage(command string, writer io.Writer) bool { - switch command { - case statusCommandName: - writeAuthStatusUsage(writer) - default: - return false - } - return true -} - -func flagWasSet(flags *flag.FlagSet, name string) bool { - found := false - flags.Visit(func(current *flag.Flag) { - if current.Name == name { - found = true - } - }) - return found -} - -func writeRootUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] ") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Commands:") - _, _ = fmt.Fprintln(writer, " login Authorize this CLI with Listenbox") - _, _ = fmt.Fprintln(writer, " import Import an RSS feed or YouTube URL") - _, _ = fmt.Fprintln(writer, " auth Manage CLI authorization") - _, _ = fmt.Fprintln(writer, " shows Manage shows by slug") - _, _ = fmt.Fprintln(writer, " episodes Manage episodes") - _, _ = fmt.Fprintln(writer, " members Manage team members") - _, _ = fmt.Fprintln(writer, " help Show help") -} - -func writeImportUsage(writer io.Writer) { - _, _ = fmt.Fprintln( - writer, - "Usage: listenbox [--config PATH] import [--config PATH] [--slug SLUG] ", - ) - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Options:") - _, _ = fmt.Fprintln(writer, " --slug SLUG Request a global show slug; omit for a server-generated slug") - _, _ = fmt.Fprintln(writer, " --config PATH Path to CLI config file") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Output:") - _, _ = fmt.Fprintln( - writer, - " Progress and the management link are written to stderr; the final show slug is written to stdout, with the "+ - "trace ID when enabled.", - ) -} - -func writeLoginUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] login [--config PATH]") -} - -func writeAuthStatusUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] auth status [--config PATH]") -} - -func writeAuthUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] auth ") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Commands:") - _, _ = fmt.Fprintln(writer, " status Show current CLI authorization") -} - -func writeShowsUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] shows ") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Commands:") - _, _ = fmt.Fprintln(writer, " list List accessible show slugs") - _, _ = fmt.Fprintln(writer, " create Create a show") - _, _ = fmt.Fprintln(writer, " delete Permanently delete a show") -} - -func writeShowsListUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] shows list [--config PATH]") -} - -func writeShowsDeleteUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] shows delete --show SLUG --yes [--config PATH]") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Options:") - _, _ = fmt.Fprintln(writer, " --show SLUG Global show slug (required)") - _, _ = fmt.Fprintln(writer, " --yes Confirm permanent deletion (required)") - _, _ = fmt.Fprintln(writer, " --config PATH Path to CLI config file") -} - -func writeShowsCreateUsage(writer io.Writer) { - _, _ = fmt.Fprintln( - writer, - "Usage: listenbox [--config PATH] shows create --title TITLE --slug SLUG "+ - "--type audio|video --language LANGUAGE [--artwork FILE] [--config PATH]", - ) - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Options:") - _, _ = fmt.Fprintln(writer, " --title TITLE Show title (required)") - _, _ = fmt.Fprintln(writer, " --slug SLUG Global show slug (required)") - _, _ = fmt.Fprintln(writer, " --type TYPE Kind of podcast: audio or video (required)") - _, _ = fmt.Fprintln(writer, " --language CODE Podcast language, such as en or en-US (required)") - _, _ = fmt.Fprintln(writer, " --artwork FILE Optional canonical show artwork") - _, _ = fmt.Fprintln(writer, " --config PATH Path to CLI config file") -} - -func writeEpisodesUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] episodes ") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Commands:") - _, _ = fmt.Fprintln(writer, " list List episode IDs newest first") - _, _ = fmt.Fprintln(writer, " create Create an episode as a draft or publish it after processing") - _, _ = fmt.Fprintln(writer, " delete Permanently delete an episode by ID") -} - -func writeEpisodesDeleteUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] episodes delete --episode ID --yes [--config PATH]") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Options:") - _, _ = fmt.Fprintln(writer, " --episode ID Episode ID (required)") - _, _ = fmt.Fprintln(writer, " --yes Confirm permanent deletion (required)") - _, _ = fmt.Fprintln(writer, " --config PATH Path to CLI config file") -} - -func writeEpisodesListUsage(writer io.Writer) { - _, _ = fmt.Fprintln( - writer, - "Usage: listenbox [--config PATH] episodes list --show SLUG [--limit N] [--config PATH]", - ) - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Options:") - _, _ = fmt.Fprintln(writer, " --show SLUG Existing show slug (required)") - _, _ = fmt.Fprintln(writer, " --limit N Episodes requested per page (1-500; default 100)") - _, _ = fmt.Fprintln(writer, " --config PATH Path to CLI config file") -} - -func writeEpisodesCreateUsage(writer io.Writer) { - _, _ = fmt.Fprintln( - writer, - "Usage: listenbox [--config PATH] episodes create --show SLUG --title TITLE "+ - "[--description TEXT] --file PATH [--publication draft|publish] [--config PATH]", - ) - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Options:") - _, _ = fmt.Fprintln(writer, " --show SLUG Existing show slug (required)") - _, _ = fmt.Fprintln(writer, " --title TITLE Episode title (required)") - _, _ = fmt.Fprintln(writer, " --description TEXT Optional episode description") - _, _ = fmt.Fprintln(writer, " --file PATH Audio or video source file (required)") - _, _ = fmt.Fprintln(writer, " --publication MODE draft or publish after processing (default draft)") - _, _ = fmt.Fprintln(writer, " --config PATH Path to CLI config file") -} - -func writeMembersUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] members ") - _, _ = fmt.Fprintln(writer) - _, _ = fmt.Fprintln(writer, "Commands:") - _, _ = fmt.Fprintln(writer, " list List members and pending invitations") - _, _ = fmt.Fprintln(writer, " invite Invite an email address") - _, _ = fmt.Fprintln(writer, " role Change a member role") - _, _ = fmt.Fprintln(writer, " remove Remove a member") -} - -func writeMembersListUsage(writer io.Writer) { - _, _ = fmt.Fprintln(writer, "Usage: listenbox [--config PATH] members list [--show SLUG] [--config PATH]") -} - -func writeMembersInviteUsage(writer io.Writer) { - _, _ = fmt.Fprintln( - writer, - "Usage: listenbox [--config PATH] members invite --email EMAIL --role read|write [--show SLUG] [--config PATH]", - ) -} - -func writeMembersRoleUsage(writer io.Writer) { - _, _ = fmt.Fprintln( - writer, - "Usage: listenbox [--config PATH] members role --member USER_ID --role read|write [--show SLUG] [--config PATH]", - ) -} - -func writeMembersRemoveUsage(writer io.Writer) { - _, _ = fmt.Fprintln( - writer, - "Usage: listenbox [--config PATH] members remove --member USER_ID --yes [--show SLUG] [--config PATH]", - ) -} diff --git a/internal/cli/command_test.go b/internal/cli/command_test.go deleted file mode 100644 index edcd303..0000000 --- a/internal/cli/command_test.go +++ /dev/null @@ -1,259 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "fmt" - "strings" - "testing" - - "gotest.tools/v3/assert" -) - -const ( - titleFlagArgument = "--" + titleFlagName - showFlagArgument = "--" + showFlagName - slugFlagArgument = "--" + slugFlagName - typeFlagArgument = "--" + typeFlagName - languageFlagArgument = "--" + languageFlagName - testAudioPodcastType = "audio" - testVideoPodcastType = "video" - testShowsCreateTitle = "Daily" - testShowsCreateSlug = "daily" - testCLIShowSlug = "cli-show" - testShowIDField = "show_id" - testEpisodeTitle = "Title" - testEpisodeSource = "source.mp3" -) - -func TestHelpOnlyDocumentsSupportedCommands(t *testing.T) { - t.Parallel() - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{helpCommandName}, - &stdout, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(stdout.String(), "login")) - assert.Assert(t, strings.Contains(stdout.String(), "auth")) - assert.Assert(t, !strings.Contains(stdout.String(), "whoami")) - assert.Assert(t, strings.Contains(stdout.String(), "shows")) - assert.Assert(t, bytes.Contains(stdout.Bytes(), []byte("episodes"))) -} - -func TestAuthHelpDocumentsSupportedCommands(t *testing.T) { - t.Parallel() - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{helpCommandName, authCommandName}, - &stdout, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.NilError(t, err) - assert.Assert(t, !strings.Contains(stdout.String(), "login")) - assert.Assert(t, strings.Contains(stdout.String(), "status")) - assert.Assert(t, !strings.Contains(stdout.String(), "whoami")) -} - -func TestLoginHelpDocumentsTopLevelCommand(t *testing.T) { - t.Parallel() - - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{helpCommandName, loginCommandName}, - &stdout, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(stdout.String(), "listenbox [--config PATH] login")) - assert.Assert(t, !strings.Contains(stdout.String(), "auth login")) -} - -func TestUnknownRootWhoamiFailsCleanly(t *testing.T) { - t.Parallel() - - err := runCLIWithHome( - context.Background(), - []string{"whoami"}, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, fmt.Sprintf("unknown command %q", "whoami")) -} - -func TestNestedAuthLoginFailsCleanly(t *testing.T) { - t.Parallel() - - err := runCLIWithHome( - context.Background(), - []string{authCommandName, loginCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, fmt.Sprintf("unknown auth command %q", loginCommandName)) -} - -func TestShowsCreateRequiresTitleSlugAndTypeFlags(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - args []string - want string - }{ - { - name: "all missing", - args: []string{showsCommandName, createCommandName}, - want: titleFlagArgument, - }, - { - name: "title missing", - args: []string{ - showsCommandName, - createCommandName, - slugFlagArgument, - testShowsCreateSlug, - typeFlagArgument, - testAudioPodcastType, - }, - want: titleFlagArgument, - }, - { - name: "slug missing", - args: []string{ - showsCommandName, - createCommandName, - titleFlagArgument, - testShowsCreateTitle, - typeFlagArgument, - testAudioPodcastType, - }, - want: slugFlagArgument, - }, - { - name: "type missing", - args: []string{ - showsCommandName, - createCommandName, - titleFlagArgument, - testShowsCreateTitle, - slugFlagArgument, - testShowsCreateSlug, - }, - want: "--type is required", - }, - } - for _, testCase := range tests { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - assertCLIErrorContains(t, testCase.args, testCase.want) - }) - } -} - -func TestShowsCreateReportsAllMissingRequiredFlags(t *testing.T) { - t.Parallel() - - err := runCLIWithHome( - context.Background(), - []string{showsCommandName, createCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.Equal(t, err.Error(), "shows create: invalid arguments\n"+ - " --title is required\n"+ - " --slug is required\n"+ - " --type is required\n"+ - " --language is required") -} - -func assertCLIErrorContains(t *testing.T, args []string, want string) { - t.Helper() - err := runCLIWithHome( - context.Background(), - args, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, want) -} - -func TestShowsCreateRejectsInvalidPodcastType(t *testing.T) { - t.Parallel() - - err := runCLIWithHome( - context.Background(), - []string{ - showsCommandName, - createCommandName, - titleFlagArgument, - testShowsCreateTitle, - slugFlagArgument, - testShowsCreateSlug, - typeFlagArgument, - "livestream", - }, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, "--type must be audio or video") -} - -func TestShowsCreateHelpDocumentsRequiredPodcastType(t *testing.T) { - t.Parallel() - - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{helpCommandName, showsCommandName, createCommandName}, - &stdout, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(stdout.String(), "--title TITLE")) - assert.Assert(t, strings.Contains(stdout.String(), "--slug SLUG")) - assert.Assert(t, strings.Contains(stdout.String(), "--type TYPE")) - assert.Assert(t, strings.Contains(stdout.String(), "Kind of podcast: audio or video (required)")) - assert.Assert(t, !strings.Contains(strings.ToLower(stdout.String()), "source type")) - assert.Assert(t, !strings.Contains(strings.ToLower(stdout.String()), "source kind")) -} - -func TestShowsListHelpDocumentsCommand(t *testing.T) { - t.Parallel() - - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{helpCommandName, showsCommandName, listCommandName}, - &stdout, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(stdout.String(), "shows list")) -} - -func TestUnknownCommandFailsCleanly(t *testing.T) { - t.Parallel() - err := runCLIWithHome( - context.Background(), - []string{"upload"}, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, `unknown command "upload"`) -} diff --git a/internal/cli/config.go b/internal/cli/config.go deleted file mode 100644 index 6628b1d..0000000 --- a/internal/cli/config.go +++ /dev/null @@ -1,132 +0,0 @@ -package cli - -import ( - "encoding/json" - "fmt" - "net" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/knadh/koanf/parsers/yaml" - "github.com/knadh/koanf/providers/file" - "github.com/knadh/koanf/v2" - cliconfig "github.com/listenbox/listenbox-cli/internal/cliconfig" -) - -const ( - httpScheme = "http" - httpsScheme = "https" -) - -type loadedCLIConfig struct { - apiOrigin string - dashboardOrigin string - printTraceIDs bool -} - -func loadCLIConfig( - path string, - explicit bool, - home string, - defaultConfig loadedCLIConfig, -) (loadedCLIConfig, error) { - if explicit && path == "" { - return loadedCLIConfig{}, fmt.Errorf("explicit config path %q is empty", path) - } - if !explicit { - path = filepath.Join(home, ".config", "listenbox", "config.yaml") - _, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - return defaultConfig, nil - } - return loadedCLIConfig{}, fmt.Errorf("inspect user config %q: %w", path, err) - } - } - - k := koanf.New(".") - if err := k.Load(file.Provider(path), yaml.Parser()); err != nil { - return loadedCLIConfig{}, fmt.Errorf("load CLI config %q: %w", path, err) - } - return decodeCLIConfig(k, fmt.Sprintf("CLI config %q", path)) -} - -func decodeCLIConfig(k *koanf.Koanf, source string) (loadedCLIConfig, error) { - raw, err := json.Marshal(k.Raw()) - if err != nil { - return loadedCLIConfig{}, fmt.Errorf("marshal %s: %w", source, err) - } - var generated cliconfig.CLIConfig - if err := json.Unmarshal(raw, &generated); err != nil { - return loadedCLIConfig{}, fmt.Errorf("decode %s: %w", source, err) - } - apiOrigin, err := normalizeOrigin(generated.ApiOrigin, "API") - if err != nil { - return loadedCLIConfig{}, fmt.Errorf("validate api_origin in %s: %w", source, err) - } - dashboardOrigin, err := normalizeOrigin(generated.DashboardOrigin, "Dashboard") - if err != nil { - return loadedCLIConfig{}, fmt.Errorf("validate dashboard_origin in %s: %w", source, err) - } - return loadedCLIConfig{ - apiOrigin: apiOrigin, - dashboardOrigin: dashboardOrigin, - printTraceIDs: generated.PrintTraceIds, - }, nil -} - -func normalizeAPIOrigin(value string) (string, error) { - return normalizeOrigin(value, "API") -} - -func normalizeOrigin(value string, name string) (string, error) { - trimmed := strings.TrimSpace(value) - parsed, err := url.Parse(trimmed) - if err != nil { - return "", fmt.Errorf("parse %s origin %q: %w", name, value, err) - } - scheme := strings.ToLower(parsed.Scheme) - if err := validateOriginComponents(parsed, scheme, value, name); err != nil { - return "", err - } - - host := canonicalOriginHost(parsed, scheme) - return scheme + "://" + host, nil -} - -func validateOriginComponents(parsed *url.URL, scheme string, original string, name string) error { - switch { - case scheme != httpScheme && scheme != httpsScheme: - return fmt.Errorf("%s origin %q must use http or https", name, original) - case parsed.Host == "" || parsed.Hostname() == "": - return fmt.Errorf("%s origin %q must include a host", name, original) - case parsed.User != nil: - return fmt.Errorf("%s origin %q must not include user information", name, original) - case parsed.Path != "" && parsed.Path != "/": - return fmt.Errorf("%s origin %q must not include a path", name, original) - case parsed.RawQuery != "" || parsed.ForceQuery: - return fmt.Errorf("%s origin %q must not include a query", name, original) - case parsed.Fragment != "": - return fmt.Errorf("%s origin %q must not include a fragment", name, original) - default: - return nil - } -} - -func canonicalOriginHost(parsed *url.URL, scheme string) string { - hostname := strings.ToLower(parsed.Hostname()) - port := parsed.Port() - if (scheme == httpScheme && port == "80") || (scheme == httpsScheme && port == "443") { - port = "" - } - host := hostname - if strings.Contains(hostname, ":") { - host = "[" + hostname + "]" - } - if port != "" { - host = net.JoinHostPort(hostname, port) - } - return host -} diff --git a/internal/cli/config_dev.go b/internal/cli/config_dev.go deleted file mode 100644 index 6a0c33a..0000000 --- a/internal/cli/config_dev.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build dev - -package cli - -import _ "embed" - -//go:embed cli.dev.yaml -var embeddedCLIConfig []byte diff --git a/internal/cli/config_dev_test.go b/internal/cli/config_dev_test.go deleted file mode 100644 index 01386ab..0000000 --- a/internal/cli/config_dev_test.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build dev - -package cli - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func TestEmbeddedCLIConfigDefaultsToDevelopment(t *testing.T) { - t.Parallel() - config, err := loadEmbeddedCLIConfig() - assert.NilError(t, err) - assert.Equal(t, config.apiOrigin, "http://localhost:8080") - assert.Equal(t, config.dashboardOrigin, "http://localhost:5174") - assert.Equal(t, config.printTraceIDs, true) -} diff --git a/internal/cli/config_prod.go b/internal/cli/config_prod.go deleted file mode 100644 index e134fab..0000000 --- a/internal/cli/config_prod.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !dev - -package cli - -import _ "embed" - -//go:embed cli.prod.yaml -var embeddedCLIConfig []byte diff --git a/internal/cli/config_prod_test.go b/internal/cli/config_prod_test.go deleted file mode 100644 index 9ba2689..0000000 --- a/internal/cli/config_prod_test.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build !dev - -package cli - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func TestEmbeddedCLIConfigDefaultsToProduction(t *testing.T) { - t.Parallel() - config, err := loadEmbeddedCLIConfig() - assert.NilError(t, err) - assert.Equal(t, config.apiOrigin, "https://v1.listenbox.app") - assert.Equal(t, config.dashboardOrigin, "https://web.listenbox.app") - assert.Equal(t, config.printTraceIDs, false) -} diff --git a/internal/cli/config_test.go b/internal/cli/config_test.go deleted file mode 100644 index 4c2f42b..0000000 --- a/internal/cli/config_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package cli - -import ( - "os" - "path/filepath" - "testing" - - "gotest.tools/v3/assert" -) - -func TestLoadCLIConfigUsesEmbeddedDefaultWhenUserConfigIsMissing(t *testing.T) { - t.Parallel() - defaultConfig, err := loadEmbeddedCLIConfig() - assert.NilError(t, err) - config, err := loadCLIConfig("", false, t.TempDir(), defaultConfig) - assert.NilError(t, err) - assert.Equal(t, config.apiOrigin, defaultConfig.apiOrigin) - assert.Equal(t, config.dashboardOrigin, defaultConfig.dashboardOrigin) -} - -func TestLoadCLIConfigReadsImplicitUserConfig(t *testing.T) { - t.Parallel() - home := t.TempDir() - path := filepath.Join(home, ".config", "listenbox", "config.yaml") - assert.NilError(t, os.MkdirAll(filepath.Dir(path), 0o700)) - assert.NilError(t, os.WriteFile(path, []byte( - "api_origin: http://EXAMPLE.com:80/\n"+ - "dashboard_origin: https://WEB.example.com:443/\n"+ - "print_trace_ids: true\n", - ), 0o600)) - config, err := loadCLIConfig("", false, home, loadedCLIConfig{}) - assert.NilError(t, err) - assert.Equal(t, config.apiOrigin, "http://example.com") - assert.Equal(t, config.dashboardOrigin, "https://web.example.com") - assert.Equal(t, config.printTraceIDs, true) -} - -func TestLoadCLIConfigRejectsExplicitFailures(t *testing.T) { - t.Parallel() - home := t.TempDir() - invalidPath := filepath.Join(home, "invalid.yaml") - assert.NilError(t, os.WriteFile(invalidPath, []byte( - "api_origin: https://example.com/path\n"+ - "dashboard_origin: https://example.com\n"+ - "print_trace_ids: false\n", - ), 0o600)) - - tests := []struct { - name string - path string - want string - }{ - {name: "empty", path: "", want: "explicit config path"}, - {name: "missing", path: filepath.Join(home, "missing.yaml"), want: "load CLI config"}, - {name: "unreadable shape", path: home, want: "load CLI config"}, - {name: "invalid origin", path: invalidPath, want: "must not include a path"}, - } - for _, testCase := range tests { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - _, err := loadCLIConfig(testCase.path, true, home, loadedCLIConfig{}) - assert.ErrorContains(t, err, testCase.want) - }) - } -} - -func TestNormalizeAPIOriginRejectsNonOrigins(t *testing.T) { - t.Parallel() - values := []string{ - "", - "ftp://example.com", - "https://user@example.com", - "https://example.com?query=yes", - "https://example.com#fragment", - } - for _, value := range values { - _, err := normalizeAPIOrigin(value) - assert.Assert(t, err != nil, value) - } -} diff --git a/internal/cli/doc.go b/internal/cli/doc.go deleted file mode 100644 index 78f5aa7..0000000 --- a/internal/cli/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package cli implements the Listenbox command-line client. -package cli diff --git a/internal/cli/episode_delete.go b/internal/cli/episode_delete.go deleted file mode 100644 index b1af4f6..0000000 --- a/internal/cli/episode_delete.go +++ /dev/null @@ -1,149 +0,0 @@ -package cli - -import ( - "context" - "errors" - "flag" - "fmt" - "io" - "net/http" - "strings" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -func runEpisodesDeleteCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox episodes delete", flag.ContinueOnError) - flags.SetOutput(stderr) - episodeID := "" - yes := false - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&episodeID, episodeFlagName, "", "episode ID (required)") - flags.BoolVar(&yes, yesFlagName, false, "confirm permanent deletion (required)") - flags.Usage = func() { writeEpisodesDeleteUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse episodes delete flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - if flags.NArg() != 0 { - return fmt.Errorf("episodes delete received unexpected argument %q", flags.Arg(0)) - } - if !validCompletionID(episodeID, "ep_") { - return fmt.Errorf("episodes delete: invalid --episode %q", episodeID) - } - if !flagWasSet(flags, yesFlagName) || !yes { - return errors.New("episodes delete: --yes is required") - } - return deleteEpisode(ctx, stdout, stderr, home, configPath, configExplicit, defaultConfig, episodeID) -} - -//nolint:cyclop,funlen,gocognit,gocyclo // The CLI validates each HTTP and SSE variant before reporting deletion. -func deleteEpisode( - ctx context.Context, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, - episodeID string, -) error { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("delete episode: not logged in; run listenbox login") - } - client, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return err - } - response, err := client.CreateEpisodeDeletion(ctx, publicapi.CreateEpisodeDeletionParams{EpisodeId: episodeID}) - if err != nil { - return fmt.Errorf("delete episode %q: %w", episodeID, err) - } - if response == nil || response.Status202 == nil { - if response != nil { - switch response.StatusCode { - case http.StatusUnauthorized: - return fmt.Errorf("delete episode %q: authentication failed; run listenbox login", episodeID) - case http.StatusForbidden: - return fmt.Errorf("delete episode %q: only the current team owner may delete it", episodeID) - case http.StatusNotFound: - return fmt.Errorf("delete episode %q: episode not found", episodeID) - } - return fmt.Errorf("delete episode %q returned HTTP status %d", episodeID, response.StatusCode) - } - return fmt.Errorf("delete episode %q returned no response", episodeID) - } - eventsResponse, err := client.EpisodeDeletionEvents(ctx, publicapi.EpisodeDeletionEventsParams{ - EpisodeDeletionRunId: response.Status202.EpisodeDeletionRunId, - }) - if err != nil { - return fmt.Errorf("read episode deletion event stream for %q: %w", episodeID, err) - } - if eventsResponse == nil || eventsResponse.Status200 == nil { - status := 0 - if eventsResponse != nil { - status = eventsResponse.StatusCode - } - return fmt.Errorf("episode deletion event stream for %q returned HTTP status %d", episodeID, status) - } - stream := eventsResponse.Status200 - defer func() { _ = stream.Close() }() - progress := newImportProgress(stderr, writerIsTerminal(stderr)) - defer progress.cleanFailureLine() - for { - event, ok, err := stream.Next(ctx) - if err != nil { - return fmt.Errorf("read episode deletion stream for %q: %w", episodeID, err) - } - if !ok { - return fmt.Errorf("episode deletion stream for %q ended before a terminal event", episodeID) - } - if current := event.EpisodeDeletionProgressEvent; current != nil { - if current.Type != showDeletionProgressEventType || current.Percent < 0 || - current.Percent > maxShowDeletionPercent || strings.TrimSpace(current.CurrentStep) == "" { - return fmt.Errorf("episode deletion stream for %q emitted malformed progress event", episodeID) - } - if err := progress.update(uint32(current.Percent)); err != nil { - return err - } - continue - } - terminal := event.EpisodeDeletionTerminalEvent - if terminal == nil { - return fmt.Errorf("episode deletion stream for %q emitted an empty event variant", episodeID) - } - if completed := terminal.EpisodeDeletionCompletedEvent; completed != nil { - if completed.Type != showDeletionTerminalEventType || completed.Status != showDeletionCompletedStatus || - completed.EpisodeId != episodeID { - return fmt.Errorf("episode deletion stream for %q emitted malformed completed event", episodeID) - } - if err := progress.complete(); err != nil { - return err - } - _, err := fmt.Fprintf(stdout, "Deleted episode %q\n", episodeID) - return err - } - failed := terminal.EpisodeDeletionFailedEvent - if failed == nil || strings.TrimSpace(failed.ErrorCode) == "" || strings.TrimSpace(failed.ErrorMessage) == "" { - return fmt.Errorf("episode deletion stream for %q emitted malformed failed event", episodeID) - } - return fmt.Errorf("delete episode %q failed (%s): %s", episodeID, failed.ErrorCode, failed.ErrorMessage) - } -} diff --git a/internal/cli/episodes.go b/internal/cli/episodes.go deleted file mode 100644 index 6267c08..0000000 --- a/internal/cli/episodes.go +++ /dev/null @@ -1,906 +0,0 @@ -package cli - -import ( - "bufio" - "bytes" - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "flag" - "fmt" - "io" - "net/http" - "net/url" - "os" - "path/filepath" - "slices" - "strings" - "time" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -const ( - episodeResumeVersion = 4 - episodeResumeDirectoryMode os.FileMode = 0o700 - episodeResumeFileMode os.FileMode = 0o600 - episodeProgressComplete = int64(100) - episodeErrorResponseLimit = 64 << 10 - episodeJSONResponseLimit = 1 << 20 - episodeAudioMIME = "audio/mpeg" - episodeShowSlugJSONField = "show_slug" - episodeMultipartMaxParts = 10000 - episodeListMaxPageLimit = 500 - episodeFinalizingPhase = "finalizing" -) - -type episodeUploadInterruptedError struct{} - -func (*episodeUploadInterruptedError) Error() string { - return "episode source upload interrupted; resumable state saved" -} - -type episodeSourceIdentity struct { - AbsolutePath string - ByteLength int64 - ContentType string - SHA256 string -} - -type episodeResumePart struct { - PartNumber int32 `json:"part_number"` - ETag string `json:"etag"` - Size int64 `json:"size"` -} - -type episodeResumeRecord struct { - Version int `json:"version"` - EpisodeID string `json:"episode_id,omitempty"` - UploadSessionID string `json:"upload_session_id,omitempty"` - TeamID string `json:"team_id,omitempty"` - ShowID string `json:"show_id,omitempty"` - ManagementURL string `json:"management_url,omitempty"` - FilePath string `json:"file_path"` - FileSHA256 string `json:"file_sha256"` - FileByteLength int64 `json:"file_byte_length"` - CompletedParts []episodeResumePart `json:"completed_parts"` - ExpiresAt int64 `json:"expires_at"` - Phase string `json:"phase"` -} - -type episodeUploadSession struct { - TeamID string `json:"team_id"` - ShowID string `json:"show_id"` - EpisodeID string `json:"episode_id,omitempty"` - UploadSessionID string `json:"upload_session_id"` - ObjectKey string `json:"object_key"` - ContentType string `json:"content_type"` - ByteLength int64 `json:"byte_length"` - PartSize int64 `json:"part_size"` - MaxParallelism int32 `json:"max_parallelism"` - ExpiresAt int64 `json:"expires_at"` - Phase string `json:"phase"` - CompletedParts []episodeResumePart `json:"completed_parts"` -} - -type completedEpisode struct { - ID string `json:"id"` - ShowID string `json:"show_id"` -} - -type episodeUploadPartPresign struct { - PartNumber int32 `json:"part_number"` - Method string `json:"method"` - UploadURL string `json:"upload_url"` - ExpiresAt int64 `json:"expires_at"` -} - -type episodeUploadPartPresigns struct { - UploadSessionID string `json:"upload_session_id"` - Parts []episodeUploadPartPresign `json:"parts"` -} - -type episodeCreateEvent struct { - Type string `json:"type"` - Status string `json:"status"` - CurrentStep string `json:"current_step"` - Percent int64 `json:"percent"` - ErrorCode string `json:"error_code"` - ErrorMessage string `json:"error_message"` - EpisodeID string `json:"episode_id"` - RunID string `json:"run_id"` - YouTubeVideoID string `json:"youtube_video_id"` -} - -type episodeUploadClient struct { - apiOrigin string - credential string - httpClient *http.Client - traceWriter io.Writer -} - -func runEpisodesCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - if len(args) == 0 { - writeEpisodesUsage(stderr) - return errors.New("episodes command is required") - } - switch args[0] { - case helpCommandName, shortHelpFlag, longHelpFlag: - if len(args) == 1 { - writeEpisodesUsage(stdout) - return nil - } - if len(args) == nestedCommandArgs { - switch args[1] { - case createCommandName: - writeEpisodesCreateUsage(stdout) - return nil - case listCommandName: - writeEpisodesListUsage(stdout) - return nil - case deleteCommandName: - writeEpisodesDeleteUsage(stdout) - return nil - } - } - return fmt.Errorf("episodes help received unknown command %q", args[1]) - case createCommandName: - return runEpisodesCreateCommand( - ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig, - ) - case listCommandName: - return runEpisodesListCommand( - ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig, - ) - case deleteCommandName: - return runEpisodesDeleteCommand( - ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig, - ) - default: - writeEpisodesUsage(stderr) - return fmt.Errorf("unknown episodes command %q", args[0]) - } -} - -func runEpisodesListCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox episodes list", flag.ContinueOnError) - flags.SetOutput(stderr) - show := "" - limit := 0 - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&show, showFlagName, "", "existing show slug (required)") - flags.IntVar(&limit, limitFlagName, 0, "episodes requested per page (1-500)") - flags.Usage = func() { writeEpisodesListUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse episodes list flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - if flags.NArg() != 0 { - return fmt.Errorf("episodes list received unexpected argument %q", flags.Arg(0)) - } - show = strings.TrimSpace(show) - if show == "" { - return errors.New("episodes list: --show is required") - } - if !showSlugPattern.MatchString(show) { - return errors.New("episodes list: --show must be a valid show slug") - } - if flagWasSet(flags, limitFlagName) && (limit < 1 || limit > episodeListMaxPageLimit) { - return errors.New("episodes list: --limit must be from 1 through 500") - } - return listEpisodes(ctx, stdout, home, configPath, configExplicit, defaultConfig, show, limit) -} - -func listEpisodes( - ctx context.Context, - stdout io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, - show string, - limit int, -) error { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("list episodes: not logged in; run listenbox login") - } - client, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return err - } - - params := publicapi.ListEpisodesParams{ShowSlug: show} - if limit != 0 { - //nolint:gosec // Flag validation bounds the value to 1 through 500 before conversion. - pageLimit := publicapi.EpisodePageLimit(limit) - params.Limit = &pageLimit - } - seenCursors := map[string]struct{}{} - for { - response, listErr := client.ListEpisodes(ctx, params) - if listErr != nil { - return fmt.Errorf("list episodes for show %q: %w", show, listErr) - } - next, err := streamEpisodeListPage(stdout, response, show, config.apiOrigin) - if err != nil { - return err - } - if next == nil { - return nil - } - if *next == "" { - return fmt.Errorf("list episodes for show %q returned an empty next cursor", show) - } - if _, repeated := seenCursors[*next]; repeated { - return fmt.Errorf("list episodes for show %q returned repeated cursor %q", show, *next) - } - seenCursors[*next] = struct{}{} - params.Cursor = next - } -} - -func streamEpisodeListPage( - stdout io.Writer, - response *publicapi.ListEpisodesResponse, - show string, - apiOrigin string, -) (*string, error) { - if response == nil { - return nil, fmt.Errorf("list episodes for show %q against %q returned no response", show, apiOrigin) - } - if response.Status200 == nil { - return nil, fmt.Errorf("list episodes for show %q returned HTTP status %d", show, response.StatusCode) - } - for _, episode := range response.Status200.Episodes { - if _, err := fmt.Fprintln(stdout, episode.Id); err != nil { - return nil, fmt.Errorf("print episode ID %q: %w", episode.Id, err) - } - } - return response.Status200.NextCursor, nil -} - -func runEpisodesCreateCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox episodes create", flag.ContinueOnError) - flags.SetOutput(stderr) - arguments := episodesCreateArguments{Publication: publicapi.EpisodePublicationDraft} - description := "" - publication := string(arguments.Publication) - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&arguments.Show, showFlagName, "", "existing show slug (required)") - flags.StringVar(&arguments.Title, titleFlagName, "", "episode title (required)") - flags.StringVar(&description, descriptionFlagName, "", "optional episode description") - flags.StringVar(&arguments.File, fileFlagName, "", "audio or video source path (required)") - flags.StringVar(&publication, publicationFlagName, publication, "draft or publish after processing") - flags.Usage = func() { writeEpisodesCreateUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse episodes create flags: %w", err) - } - if flagWasSet(flags, descriptionFlagName) { - arguments.Description = &description - } - arguments.Publication = publicapi.EpisodePublication(publication) - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - if flags.NArg() != 0 { - return fmt.Errorf("episodes create received unexpected argument %q", flags.Arg(0)) - } - if err := validateEpisodesCreateArguments(&arguments); err != nil { - return err - } - return createEpisode(ctx, stdout, stderr, home, configPath, configExplicit, defaultConfig, arguments) -} - -//nolint:cyclop,funlen // Coordinates explicit durable boundaries and output failures. -func createEpisode( - ctx context.Context, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, - args episodesCreateArguments, -) error { - source, err := inspectEpisodeSource(args.File) - if err != nil { - return err - } - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("create episode: not logged in; run listenbox login") - } - resumePath, record, err := loadOrCreateEpisodeResume(home, args, source) - if err != nil { - return err - } - transport, ok := http.DefaultTransport.(*http.Transport) - if !ok { - return errors.New("create episode: default HTTP transport is unavailable") - } - client := &episodeUploadClient{ - apiOrigin: config.apiOrigin, - credential: auth.APIKey, - httpClient: &http.Client{Transport: transport.Clone()}, - } - if config.printTraceIDs { - client.traceWriter = stderr - } - wasFinalizing := record.Phase == episodeFinalizingPhase - var session episodeUploadSession - if !wasFinalizing { - session, err = client.createOrResume(ctx, args, source) - if err != nil { - return err - } - record.EpisodeID = session.EpisodeID - record.UploadSessionID = session.UploadSessionID - record.TeamID = session.TeamID - record.ShowID = session.ShowID - record.CompletedParts = session.CompletedParts - record.ExpiresAt = session.ExpiresAt - record.Phase = session.Phase - } - if err := writeEpisodeResume(resumePath, record); err != nil { - return err - } - if record.Phase != episodeFinalizingPhase { - if err := uploadMissingEpisodeParts(ctx, stderr, source, session, record, resumePath, client); err != nil { - return err - } - record.Phase = episodeFinalizingPhase - if err := writeEpisodeResume(resumePath, record); err != nil { - return err - } - } - episode, err := client.complete(ctx, record.UploadSessionID, args.Publication) - if err != nil { - return err - } - record.EpisodeID = episode.ID - record.ShowID = episode.ShowID - record.Phase = processingStatus - managementURL, err := episodeManagementURL( - config.dashboardOrigin, record.TeamID, record.ShowID, record.EpisodeID, - ) - if err != nil { - return err - } - record.ManagementURL = managementURL - if err := writeEpisodeResume(resumePath, record); err != nil { - return err - } - event, err := client.waitForSafeHandoff(ctx, stderr, record.UploadSessionID) - if err != nil { - if errors.Is(ctx.Err(), context.Canceled) { - return nil - } - return err - } - record.Phase = importCompletedStatus - if err := writeEpisodeResume(resumePath, record); err != nil { - return err - } - result := "Created draft episode" - if args.Publication == publicapi.EpisodePublicationPublish { - result = "Published episode" - } - if _, err := fmt.Fprintf( - stdout, "%s %q\nOpen in Listenbox: %s\n", result, args.Title, record.ManagementURL, - ); err != nil { - return fmt.Errorf("print created episode: %w", err) - } - if event.YouTubeVideoID != "" { - if _, err := fmt.Fprintf( - stdout, - "YouTube (processing): https://www.youtube.com/watch?v=%s\n", - event.YouTubeVideoID, - ); err != nil { - return fmt.Errorf("print YouTube processing URL: %w", err) - } - } - return nil -} - -func inspectEpisodeSource(path string) (episodeSourceIdentity, error) { - absolutePath, err := filepath.Abs(path) - if err != nil { - return episodeSourceIdentity{}, fmt.Errorf("resolve --file %q: %w", path, err) - } - file, err := os.Open(absolutePath) - if err != nil { - return episodeSourceIdentity{}, fmt.Errorf("open --file %q: %w", path, err) - } - defer file.Close() - info, err := file.Stat() - if err != nil { - return episodeSourceIdentity{}, fmt.Errorf("inspect --file %q: %w", path, err) - } - if !info.Mode().IsRegular() || info.Size() <= 0 { - return episodeSourceIdentity{}, fmt.Errorf("--file %q must be a non-empty regular file", path) - } - hash := sha256.New() - if _, err := io.Copy(hash, file); err != nil { - return episodeSourceIdentity{}, fmt.Errorf("hash --file %q: %w", path, err) - } - contentType, err := episodeContentTypeForFileName(absolutePath) - if err != nil { - return episodeSourceIdentity{}, fmt.Errorf("inspect --file %q: %w", path, err) - } - return episodeSourceIdentity{ - AbsolutePath: absolutePath, ByteLength: info.Size(), ContentType: contentType, - SHA256: hex.EncodeToString(hash.Sum(nil)), - }, nil -} - -func episodeContentTypeForFileName(fileName string) (string, error) { - switch strings.ToLower(filepath.Ext(strings.TrimSpace(fileName))) { - case ".mp3": - return string(publicapi.EpisodeUploadContentTypeAudioMpeg), nil - case ".m4a": - return string(publicapi.EpisodeUploadContentTypeAudioMp4), nil - case ".wav": - return string(publicapi.EpisodeUploadContentTypeAudioWav), nil - case ".flac": - return string(publicapi.EpisodeUploadContentTypeAudioFlac), nil - case ".mp4", ".m4v": - return string(publicapi.EpisodeUploadContentTypeVideoMp4), nil - case ".mov": - return string(publicapi.EpisodeUploadContentTypeVideoQuicktime), nil - default: - return "", fmt.Errorf( - "unsupported media upload file %q; accepted extensions: .mp3, .m4a, .wav, .flac, .mp4, .m4v, .mov", - fileName, - ) - } -} - -//nolint:cyclop // Validates every persisted resume invariant before mutation. -func loadOrCreateEpisodeResume( - home string, - args episodesCreateArguments, - source episodeSourceIdentity, -) (string, episodeResumeRecord, error) { - resumeIdentity := sha256.Sum256([]byte(args.Show + "\x00" + source.SHA256)) - directory := filepath.Join(home, ".config", "listenbox", "episode-resume") - path := filepath.Join(directory, hex.EncodeToString(resumeIdentity[:])+".json") - raw, err := os.ReadFile(path) - if err != nil && !os.IsNotExist(err) { - return "", episodeResumeRecord{}, fmt.Errorf("read episode resume record %q: %w", path, err) - } - if os.IsNotExist(err) { - record := episodeResumeRecord{ - Version: episodeResumeVersion, - FilePath: source.AbsolutePath, FileSHA256: source.SHA256, FileByteLength: source.ByteLength, - CompletedParts: []episodeResumePart{}, Phase: "prepared", - } - if err := writeEpisodeResume(path, record); err != nil { - return "", record, err - } - return path, record, nil - } - var record episodeResumeRecord - if decodeErr := json.Unmarshal(raw, &record); decodeErr != nil { - return "", record, fmt.Errorf("decode episode resume record %q: %w", path, decodeErr) - } - if record.Version != episodeResumeVersion { - return "", record, fmt.Errorf( - "episode resume record %q has an unsupported version; remove it to restart explicitly", - path, - ) - } - if record.FileSHA256 != source.SHA256 || record.FileByteLength != source.ByteLength { - return "", record, fmt.Errorf( - "--file identity changed since resumable upload %s; remove %q to restart explicitly", - record.UploadSessionID, - path, - ) - } - if record.ExpiresAt > 0 && record.ExpiresAt <= time.Now().UnixMilli() && - record.Phase != "finalizing" && record.Phase != processingStatus && record.Phase != "completed" { - return "", record, fmt.Errorf( - "resumable upload %s expired; remove %q to restart explicitly", - record.UploadSessionID, - path, - ) - } - record.FilePath = source.AbsolutePath - return path, record, nil -} - -func writeEpisodeResume(path string, record episodeResumeRecord) error { - directory := filepath.Dir(path) - if err := os.MkdirAll(directory, episodeResumeDirectoryMode); err != nil { - return fmt.Errorf("create episode resume directory %q: %w", directory, err) - } - if err := os.Chmod(directory, episodeResumeDirectoryMode); err != nil { - return fmt.Errorf("secure episode resume directory %q: %w", directory, err) - } - temporary, err := os.CreateTemp(directory, ".episode-resume-*") - if err != nil { - return fmt.Errorf("create temporary episode resume record: %w", err) - } - temporaryPath := temporary.Name() - defer func() { _ = os.Remove(temporaryPath) }() - if err := temporary.Chmod(episodeResumeFileMode); err != nil { - _ = temporary.Close() - return err - } - encoder := json.NewEncoder(temporary) - encoder.SetIndent("", " ") - if err := encoder.Encode(record); err != nil { - _ = temporary.Close() - return fmt.Errorf("encode episode resume record: %w", err) - } - if err := temporary.Sync(); err != nil { - _ = temporary.Close() - return fmt.Errorf("sync episode resume record: %w", err) - } - if err := temporary.Close(); err != nil { - return fmt.Errorf("close episode resume record: %w", err) - } - if err := os.Rename(temporaryPath, path); err != nil { - return fmt.Errorf("replace episode resume record %q: %w", path, err) - } - return syncAuthDirectory(directory) -} - -//nolint:cyclop // Each accepted part has its own durable save boundary. -func uploadMissingEpisodeParts( - ctx context.Context, - stderr io.Writer, - source episodeSourceIdentity, - session episodeUploadSession, - record episodeResumeRecord, - resumePath string, - client *episodeUploadClient, -) error { - accepted := make(map[int32]episodeResumePart, len(session.CompletedParts)) - for _, part := range session.CompletedParts { - accepted[part.PartNumber] = part - } - partCount64 := (source.ByteLength + session.PartSize - 1) / session.PartSize - if partCount64 > episodeMultipartMaxParts { - return errors.New("episode source requires too many multipart parts") - } - partCount := int32(partCount64) // #nosec G115 -- bounded by the multipart maximum above. - missing := make([]int32, 0, partCount) - for partNumber := int32(1); partNumber <= partCount; partNumber++ { - if _, ok := accepted[partNumber]; !ok { - missing = append(missing, partNumber) - } - } - if err := printSavedEpisodeProgress(stderr, accepted, source.ByteLength); err != nil { - return err - } - if len(missing) == 0 { - return nil - } - presigns, err := client.presign(ctx, session.UploadSessionID, missing) - if err != nil { - return err - } - file, err := os.Open(source.AbsolutePath) - if err != nil { - return fmt.Errorf("reopen --file %q: %w", source.AbsolutePath, err) - } - defer file.Close() - for _, presign := range presigns.Parts { - start := int64(presign.PartNumber-1) * session.PartSize - length := min(session.PartSize, source.ByteLength-start) - etag, err := client.uploadPart(ctx, presign, io.NewSectionReader(file, start, length), length) - if err != nil { - if errors.Is(ctx.Err(), context.Canceled) { - return &episodeUploadInterruptedError{} - } - return err - } - accepted[presign.PartNumber] = episodeResumePart{ - PartNumber: presign.PartNumber, ETag: etag, Size: length, - } - record.CompletedParts = sortedEpisodeResumeParts(accepted) - record.Phase = "uploading" - if err := writeEpisodeResume(resumePath, record); err != nil { - return err - } - if err := printSavedEpisodeProgress(stderr, accepted, source.ByteLength); err != nil { - return err - } - } - return nil -} - -func sortedEpisodeResumeParts(parts map[int32]episodeResumePart) []episodeResumePart { - result := make([]episodeResumePart, 0, len(parts)) - for _, part := range parts { - result = append(result, part) - } - slices.SortFunc(result, func(a, b episodeResumePart) int { return int(a.PartNumber - b.PartNumber) }) - return result -} - -func printSavedEpisodeProgress( - writer io.Writer, - parts map[int32]episodeResumePart, - total int64, -) error { - var saved int64 - for _, part := range parts { - saved += part.Size - } - percent := min(episodeProgressComplete, saved*episodeProgressComplete/total) - if _, err := fmt.Fprintf(writer, "Source upload: %d%% saved\n", percent); err != nil { - return fmt.Errorf("print source upload progress: %w", err) - } - return nil -} - -func (client *episodeUploadClient) createOrResume( - ctx context.Context, - args episodesCreateArguments, - source episodeSourceIdentity, -) (episodeUploadSession, error) { - body := map[string]any{ - episodeShowSlugJSONField: args.Show, "title": args.Title, - "file_name": filepath.Base(source.AbsolutePath), "content_type": source.ContentType, - "byte_length": source.ByteLength, "source_sha256": source.SHA256, - } - if args.Description != nil { - body[descriptionFlagName] = *args.Description - } - var session episodeUploadSession - err := client.doJSON( - ctx, http.MethodPost, "/s/episode-upload-sessions", body, &session, http.StatusOK, http.StatusCreated, - ) - return session, err -} - -func (client *episodeUploadClient) presign( - ctx context.Context, - uploadSessionID string, - partNumbers []int32, -) (episodeUploadPartPresigns, error) { - var result episodeUploadPartPresigns - err := client.doJSON( - ctx, http.MethodPost, - "/s/episode-upload-sessions/"+uploadSessionID+"/parts/presign", - map[string]any{"part_numbers": partNumbers}, &result, http.StatusCreated, - ) - return result, err -} - -func (client *episodeUploadClient) complete( - ctx context.Context, - uploadSessionID string, - publication publicapi.EpisodePublication, -) (completedEpisode, error) { - var result completedEpisode - err := client.doJSON( - ctx, http.MethodPost, "/s/episode-upload-sessions/"+uploadSessionID+"/complete", - map[string]any{publicationFlagName: publication}, &result, http.StatusOK, http.StatusCreated, - ) - return result, err -} - -func (client *episodeUploadClient) uploadPart( - ctx context.Context, - presign episodeUploadPartPresign, - body io.Reader, - length int64, -) (string, error) { - request, err := http.NewRequestWithContext(ctx, presign.Method, presign.UploadURL, body) - if err != nil { - return "", fmt.Errorf("create upload request for part %d: %w", presign.PartNumber, err) - } - request.ContentLength = length - response, err := client.httpClient.Do(request) - if err != nil { - return "", fmt.Errorf("upload source part %d: %w", presign.PartNumber, err) - } - defer response.Body.Close() - if response.StatusCode < 200 || response.StatusCode >= 300 { - message, _ := io.ReadAll(io.LimitReader(response.Body, episodeErrorResponseLimit)) - return "", fmt.Errorf( - "upload source part %d returned HTTP %d: %s", - presign.PartNumber, - response.StatusCode, - strings.TrimSpace(string(message)), - ) - } - etag := response.Header.Get("ETag") - if etag == "" { - return "", fmt.Errorf("upload source part %d returned no ETag", presign.PartNumber) - } - return etag, nil -} - -func (client *episodeUploadClient) doJSON( - ctx context.Context, - method string, - path string, - body any, - target any, - wantStatus ...int, -) error { - var requestBody io.Reader - if body != nil { - encoded, err := json.Marshal(body) - if err != nil { - return fmt.Errorf("encode episode upload request: %w", err) - } - requestBody = bytes.NewReader(encoded) - } - request, err := http.NewRequestWithContext(ctx, method, client.apiOrigin+path, requestBody) - if err != nil { - return fmt.Errorf("create episode upload request: %w", err) - } - request.Header.Set("Authorization", "Bearer "+client.credential) - request.Header.Set("Accept", "application/json") - if body != nil { - request.Header.Set("Content-Type", "application/json") - } - response, err := client.httpClient.Do(request) - if err != nil { - return fmt.Errorf("send episode upload request: %w", err) - } - defer response.Body.Close() - if err := client.printResponseTrace(response); err != nil { - return err - } - if !slices.Contains(wantStatus, response.StatusCode) { - message, _ := io.ReadAll(io.LimitReader(response.Body, episodeErrorResponseLimit)) - return fmt.Errorf("episode upload returned HTTP %d: %s", response.StatusCode, strings.TrimSpace(string(message))) - } - decoder := json.NewDecoder(io.LimitReader(response.Body, episodeJSONResponseLimit)) - if err := decoder.Decode(target); err != nil { - return fmt.Errorf("decode episode upload response: %w", err) - } - return nil -} - -//nolint:cyclop,funlen // SSE parsing keeps each malformed and terminal condition explicit. -func (client *episodeUploadClient) waitForSafeHandoff( - ctx context.Context, - stderr io.Writer, - uploadSessionID string, -) (episodeCreateEvent, error) { - request, err := http.NewRequestWithContext( - ctx, http.MethodGet, client.apiOrigin+"/s/episode-upload-sessions/"+uploadSessionID+"/events", nil, - ) - if err != nil { - return episodeCreateEvent{}, err - } - request.Header.Set("Authorization", "Bearer "+client.credential) - request.Header.Set("Accept", "text/event-stream") - response, err := client.httpClient.Do(request) - if err != nil { - return episodeCreateEvent{}, fmt.Errorf("attach to episode processing: %w", err) - } - defer response.Body.Close() - if err := client.printResponseTrace(response); err != nil { - return episodeCreateEvent{}, err - } - if response.StatusCode != http.StatusOK { - message, _ := io.ReadAll(io.LimitReader(response.Body, episodeErrorResponseLimit)) - return episodeCreateEvent{}, fmt.Errorf( - "episode processing stream returned HTTP %d: %s", - response.StatusCode, - strings.TrimSpace(string(message)), - ) - } - scanner := bufio.NewScanner(response.Body) - var data string - for scanner.Scan() { - line := scanner.Text() - if value, ok := strings.CutPrefix(line, "data:"); ok { - data = strings.TrimSpace(value) - continue - } - if line != "" || data == "" { - continue - } - var event episodeCreateEvent - if err := json.Unmarshal([]byte(data), &event); err != nil { - return event, fmt.Errorf("decode episode processing event: %w", err) - } - data = "" - switch event.Type { - case "episode.processing.progress": - if _, err := fmt.Fprintf(stderr, "%s: %d%%\n", episodeProgressLabel(event.CurrentStep), event.Percent); err != nil { - return event, err - } - case "episode.processing.safe_handoff": - return event, nil - case "episode.processing.failed": - return event, fmt.Errorf("episode processing failed (%s): %s", event.ErrorCode, event.ErrorMessage) - default: - return event, fmt.Errorf("episode processing emitted unknown event type %q", event.Type) - } - } - if err := scanner.Err(); err != nil { - return episodeCreateEvent{}, fmt.Errorf("read episode processing stream: %w", err) - } - return episodeCreateEvent{}, errors.New("episode processing stream ended before safe handoff") -} - -func episodeProgressLabel(step string) string { - switch step { - case "queued", "processing_audio", "listenbox_processed": - return "Listenbox processing" - case "video_preparation", "video_prepared": - return "Video preparation" - case "youtube_upload": - return "YouTube upload" - default: - return "Episode processing" - } -} - -func episodeManagementURL(origin string, teamID string, showID string, episodeID string) (string, error) { - if !validCompletionID(teamID, "team_") || !validCompletionID(showID, "shw_") || - !validCompletionID(episodeID, "ep_") { - return "", errors.New("episode create returned invalid management identifiers") - } - result, err := url.JoinPath(origin, teamID, "shows", showID, "episodes", episodeID, "edit") - if err != nil { - return "", fmt.Errorf("build episode management URL: %w", err) - } - return result, nil -} - -func (client *episodeUploadClient) printResponseTrace(response *http.Response) error { - if client.traceWriter == nil { - return nil - } - traceID := response.Header.Get("X-Trace-Id") - decoded, err := hex.DecodeString(traceID) - if err != nil || len(decoded) != 16 || traceID != strings.ToLower(traceID) || strings.Trim(traceID, "0") == "" { - return fmt.Errorf("print episode request trace ID: invalid X-Trace-Id %q", traceID) - } - if _, err := fmt.Fprintf(client.traceWriter, "Trace ID: %s\n", traceID); err != nil { - return fmt.Errorf("print episode request trace ID %q: %w", traceID, err) - } - return nil -} diff --git a/internal/cli/episodes_list_test.go b/internal/cli/episodes_list_test.go deleted file mode 100644 index 5ba262e..0000000 --- a/internal/cli/episodes_list_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "gotest.tools/v3/assert" -) - -func TestEpisodesListAutoDrainsPagesWithLimit(t *testing.T) { - t.Parallel() - - requestNumber := 0 - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - requestNumber++ - assert.Equal(t, request.Method, http.MethodGet) - assert.Equal(t, request.URL.Path, "/s/shows/cli-show/episodes") - assert.Equal(t, request.Header.Get("Authorization"), "Bearer "+testCLIAPIKey) - assert.Equal(t, request.URL.Query().Get("limit"), "2") - writer.Header().Set("Content-Type", "application/json") - switch requestNumber { - case 1: - assert.Equal(t, request.URL.Query().Get("cursor"), "") - writeEpisodeListTestPage(t, writer, []string{"newest", "middle"}, "cursor-1") - case 2: - assert.Equal(t, request.URL.Query().Get("cursor"), "cursor-1") - writeEpisodeListTestPage(t, writer, []string{"oldest"}, "") - default: - http.Error(writer, "unexpected request", http.StatusInternalServerError) - } - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{ - configFlagArgument, configPath, episodesCommandName, listCommandName, - showFlagArgument, testCLIShowSlug, "--limit", "2", - }, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.NilError(t, err) - assert.Equal(t, requestNumber, 2) - assert.Equal(t, stdout.String(), "ep_newest\nep_middle\nep_oldest\n") -} - -func TestEpisodesListOmitsOptionalLimitAndHandlesEmptyShow(t *testing.T) { - t.Parallel() - - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - _, limitPresent := request.URL.Query()["limit"] - assert.Assert(t, !limitPresent, "limit omitted") - writer.Header().Set("Content-Type", "application/json") - writeEpisodeListTestPage(t, writer, []string{}, "") - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, episodesCommandName, listCommandName, showFlagArgument, testCLIShowSlug}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.NilError(t, err) - assert.Equal(t, stdout.String(), "") -} - -func TestEpisodesListRejectsRepeatedCursorAfterStreamingPages(t *testing.T) { - t.Parallel() - - requestNumber := 0 - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { - requestNumber++ - writer.Header().Set("Content-Type", "application/json") - writeEpisodeListTestPage(t, writer, []string{fmt.Sprintf("episode-%d", requestNumber)}, "repeat") - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, episodesCommandName, listCommandName, showFlagArgument, testCLIShowSlug}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.ErrorContains(t, err, "returned repeated cursor") - assert.Equal(t, stdout.String(), "ep_episode-1\nep_episode-2\n") -} - -func TestEpisodesListPreservesStreamedOutputWhenLaterPageFails(t *testing.T) { - t.Parallel() - - requestNumber := 0 - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { - requestNumber++ - if requestNumber == 1 { - writer.Header().Set("Content-Type", "application/json") - writeEpisodeListTestPage(t, writer, []string{"already-streamed"}, "next") - return - } - http.Error(writer, "later failure", http.StatusInternalServerError) - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, episodesCommandName, listCommandName, showFlagArgument, testCLIShowSlug}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.ErrorContains(t, err, "later failure") - assert.Equal(t, stdout.String(), "ep_already-streamed\n") -} - -func writeEpisodeListTestPage(t *testing.T, writer http.ResponseWriter, slugs []string, nextCursor string) { - t.Helper() - episodes := make([]map[string]any, 0, len(slugs)) - for _, slug := range slugs { - episodes = append(episodes, map[string]any{ - "id": "ep_" + slug, testShowIDField: "shw_test", "title": slug, - "slug": slug, statusCommandName: "draft", "enclosures": []any{}, - }) - } - page := map[string]any{"episodes": episodes} - if nextCursor != "" { - page["next_cursor"] = nextCursor - } - assert.NilError(t, json.NewEncoder(writer).Encode(page)) -} diff --git a/internal/cli/episodes_resume_test.go b/internal/cli/episodes_resume_test.go deleted file mode 100644 index 1d4119a..0000000 --- a/internal/cli/episodes_resume_test.go +++ /dev/null @@ -1,98 +0,0 @@ -//nolint:lll // Resume fixtures keep the complete strong file identity visible. -package cli - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - "time" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" - "gotest.tools/v3/assert" -) - -func TestEpisodeResumeLookupIsScopedByShowAndFileSHA(t *testing.T) { - t.Parallel() - home := t.TempDir() - path := filepath.Join(t.TempDir(), "source.mp3") - args := episodesCreateArguments{ - Show: testShowsCreateSlug, Title: testEpisodeTitle, File: path, - Publication: publicapi.EpisodePublicationDraft, - } - source := episodeSourceIdentity{AbsolutePath: path, ByteLength: 10, ContentType: episodeAudioMIME, SHA256: strings.Repeat("a", 64)} - resumePath, first, err := loadOrCreateEpisodeResume(home, args, source) - assert.NilError(t, err) - secondPath := filepath.Join(t.TempDir(), "renamed.mp3") - resumed := source - resumed.AbsolutePath = secondPath - changedArgs := args - changedArgs.Title = "Changed local command metadata" - changedArgs.Publication = publicapi.EpisodePublicationPublish - resolvedPath, second, err := loadOrCreateEpisodeResume(home, changedArgs, resumed) - assert.NilError(t, err) - assert.Equal(t, resolvedPath, resumePath) - assert.Equal(t, second.FileSHA256, first.FileSHA256) - assert.Equal(t, second.FilePath, secondPath) - _, statErr := os.Stat(resumePath) - assert.NilError(t, statErr) - changedHash := source - changedHash.SHA256 = strings.Repeat("b", 64) - newPath, _, err := loadOrCreateEpisodeResume(home, args, changedHash) - assert.NilError(t, err) - assert.Assert(t, newPath != resumePath) -} - -func TestEpisodeResumeRejectsExpiredAttemptAndPreservesDraftRecord(t *testing.T) { - t.Parallel() - home := t.TempDir() - path := filepath.Join(t.TempDir(), "source.mp3") - args := episodesCreateArguments{ - Show: testShowsCreateSlug, Title: testEpisodeTitle, File: path, - Publication: publicapi.EpisodePublicationDraft, - } - source := episodeSourceIdentity{AbsolutePath: path, ByteLength: 10, ContentType: episodeAudioMIME, SHA256: strings.Repeat("a", 64)} - resumePath, record, err := loadOrCreateEpisodeResume(home, args, source) - assert.NilError(t, err) - record.ExpiresAt = time.Now().Add(-time.Minute).UnixMilli() - record.Phase = "uploading" - assert.NilError(t, writeEpisodeResume(resumePath, record)) - _, preserved, err := loadOrCreateEpisodeResume(home, args, source) - assert.ErrorContains(t, err, "expired") - assert.Equal(t, preserved.UploadSessionID, record.UploadSessionID) -} - -func TestSavedEpisodeProgressCountsAcceptedPartsOnly(t *testing.T) { - t.Parallel() - parts := map[int32]episodeResumePart{ - 1: {PartNumber: 1, Size: 5}, - } - var output bytes.Buffer - assert.NilError(t, printSavedEpisodeProgress(&output, parts, 10)) - assert.Equal(t, output.String(), "Source upload: 50% saved\n") - parts[2] = episodeResumePart{PartNumber: 2, Size: 5} - assert.NilError(t, printSavedEpisodeProgress(&output, parts, 10)) - assert.Equal(t, output.String(), "Source upload: 50% saved\nSource upload: 100% saved\n") -} - -func TestEpisodeResumeRecordContainsUploadFactsOnly(t *testing.T) { - t.Parallel() - home := t.TempDir() - path := filepath.Join(t.TempDir(), "source.mp3") - args := episodesCreateArguments{ - Show: testShowsCreateSlug, Title: testEpisodeTitle, File: path, - Publication: publicapi.EpisodePublicationDraft, - } - source := episodeSourceIdentity{AbsolutePath: path, ByteLength: 10, ContentType: episodeAudioMIME, SHA256: strings.Repeat("a", 64)} - resumePath, _, err := loadOrCreateEpisodeResume(home, args, source) - assert.NilError(t, err) - raw, err := os.ReadFile(resumePath) - assert.NilError(t, err) - assert.Assert(t, !bytes.Contains(raw, []byte("upload_url"))) - assert.Assert(t, !bytes.Contains(raw, []byte("api_key"))) - assert.Assert(t, !bytes.Contains(raw, []byte("publication"))) - info, err := os.Stat(resumePath) - assert.NilError(t, err) - assert.Equal(t, info.Mode().Perm(), os.FileMode(0o600)) -} diff --git a/internal/cli/episodes_source_test.go b/internal/cli/episodes_source_test.go deleted file mode 100644 index 8b210a2..0000000 --- a/internal/cli/episodes_source_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package cli - -import ( - "crypto/sha256" - "encoding/hex" - "os" - "path/filepath" - "testing" - - "gotest.tools/v3/assert" -) - -func TestInspectEpisodeSourceLeavesMediaValidationToServer(t *testing.T) { - t.Parallel() - - sourceBytes := []byte("opaque upload bytes that only the server may probe") - sourcePath := filepath.Join(t.TempDir(), "episode.mp4") - assert.NilError(t, os.WriteFile(sourcePath, sourceBytes, 0o600)) - - source, err := inspectEpisodeSource(sourcePath) - assert.NilError(t, err) - assert.Equal(t, source.ContentType, "video/mp4") - assert.Assert(t, source.ByteLength > 0) - assert.Assert(t, source.ByteLength < 1<<10) - expectedHash := sha256.Sum256(sourceBytes) - assert.Equal(t, source.SHA256, hex.EncodeToString(expectedHash[:])) -} diff --git a/internal/cli/execute.go b/internal/cli/execute.go deleted file mode 100644 index 15fda0f..0000000 --- a/internal/cli/execute.go +++ /dev/null @@ -1,49 +0,0 @@ -package cli - -import ( - "context" - "errors" - "fmt" - "io" - "os" - "os/signal" - "syscall" - - "github.com/knadh/koanf/parsers/yaml" - "github.com/knadh/koanf/providers/rawbytes" - "github.com/knadh/koanf/v2" -) - -const interruptedExitCode = 130 - -// Execute runs the CLI and terminates the process with its exit status. -func Execute() { - os.Exit(runMain()) -} - -func runMain() int { - ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) - defer stop() - - if err := runCLI(ctx, os.Args[1:], os.Stdout, os.Stderr); err != nil { - if _, ok := errors.AsType[*episodeUploadInterruptedError](err); ok { - writeCLIError(os.Stderr, err) - return interruptedExitCode - } - writeCLIError(os.Stderr, err) - return 1 - } - return 0 -} - -func writeCLIError(stderr io.Writer, err error) { - _, _ = fmt.Fprintln(stderr, err) -} - -func loadEmbeddedCLIConfig() (loadedCLIConfig, error) { - k := koanf.New(".") - if err := k.Load(rawbytes.Provider(embeddedCLIConfig), yaml.Parser()); err != nil { - return loadedCLIConfig{}, fmt.Errorf("load embedded CLI config: %w", err) - } - return decodeCLIConfig(k, "embedded CLI config") -} diff --git a/internal/cli/import.go b/internal/cli/import.go deleted file mode 100644 index 411cbe8..0000000 --- a/internal/cli/import.go +++ /dev/null @@ -1,403 +0,0 @@ -package cli - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "strings" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" - "github.com/mattn/go-isatty" -) - -const ( - importProgressBarWidth = 24 - importProgressEventType = "progress" - importTerminalEventType = "terminal" - importCompletedStatus = "completed" - importCancelledStatus = "cancelled" - importFailedStatus = "failed" - maxImportPercent = 100 -) - -func importPodcast( - ctx context.Context, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, - sourceURL string, - slug string, - slugExplicit bool, -) error { - if isYouTubeSource(sourceURL) { - return importYouTube( - ctx, stdout, stderr, home, configPath, configExplicit, defaultConfig, sourceURL, slug, slugExplicit, - ) - } - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("import RSS feed: not logged in; run listenbox login") - } - client, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return err - } - body := importRSSRequest(sourceURL, slug, slugExplicit) - response, err := client.ImportRSS(ctx, publicapi.ImportRSSParams{Body: body}) - if err != nil { - return fmt.Errorf("import RSS feed %q: %w", sourceURL, err) - } - if response == nil { - return fmt.Errorf("import RSS feed %q against %q returned no response", sourceURL, config.apiOrigin) - } - if response.Status202 != nil { - if config.printTraceIDs { - if err := printImportTraceID(stdout, response.Raw.Header.Get("X-Trace-Id")); err != nil { - return err - } - } - return consumeImportRSSRun(ctx, stdout, stderr, sourceURL, config, client, response) - } - return handleImportRSSResponse( - sourceURL, - slug, - slugExplicit, - response, - ) -} - -func importRSSRequest(sourceURL string, slug string, slugExplicit bool) publicapi.ImportRSSRequest { - body := publicapi.ImportRSSRequest{SourceUrl: sourceURL} - if slugExplicit { - body.Slug = &slug - } - return body -} - -func consumeImportRSSRun( - ctx context.Context, - stdout io.Writer, - stderr io.Writer, - sourceURL string, - config loadedCLIConfig, - client *publicapi.Client, - response *publicapi.ImportRSSResponse, -) error { - eventsResponse, err := client.ImportRSSRunEvents(ctx, publicapi.ImportRSSRunEventsParams{ - ImportRunId: response.Status202.ImportRunId, - }) - if err != nil { - return fmt.Errorf("read RSS import event stream for %q: %w", sourceURL, err) - } - if eventsResponse == nil || eventsResponse.Status200 == nil { - status := 0 - if eventsResponse != nil { - status = eventsResponse.StatusCode - } - return fmt.Errorf("RSS import event stream for %q returned HTTP status %d", sourceURL, status) - } - return consumeImportRSSStream( - ctx, - stdout, - stderr, - sourceURL, - config.dashboardOrigin, - eventsResponse.Status200, - ) -} - -func handleImportRSSResponse( - sourceURL string, - requestedSlug string, - slugExplicit bool, - response *publicapi.ImportRSSResponse, -) error { - switch { - case response.Status202 != nil: - return fmt.Errorf("import RSS feed %q returned a run without an event stream", sourceURL) - case response.Status400 != nil: - return fmt.Errorf("import RSS feed %q: %s", sourceURL, response.Status400.Message) - case response.Status402 != nil: - return fmt.Errorf( - "import RSS feed %q: %s import requires the %s entitlement (current: %s); upgrade at %s and retry", - sourceURL, - response.Status402.MediaKind, - response.Status402.RequiredEntitlement, - response.Status402.CurrentEntitlement, - response.Status402.PricingUrl, - ) - case response.StatusCode == http.StatusUnauthorized: - return fmt.Errorf("import RSS feed %q: authentication failed; run listenbox login", sourceURL) - case response.StatusCode == http.StatusForbidden: - return fmt.Errorf("import RSS feed %q: API key lacks show:create scope", sourceURL) - case response.StatusCode == http.StatusConflict && slugExplicit: - return fmt.Errorf("import RSS feed %q: requested slug %q conflicts", sourceURL, requestedSlug) - case response.StatusCode == http.StatusConflict: - return fmt.Errorf("import RSS feed %q: import choice conflicts", sourceURL) - default: - return fmt.Errorf("import RSS feed %q returned HTTP status %d", sourceURL, response.StatusCode) - } -} - -func printImportTraceID(stdout io.Writer, traceID string) error { - decoded, err := hex.DecodeString(traceID) - if err != nil || len(decoded) != 16 || traceID != strings.ToLower(traceID) || strings.Trim(traceID, "0") == "" { - return fmt.Errorf("print RSS import trace ID: invalid trace_id %q", traceID) - } - if _, err := fmt.Fprintf(stdout, "Trace ID: %s\n", traceID); err != nil { - return fmt.Errorf("print RSS import trace ID %q: %w", traceID, err) - } - return nil -} - -func consumeImportRSSStream( - ctx context.Context, - stdout io.Writer, - stderr io.Writer, - sourceURL string, - dashboardOrigin string, - stream *publicapi.SSEStream[publicapi.PublicRSSImportEvent], -) error { - defer func() { _ = stream.Close() }() - progress := newImportProgress(stderr, writerIsTerminal(stderr)) - defer progress.cleanFailureLine() - for { - event, ok, err := stream.Next(ctx) - if err != nil { - return fmt.Errorf("read RSS import stream for %q: %w", sourceURL, err) - } - if !ok { - return fmt.Errorf("RSS import stream for %q ended before a terminal event", sourceURL) - } - done, err := handleImportRSSEvent(stdout, stderr, sourceURL, dashboardOrigin, progress, event) - if err != nil { - return err - } - if done { - return nil - } - } -} - -func handleImportRSSEvent( - stdout io.Writer, - stderr io.Writer, - sourceURL string, - dashboardOrigin string, - progress *importProgress, - event publicapi.PublicRSSImportEvent, -) (bool, error) { - if event.RSSImportProgressEvent != nil { - current := event.RSSImportProgressEvent - if current.Type != importProgressEventType || current.Percent < 0 || current.Percent > maxImportPercent { - return false, fmt.Errorf("RSS import stream for %q emitted malformed progress event", sourceURL) - } - return false, progress.update(uint32(current.Percent)) - } - if event.PublicRSSImportTerminalEvent == nil { - return false, fmt.Errorf("RSS import stream for %q emitted an empty event variant", sourceURL) - } - return handleImportRSSTerminal( - stdout, - stderr, - sourceURL, - dashboardOrigin, - progress, - event.PublicRSSImportTerminalEvent, - ) -} - -func handleImportRSSTerminal( - stdout io.Writer, - stderr io.Writer, - sourceURL string, - dashboardOrigin string, - progress *importProgress, - terminal *publicapi.PublicRSSImportTerminalEvent, -) (bool, error) { - if terminal.PublicRSSImportCompletedTerminalEvent != nil { - return finishCompletedRSSImport( - stdout, - stderr, - sourceURL, - dashboardOrigin, - progress, - terminal.PublicRSSImportCompletedTerminalEvent, - ) - } - if terminal.RSSImportCancelledTerminalEvent != nil { - cancelled := terminal.RSSImportCancelledTerminalEvent - if cancelled.Type != importTerminalEventType || cancelled.Status != importCancelledStatus { - return false, fmt.Errorf("RSS import stream for %q emitted malformed cancelled terminal event", sourceURL) - } - return false, fmt.Errorf("RSS import feed %q was cancelled", sourceURL) - } - if terminal.RSSImportFailedTerminalEvent == nil { - return false, fmt.Errorf("RSS import stream for %q emitted an empty terminal event variant", sourceURL) - } - return false, failedRSSImportError(sourceURL, terminal.RSSImportFailedTerminalEvent) -} - -func finishCompletedRSSImport( - stdout io.Writer, - stderr io.Writer, - sourceURL string, - dashboardOrigin string, - progress *importProgress, - completed *publicapi.PublicRSSImportCompletedTerminalEvent, -) (bool, error) { - if completed.Type != importTerminalEventType || completed.Status != importCompletedStatus || - !validCompletionID(completed.FeedId, "lb_") || !validShowSlug(completed.ShowSlug) { - return false, fmt.Errorf("RSS import stream for %q emitted malformed completed terminal event", sourceURL) - } - showURL, err := showManagementURL(dashboardOrigin, completed.TeamId, completed.ShowId) - if err != nil { - return false, fmt.Errorf( - "RSS import stream for %q emitted malformed completed terminal event: %w", - sourceURL, - err, - ) - } - if err := progress.complete(); err != nil { - return false, err - } - if _, err := fmt.Fprintln(stdout, completed.ShowSlug); err != nil { - return false, fmt.Errorf("print imported show slug %q: %w", completed.ShowSlug, err) - } - if _, err := fmt.Fprintf(stderr, "Open in Listenbox: %s\n", showURL); err != nil { - return false, fmt.Errorf("print imported show URL %q: %w", showURL, err) - } - return true, nil -} - -func showManagementURL(dashboardOrigin string, teamID string, showID string) (string, error) { - normalizedOrigin, err := normalizeOrigin(dashboardOrigin, "Dashboard") - if err != nil { - return "", err - } - if !validCompletionID(teamID, "team_") { - return "", fmt.Errorf("invalid team_id %q", teamID) - } - if !validCompletionID(showID, "shw_") { - return "", fmt.Errorf("invalid show_id %q", showID) - } - showURL, err := url.JoinPath(normalizedOrigin, teamID, "shows", showID) - if err != nil { - return "", fmt.Errorf( - "build show URL from Dashboard origin %q, team ID %q, and show ID %q: %w", - normalizedOrigin, - teamID, - showID, - err, - ) - } - return showURL, nil -} - -func validCompletionID(value string, prefix string) bool { - return strings.HasPrefix(value, prefix) && len(value) > len(prefix) && - strings.TrimSpace(value) == value && url.PathEscape(value) == value -} - -func validShowSlug(value string) bool { - if len(value) == 0 || len(value) > 63 { - return false - } - previousHyphen := true - for _, current := range value { - switch { - case current >= 'a' && current <= 'z', current >= '0' && current <= '9': - previousHyphen = false - case current == '-' && !previousHyphen: - previousHyphen = true - default: - return false - } - } - return !previousHyphen -} - -func failedRSSImportError(sourceURL string, failed *publicapi.RSSImportFailedTerminalEvent) error { - if failed.Type != importTerminalEventType || failed.Status != importFailedStatus || - strings.TrimSpace(failed.ErrorCode) == "" || strings.TrimSpace(failed.ErrorMessage) == "" { - return fmt.Errorf("RSS import stream for %q emitted malformed failed terminal event", sourceURL) - } - return fmt.Errorf( - "RSS import feed %q failed (%s): %s", - sourceURL, - failed.ErrorCode, - failed.ErrorMessage, - ) -} - -type importProgress struct { - writer io.Writer - tty bool - hasValue bool - last uint32 - lineOpen bool -} - -func newImportProgress(writer io.Writer, tty bool) *importProgress { - return &importProgress{writer: writer, tty: tty} -} - -func (progress *importProgress) update(percent uint32) error { - if progress.hasValue && progress.last == percent { - return nil - } - progress.hasValue = true - progress.last = percent - if !progress.tty { - if _, err := fmt.Fprintf(progress.writer, "Import progress: %d%%\n", percent); err != nil { - return fmt.Errorf("print RSS import progress %d%%: %w", percent, err) - } - return nil - } - filled := int(percent) * importProgressBarWidth / maxImportPercent - bar := strings.Repeat("#", filled) + strings.Repeat("-", importProgressBarWidth-filled) - if _, err := fmt.Fprintf(progress.writer, "\rImporting [%s] %3d%%", bar, percent); err != nil { - return fmt.Errorf("print RSS import progress %d%%: %w", percent, err) - } - progress.lineOpen = true - return nil -} - -func (progress *importProgress) complete() error { - if err := progress.update(maxImportPercent); err != nil { - return err - } - if progress.tty && progress.lineOpen { - if _, err := fmt.Fprintln(progress.writer); err != nil { - return fmt.Errorf("finish RSS import progress line: %w", err) - } - progress.lineOpen = false - } - return nil -} - -func (progress *importProgress) cleanFailureLine() { - if progress.tty && progress.lineOpen { - _, _ = fmt.Fprintln(progress.writer) - progress.lineOpen = false - } -} - -func writerIsTerminal(writer io.Writer) bool { - descriptor, ok := writer.(interface{ Fd() uintptr }) - if !ok { - return false - } - return isatty.IsTerminal(descriptor.Fd()) || isatty.IsCygwinTerminal(descriptor.Fd()) -} diff --git a/internal/cli/import_test.go b/internal/cli/import_test.go deleted file mode 100644 index 8c116dc..0000000 --- a/internal/cli/import_test.go +++ /dev/null @@ -1,445 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "strings" - "sync/atomic" - "testing" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" - "gotest.tools/v3/assert" -) - -const ( - //nolint:gosec // Synthetic credential used only by local test servers. - testCLIAPIKey = "apk_cli.AAAAAAAAAAAAAAAAAAAAAA" - testRSSURL = "https://feeds.test/rss" - testMalformedImportCompletion = "malformed completed terminal event" -) - -//nolint:funlen // Covers all supported help entry points and positional errors together. -func TestImportHelpAndArgumentParsing(t *testing.T) { - t.Parallel() - - t.Run("root lists import", func(t *testing.T) { - t.Parallel() - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{helpCommandName}, - &stdout, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(stdout.String(), "import Import an RSS feed")) - }) - - for _, args := range [][]string{ - {helpCommandName, importCommandName}, - {importCommandName, longHelpFlag}, - {importCommandName, shortHelpFlag}, - } { - t.Run(strings.Join(args, " "), func(t *testing.T) { - t.Parallel() - var stdout bytes.Buffer - var stderr bytes.Buffer - err := runCLIWithHome(context.Background(), args, &stdout, &stderr, t.TempDir()) - assert.NilError(t, err) - output := stdout.String() + stderr.String() - assert.Assert(t, strings.Contains(output, - "Usage: listenbox [--config PATH] import [--config PATH] [--slug SLUG] ")) - assert.Assert(t, strings.Contains(output, "--slug SLUG")) - assert.Assert(t, strings.Contains(output, "--config PATH")) - assert.Assert(t, strings.Contains( - output, - "Progress and the management link are written to stderr", - )) - assert.Assert(t, strings.Contains(output, "final show slug is written to stdout")) - }) - } - - for _, testCase := range []struct { - name string - args []string - want string - }{ - {name: "missing URL", args: []string{importCommandName}, want: "requires exactly one source URL"}, - { - name: "extra URL", - args: []string{importCommandName, "https://one.test/rss", "https://two.test/rss"}, - want: `unexpected argument "https://two.test/rss"`, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - testCase.args, - &stdout, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, testCase.want) - assert.Equal(t, stdout.String(), "") - }) - } -} - -//nolint:funlen // Covers exact request and split stdout/stderr contracts for optional slug cases. -func TestImportRequestAuthOptionalSlugAndExactOutput(t *testing.T) { - t.Parallel() - - for _, testCase := range []struct { - name string - args []string - wantSlug string - wantSlugInBody bool - }{ - { - name: "omitted slug", - args: []string{"https://feeds.test/podcast.xml"}, - }, - { - name: "explicit slug", - args: []string{slugFlagArgument, "requested-show", "https://feeds.test/podcast.xml"}, - wantSlug: "requested-show", - wantSlugInBody: true, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - requests := make(chan importTestRequest, 1) - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodPost { - var body map[string]any - decodeErr := json.NewDecoder(request.Body).Decode(&body) - requests <- importTestRequest{ - authorization: request.Header.Get("Authorization"), - body: body, - decodeErr: decodeErr, - } - writeTestImportCreation(writer) - return - } - writer.Header().Set("Content-Type", "text/event-stream") - writeTestSSEEvent(writer, map[string]any{testEventTypeField: importProgressEventType, "percent": 10}) - writeTestSSEEvent(writer, map[string]any{testEventTypeField: importProgressEventType, "percent": 10}) - writeTestSSEEvent(writer, map[string]any{ - testEventTypeField: importTerminalEventType, - statusCommandName: importCompletedStatus, - "feed_id": "lb_server_feed", - testTeamIDField: "team_0123456789abcdef", - testShowIDField: "shw_0123456789abcdef", - "show_slug": "actual-server-show", - }) - })) - defer server.Close() - - stdout, stderr, err := runImportAgainstServer( - context.Background(), - t, - server.URL, - testCase.args, - "https://dashboard.test:9443", - ) - assert.NilError(t, err) - assert.Equal(t, stdout, "actual-server-show\n") - assert.Equal( - t, - stderr, - "Import progress: 10%\nImport progress: 100%\n"+ - "Open in Listenbox: https://dashboard.test:9443/team_0123456789abcdef/"+ - "shows/shw_0123456789abcdef\n", - ) - request := <-requests - assert.NilError(t, request.decodeErr) - assert.Equal(t, request.authorization, "Bearer "+testCLIAPIKey) - assert.Equal(t, request.body["source_url"], "https://feeds.test/podcast.xml") - slug, hasSlug := request.body["slug"] - assert.Equal(t, hasSlug, testCase.wantSlugInBody) - if hasSlug { - assert.Equal(t, slug, testCase.wantSlug) - } - }) - } -} - -func TestImportAcceptsCommandLocalConfigFlag(t *testing.T) { - t.Parallel() - - server := importCompletedTestServer("local-config-show") - defer server.Close() - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{importCommandName, "--config", configPath, testRSSURL}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.NilError(t, err) - assert.Equal(t, stdout.String(), "local-config-show\n") -} - -func TestImportHTTPStatusMappingsLeaveStdoutEmpty(t *testing.T) { - t.Parallel() - - for _, testCase := range []struct { - name string - status int - body string - want string - }{ - { - name: "bad request", - status: http.StatusBadRequest, - body: `{"message":"invalid RSS URL","errors":[]}`, - want: "invalid RSS URL", - }, - { - name: "entitlement required", - status: http.StatusPaymentRequired, - body: `{"code":"rss_import_entitlement_required","media_kind":"video",` + - `"current_entitlement":"audio","required_entitlement":"video_hd",` + - `"pricing_url":"https://listenbox.app/pricing"}`, - want: "video import requires the video_hd entitlement (current: audio); upgrade at " + - "https://listenbox.app/pricing and retry", - }, - {name: "unauthorized", status: http.StatusUnauthorized, want: "authentication failed"}, - {name: "forbidden", status: http.StatusForbidden, want: "lacks show:create scope"}, - {name: "conflict", status: http.StatusConflict, want: "requested slug \"taken\" conflicts"}, - {name: "unexpected", status: http.StatusTeapot, body: "unexpected", want: "HTTP status 418"}, - } { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { - if testCase.body != "" { - writer.Header().Set("Content-Type", "application/json") - } - writer.WriteHeader(testCase.status) - _, _ = io.WriteString(writer, testCase.body) - })) - defer server.Close() - stdout, _, err := runImportAgainstServer( - context.Background(), - t, - server.URL, - []string{"--slug", "taken", testRSSURL}, - ) - assert.ErrorContains(t, err, testCase.want) - assert.Equal(t, stdout, "") - }) - } -} - -//nolint:funlen // Table enumerates malformed and failed stream terminal contracts. -func TestImportStreamFailuresLeaveStdoutEmpty(t *testing.T) { - t.Parallel() - - for _, testCase := range []struct { - name string - events []string - want string - }{ - { - name: "failed terminal", - events: []string{ - `{"type":"terminal","status":"failed","error_code":"feed_fetch_failed","error_message":"boom"}`, - }, - want: "failed (feed_fetch_failed): boom", - }, - { - name: "premature EOF", - events: []string{`{"type":"progress","percent":30}`}, - want: "ended before a terminal event", - }, - { - name: "empty variant", - events: []string{`{}`}, - want: "unsupported PublicRSSImportEvent discriminator", - }, - { - name: "missing feed id", - events: []string{ - `{"type":"terminal","status":"completed","team_id":"team_valid",` + - `"show_id":"shw_valid","show_slug":"show"}`, - }, - want: testMalformedImportCompletion, - }, - { - name: "missing show slug", - events: []string{ - `{"type":"terminal","status":"completed","feed_id":"lb_feed",` + - `"team_id":"team_valid","show_id":"shw_valid"}`, - }, - want: testMalformedImportCompletion, - }, - { - name: "missing team id", - events: []string{ - `{"type":"terminal","status":"completed","feed_id":"lb_feed",` + - `"show_id":"shw_valid","show_slug":"show"}`, - }, - want: "invalid team_id", - }, - { - name: "missing show id", - events: []string{ - `{"type":"terminal","status":"completed","feed_id":"lb_feed",` + - `"team_id":"team_valid","show_slug":"show"}`, - }, - want: "invalid show_id", - }, - { - name: "unsafe team id", - events: []string{ - `{"type":"terminal","status":"completed","feed_id":"lb_feed",` + - `"team_id":"team_bad/segment","show_id":"shw_valid","show_slug":"show"}`, - }, - want: "invalid team_id", - }, - { - name: "unsafe show slug", - events: []string{ - `{"type":"terminal","status":"completed","feed_id":"lb_feed",` + - `"team_id":"team_valid","show_id":"shw_valid","show_slug":"show\nextra"}`, - }, - want: testMalformedImportCompletion, - }, - } { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - var streamServed atomic.Bool - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodPost { - writeTestImportCreation(writer) - return - } - if streamServed.Swap(true) { - writer.WriteHeader(http.StatusNoContent) - return - } - writer.Header().Set("Content-Type", "text/event-stream") - for _, event := range testCase.events { - _, _ = io.WriteString(writer, "data: "+event+"\n\n") - } - })) - defer server.Close() - stdout, _, err := runImportAgainstServer( - context.Background(), - t, - server.URL, - []string{testRSSURL}, - ) - assert.ErrorContains(t, err, testCase.want) - assert.Equal(t, stdout, "") - }) - } -} - -func TestImportStreamCancellationFails(t *testing.T) { - t.Parallel() - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - stream := publicapi.NewSSEStream[publicapi.PublicRSSImportEvent]( - io.NopCloser(strings.NewReader("")), - ) - var stdout bytes.Buffer - err := consumeImportRSSStream( - ctx, - &stdout, - &bytes.Buffer{}, - testRSSURL, - "https://web.listenbox.app", - stream, - ) - assert.ErrorContains(t, err, "context canceled") - assert.Equal(t, stdout.String(), "") -} - -func TestImportProgressTTYAndNonTTY(t *testing.T) { - t.Parallel() - - var plain bytes.Buffer - plainProgress := newImportProgress(&plain, false) - assert.NilError(t, plainProgress.update(25)) - assert.NilError(t, plainProgress.update(25)) - assert.NilError(t, plainProgress.complete()) - assert.Equal(t, plain.String(), "Import progress: 25%\nImport progress: 100%\n") - - var tty bytes.Buffer - ttyProgress := newImportProgress(&tty, true) - assert.NilError(t, ttyProgress.update(25)) - assert.NilError(t, ttyProgress.update(25)) - assert.NilError(t, ttyProgress.complete()) - assert.Equal(t, strings.Count(tty.String(), " 25%"), 1) - assert.Equal(t, strings.Count(tty.String(), "100%"), 1) - assert.Assert(t, strings.Contains(tty.String(), "\rImporting [")) - assert.Assert(t, strings.HasSuffix(tty.String(), "\n")) -} - -type importTestRequest struct { - authorization string - body map[string]any - decodeErr error -} - -func runImportAgainstServer( - ctx context.Context, - t *testing.T, - apiOrigin string, - importArgs []string, - dashboardOrigins ...string, -) (string, string, error) { - t.Helper() - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, apiOrigin, dashboardOrigins...) - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - args := make([]string, 0, 3+len(importArgs)) - args = append(args, configFlagArgument, configPath, importCommandName) - args = append(args, importArgs...) - var stdout bytes.Buffer - var stderr bytes.Buffer - err := runCLIWithHome(ctx, args, &stdout, &stderr, home) - return stdout.String(), stderr.String(), err -} - -func importCompletedTestServer(showSlug string) *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodPost { - writeTestImportCreation(writer) - return - } - writer.Header().Set("Content-Type", "text/event-stream") - writeTestSSEEvent(writer, map[string]any{ - testEventTypeField: importTerminalEventType, - "status": "completed", - "feed_id": "lb_feed", - testTeamIDField: "team_0123456789abcdef", - "show_id": "shw_0123456789abcdef", - "show_slug": showSlug, - }) - })) -} - -func writeTestImportCreation(writer http.ResponseWriter) { - writer.Header().Set("Content-Type", "application/json") - writer.Header().Set("X-Trace-Id", "0123456789abcdef0123456789abcdef") - writer.WriteHeader(http.StatusAccepted) - if err := json.NewEncoder(writer).Encode(map[string]string{ - "import_run_id": "imp_0123456789abcdef0123456789abcdef", - }); err != nil { - panic(err) - } -} diff --git a/internal/cli/login.go b/internal/cli/login.go deleted file mode 100644 index 5e6388b..0000000 --- a/internal/cli/login.go +++ /dev/null @@ -1,310 +0,0 @@ -package cli - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "slices" - - "github.com/listenbox/listenbox-cli/publicapi" -) - -func login( - ctx context.Context, - stdout io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - authPath := defaultAuthPath(home) - previous, hasPrevious := readUsableStoredAuth(authPath) - - client, response, err := beginCLIAuthorization(ctx, config, previous, hasPrevious) - if err != nil { - return err - } - created := response.Status201 - credential, err := acceptPendingAuthorization( - stdout, - "", - &publicapi.CLIAuthorizationPendingEvent{ - Type: "cli.authorization.pending", - VerificationUrl: created.VerificationUrl, - Credential: created.Credential, - }, - config.apiOrigin, - config.dashboardOrigin, - ) - if err != nil { - return err - } - stream, err := openCLIAuthorizationStream(ctx, client, created.Code) - if err != nil { - return err - } - defer func() { _ = stream.Close() }() - return consumeAuthorizationStreamWithCredential( - ctx, - stdout, - stream, - credential, - config.apiOrigin, - config.dashboardOrigin, - authPath, - ) -} - -func beginCLIAuthorization( - ctx context.Context, - config loadedCLIConfig, - previous storedAuth, - hasPrevious bool, -) (*publicapi.Client, *publicapi.CreateCLIAuthorizationResponse, error) { - client, err := newPublicClient(config.apiOrigin, previous.APIKey, hasPrevious) - if err != nil { - return nil, nil, err - } - params := publicapi.CreateCLIAuthorizationParams{ - Body: publicapi.CreateCLIAuthorization{Scopes: requestedCLIScopes()}, - } - response, err := client.CreateCLIAuthorization(ctx, params) - if hasPrevious && (isUnauthorized(err) || responseUnauthorized(response)) { - client, err = newPublicClient(config.apiOrigin, "", false) - if err != nil { - return nil, nil, err - } - response, err = client.CreateCLIAuthorization(ctx, params) - } - if err != nil { - return nil, nil, fmt.Errorf("start CLI authorization: %w", err) - } - if response == nil { - return nil, nil, fmt.Errorf("start CLI authorization against %q returned no response", config.apiOrigin) - } - if response.Status201 == nil { - return nil, nil, fmt.Errorf("start CLI authorization returned HTTP status %d", response.StatusCode) - } - return client, response, nil -} - -func openCLIAuthorizationStream( - ctx context.Context, - client *publicapi.Client, - code publicapi.CLIAuthorizationCode, -) (*publicapi.SSEStream[publicapi.CLIAuthorizationEvent], error) { - response, err := client.CliAuthorizationEvents(ctx, publicapi.CliAuthorizationEventsParams{Code: code}) - if err != nil { - return nil, fmt.Errorf("start CLI authorization event stream: %w", err) - } - if response == nil || response.Status200 == nil { - status := 0 - if response != nil { - status = response.StatusCode - } - return nil, fmt.Errorf("CLI authorization event stream returned HTTP status %d", status) - } - return response.Status200, nil -} - -func consumeAuthorizationStreamWithCredential( - ctx context.Context, - stdout io.Writer, - stream *publicapi.SSEStream[publicapi.CLIAuthorizationEvent], - credential string, - apiOrigin string, - dashboardOrigin string, - authPath string, -) error { - for { - event, ok, nextErr := stream.Next(ctx) - if nextErr != nil { - return fmt.Errorf("read CLI authorization stream: %w", nextErr) - } - if !ok { - return fmt.Errorf("CLI authorization stream from %q ended before a terminal event", apiOrigin) - } - if event.CLIAuthorizationPendingEvent == nil { - return finishAuthorization(ctx, event, credential, apiOrigin, authPath) - } - var err error - credential, err = acceptPendingAuthorization( - stdout, - credential, - event.CLIAuthorizationPendingEvent, - apiOrigin, - dashboardOrigin, - ) - if err != nil { - return err - } - } -} - -func acceptPendingAuthorization( - stdout io.Writer, - currentCredential string, - pending *publicapi.CLIAuthorizationPendingEvent, - apiOrigin string, - dashboardOrigin string, -) (string, error) { - if currentCredential != "" { - return "", fmt.Errorf("CLI authorization stream from %q emitted more than one pending event", apiOrigin) - } - if !validHeaderCredential(pending.Credential) { - return "", fmt.Errorf("CLI authorization stream from %q emitted an invalid pending credential", apiOrigin) - } - verificationURL, err := configuredVerificationURL(pending.VerificationUrl, dashboardOrigin) - if err != nil { - return "", err - } - if _, err := fmt.Fprintf(stdout, "Verification URL: %s\n", verificationURL); err != nil { - return "", fmt.Errorf("print verification URL %q: %w", verificationURL, err) - } - return pending.Credential, nil -} - -func finishAuthorization( - ctx context.Context, - event publicapi.CLIAuthorizationEvent, - credential string, - apiOrigin string, - authPath string, -) error { - switch { - case event.CLIAuthorizationApprovedEvent != nil: - if credential == "" { - return fmt.Errorf("CLI authorization stream from %q approved before its pending event", apiOrigin) - } - approved := event.CLIAuthorizationApprovedEvent - if err := verifyApprovedCredential(ctx, apiOrigin, credential, approved.TeamId); err != nil { - return err - } - return writeStoredAuth(authPath, storedAuth{APIKey: credential}) - case event.CLIAuthorizationDeniedEvent != nil: - if credential == "" { - return fmt.Errorf("CLI authorization stream from %q denied before its pending event", apiOrigin) - } - return fmt.Errorf("CLI authorization was denied by %q", apiOrigin) - case event.CLIAuthorizationExpiredEvent != nil: - if credential == "" { - return fmt.Errorf("CLI authorization stream from %q expired before its pending event", apiOrigin) - } - return fmt.Errorf("CLI authorization expired against %q", apiOrigin) - default: - return fmt.Errorf("CLI authorization stream from %q emitted an empty event variant", apiOrigin) - } -} - -func newPublicClient(apiOrigin string, credential string, authenticated bool) (*publicapi.Client, error) { - options := make([]publicapi.Option, 0, 1) - if authenticated { - options = append(options, publicapi.WithRequestEditorFn(bearerTokenRequestEditor(credential))) - } - client, err := publicapi.NewClient(publicapi.ClientOptions{BaseURL: apiOrigin}, options...) - if err != nil { - return nil, fmt.Errorf("create public API client for %q: %w", apiOrigin, err) - } - return client, nil -} - -func bearerTokenRequestEditor(token string) publicapi.RequestEditorFn { - return func(_ context.Context, request *http.Request) error { - request.Header.Set("Authorization", "Bearer "+token) - return nil - } -} - -func isUnauthorized(err error) bool { - var statusErr *publicapi.UnexpectedStatusError - return errors.As(err, &statusErr) && statusErr.StatusCode == http.StatusUnauthorized -} - -func responseUnauthorized(response *publicapi.CreateCLIAuthorizationResponse) bool { - return response != nil && response.StatusCode == http.StatusUnauthorized -} - -func validateVerificationURL(value string) error { - parsed, err := url.Parse(value) - if err != nil { - return fmt.Errorf("parse verification URL %q: %w", value, err) - } - if (parsed.Scheme != httpScheme && parsed.Scheme != httpsScheme) || parsed.Host == "" { - return fmt.Errorf("verification URL %q must be an absolute HTTP(S) URL", value) - } - if parsed.User != nil || parsed.Fragment != "" { - return fmt.Errorf("verification URL %q contains forbidden URL components", value) - } - return nil -} - -func configuredVerificationURL(value string, dashboardOrigin string) (string, error) { - if err := validateVerificationURL(value); err != nil { - return "", err - } - verificationURL, err := url.Parse(value) - if err != nil { - return "", fmt.Errorf("parse verification URL %q after validation: %w", value, err) - } - dashboardURL, err := url.Parse(dashboardOrigin) - if err != nil { - return "", fmt.Errorf("parse configured Dashboard origin %q: %w", dashboardOrigin, err) - } - verificationURL.Scheme = dashboardURL.Scheme - verificationURL.Host = dashboardURL.Host - return verificationURL.String(), nil -} - -func verifyApprovedCredential( - ctx context.Context, - apiOrigin string, - credential string, - approvedTeamID publicapi.TeamID, -) error { - client, err := newPublicClient(apiOrigin, credential, true) - if err != nil { - return err - } - response, err := client.Whoami(ctx) - if err != nil { - return fmt.Errorf("verify approved credential: %w", err) - } - if response.Status200 == nil { - return fmt.Errorf("verify approved credential returned HTTP status %d", response.StatusCode) - } - whoami := response.Status200 - if whoami.TeamId == "" || whoami.TeamId != approvedTeamID { - return fmt.Errorf( - "verify approved credential team %q does not match approved team %q", - whoami.TeamId, - approvedTeamID, - ) - } - for _, scope := range requestedCLIScopes() { - if !slices.Contains(whoami.Scopes, scope) { - return fmt.Errorf("verify approved credential is missing scope %q", scope) - } - } - return nil -} - -func requestedCLIScopes() []publicapi.ApiKeyScope { - return []publicapi.ApiKeyScope{ - publicapi.ApiKeyScopeTeamRead, - publicapi.ApiKeyScopeTeamManage, - publicapi.ApiKeyScopeShowRead, - publicapi.ApiKeyScopeShowCreate, - publicapi.ApiKeyScopeShowUpdate, - publicapi.ApiKeyScopeEpisodeCreate, - publicapi.ApiKeyScopeEpisodeDelete, - publicapi.ApiKeyScopeEpisodeUpdate, - publicapi.ApiKeyScopeEpisodePublish, - } -} diff --git a/internal/cli/login_test.go b/internal/cli/login_test.go deleted file mode 100644 index 92a73aa..0000000 --- a/internal/cli/login_test.go +++ /dev/null @@ -1,338 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" - - "gotest.tools/v3/assert" -) - -const ( - configFlagArgument = "--" + configFlagName - testEventTypeField = "type" - testTeamIDField = "team_id" -) - -//nolint:funlen // This test intentionally verifies the complete login transaction. -func TestLoginUsesConfiguredDashboardOriginAndAtomicallyReplacesAuth(t *testing.T) { - t.Parallel() - - const ( - previousCredential = "apk_previous.AAAAAAAAAAAAAAAAAAAAAA" - pendingCredential = "apk_pending.BBBBBBBBBBBBBBBBBBBBBB" - teamID = "team_cli_test" - ) - requestedAuthorization := make(chan string, 1) - requestedScopes := make(chan []string, 1) - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - switch { - case request.Method == http.MethodPost && request.URL.Path == "/cli/authorizations": - requestedAuthorization <- request.Header.Get("Authorization") - var body struct { - Scopes []string `json:"scopes"` - } - if err := json.NewDecoder(request.Body).Decode(&body); err != nil { - http.Error(writer, "invalid request", http.StatusBadRequest) - return - } - requestedScopes <- body.Scopes - writeTestAuthorizationCreation(writer, request, "cla_test", pendingCredential) - case request.Method == http.MethodGet && strings.HasPrefix(request.URL.Path, "/cli/authorizations/"): - writer.Header().Set("Content-Type", "text/event-stream") - writeTestSSEEvent(writer, approvedTestEvent(teamID)) - case request.URL.Path == "/s/whoami": - if request.Header.Get("Authorization") != "Bearer "+pendingCredential { - http.Error(writer, "wrong credential", http.StatusUnauthorized) - return - } - writer.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(writer).Encode(map[string]any{ - "id": "apk_pending", - testTeamIDField: teamID, - "scopes": requestedScopeStrings(), - }) - default: - http.NotFound(writer, request) - } - })) - defer server.Close() - dashboardServer := httptest.NewServer(http.NotFoundHandler()) - defer dashboardServer.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL, dashboardServer.URL) - authPath := defaultAuthPath(home) - assert.NilError(t, writeStoredAuth(authPath, storedAuth{APIKey: previousCredential})) - - var stdout bytes.Buffer - var stderr bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, loginCommandName}, - &stdout, - &stderr, - home, - ) - assert.NilError(t, err) - assert.Equal(t, stderr.String(), "") - assert.Equal( - t, - stdout.String(), - "Verification URL: "+dashboardServer.URL+"/cli/authorize?code=cla_test\n", - ) - assert.Equal(t, <-requestedAuthorization, "Bearer "+previousCredential) - assert.DeepEqual(t, <-requestedScopes, requestedScopeStrings()) - - raw, err := os.ReadFile(authPath) - assert.NilError(t, err) - var auth storedAuth - assert.NilError(t, json.Unmarshal(raw, &auth)) - assert.DeepEqual(t, auth, storedAuth{APIKey: pendingCredential}) - var fields map[string]any - assert.NilError(t, json.Unmarshal(raw, &fields)) - _, hasAPIOrigin := fields["api_origin"] - assert.Assert(t, !hasAPIOrigin) - authInfo, err := os.Stat(authPath) - assert.NilError(t, err) - assert.Equal(t, authInfo.Mode().Perm(), os.FileMode(0o600)) - directoryInfo, err := os.Stat(filepath.Dir(authPath)) - assert.NilError(t, err) - assert.Equal(t, directoryInfo.Mode().Perm(), os.FileMode(0o700)) -} - -func TestLoginDenialPreservesPreviousAuth(t *testing.T) { - t.Parallel() - - const previousCredential = "apk_previous.AAAAAAAAAAAAAAAAAAAAAA" - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodPost { - writeTestAuthorizationCreation(writer, request, "cla_denied", "apk_pending.BBBBBBBBBBBBBBBBBBBBBB") - return - } - writer.Header().Set("Content-Type", "text/event-stream") - writeTestSSEEvent(writer, deniedTestEvent()) - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - authPath := defaultAuthPath(home) - previous := storedAuth{APIKey: previousCredential} - assert.NilError(t, writeStoredAuth(authPath, previous)) - - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{loginCommandName, configFlagArgument, configPath}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.ErrorContains(t, err, "authorization was denied") - assert.Equal( - t, - stdout.String(), - "Verification URL: "+server.URL+"/cli/authorize?code=cla_denied\n", - ) - auth, ok := readUsableStoredAuth(authPath) - assert.Assert(t, ok) - assert.DeepEqual(t, auth, previous) -} - -func TestLoginDoesNotWriteAuthWithoutApproval(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - terminal map[string]string - want string - }{ - { - name: "expired", - terminal: map[string]string{testEventTypeField: "cli.authorization.expired"}, - want: "authorization expired", - }, - { - name: "stream interruption", - want: "ended before a terminal event", - }, - } - for _, testCase := range tests { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodPost { - writeTestAuthorizationCreation(writer, request, "cla_terminal", "apk_pending.BBBBBBBBBBBBBBBBBBBBBB") - return - } - writer.Header().Set("Content-Type", "text/event-stream") - if testCase.terminal != nil { - writeTestSSEEvent(writer, testCase.terminal) - } - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, loginCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - home, - ) - assert.ErrorContains(t, err, testCase.want) - _, statErr := os.Stat(defaultAuthPath(home)) - assert.Assert(t, os.IsNotExist(statErr)) - }) - } -} - -func TestLoginRetriesAnInvalidPreviousCredentialAnonymously(t *testing.T) { - t.Parallel() - - //nolint:gosec // Synthetic credential used only by the local test server. - const previousCredential = "apk_stale.AAAAAAAAAAAAAAAAAAAAAA" - requestedAuthorization := make(chan string, 2) - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodPost { - authorization := request.Header.Get("Authorization") - requestedAuthorization <- authorization - if authorization != "" { - writer.WriteHeader(http.StatusUnauthorized) - return - } - writeTestAuthorizationCreation(writer, request, "cla_retry", "apk_pending.BBBBBBBBBBBBBBBBBBBBBB") - return - } - writer.Header().Set("Content-Type", "text/event-stream") - writeTestSSEEvent(writer, deniedTestEvent()) - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - assert.NilError(t, writeStoredAuth( - defaultAuthPath(home), - storedAuth{APIKey: previousCredential}, - )) - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, loginCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - home, - ) - assert.ErrorContains(t, err, "authorization was denied") - assert.Equal(t, <-requestedAuthorization, "Bearer "+previousCredential) - assert.Equal(t, <-requestedAuthorization, "") -} - -func TestLoginDoesNotWriteAuthWhenFinalWhoamiRejectsCredential(t *testing.T) { - t.Parallel() - - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - switch { - case request.Method == http.MethodPost && request.URL.Path == "/cli/authorizations": - writeTestAuthorizationCreation(writer, request, "cla_rejected", "apk_rejected.BBBBBBBBBBBBBBBBBBBBBB") - case request.Method == http.MethodGet && strings.HasPrefix(request.URL.Path, "/cli/authorizations/"): - writer.Header().Set("Content-Type", "text/event-stream") - writeTestSSEEvent(writer, approvedTestEvent("team_rejected")) - case request.URL.Path == "/s/whoami": - writer.WriteHeader(http.StatusUnauthorized) - default: - http.NotFound(writer, request) - } - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL) - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, loginCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - home, - ) - assert.ErrorContains(t, err, "verify approved credential returned HTTP status 401") - _, statErr := os.Stat(defaultAuthPath(home)) - assert.Assert(t, os.IsNotExist(statErr)) -} - -func writeTestCLIConfig( - t *testing.T, - directory string, - apiOrigin string, - dashboardOrigins ...string, -) string { - t.Helper() - dashboardOrigin := apiOrigin - if len(dashboardOrigins) == 1 { - dashboardOrigin = dashboardOrigins[0] - } - path := filepath.Join(directory, "cli.yaml") - assert.NilError(t, os.WriteFile(path, []byte( - "api_origin: "+apiOrigin+"\n"+ - "dashboard_origin: "+dashboardOrigin+"\n"+ - "print_trace_ids: false\n", - ), 0o600)) - return path -} - -func serverURL(request *http.Request) string { - return httpScheme + "://" + request.Host -} - -func writeTestAuthorizationCreation(writer http.ResponseWriter, request *http.Request, code string, credential string) { - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusCreated) - if err := json.NewEncoder(writer).Encode(map[string]string{ - "code": code, - "verification_url": serverURL(request) + "/cli/authorize?code=" + code, - "credential": credential, - }); err != nil { - panic(err) - } -} - -func approvedTestEvent(teamID string) map[string]string { - return map[string]string{ - testEventTypeField: "cli.authorization.approved", - testTeamIDField: teamID, - } -} - -func deniedTestEvent() map[string]string { - return map[string]string{testEventTypeField: "cli.authorization.denied"} -} - -func writeTestSSEEvent(writer io.Writer, event any) { - _, _ = io.WriteString(writer, "data: ") - if err := json.NewEncoder(writer).Encode(event); err != nil { - return - } - _, _ = io.WriteString(writer, "\n") -} - -func requestedScopeStrings() []string { - return []string{ - "team:read", - "team:manage", - "show:read", - "show:create", - "show:update", - "episode:create", - "episode:delete", - "episode:update", - "episode:publish", - } -} diff --git a/internal/cli/main_test.go b/internal/cli/main_test.go deleted file mode 100644 index d4bc9b4..0000000 --- a/internal/cli/main_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "testing" - - "go.uber.org/goleak" - "gotest.tools/v3/assert" -) - -func TestWriteCLIErrorPrintsOnlyValidationMessages(t *testing.T) { - t.Parallel() - - err := runCLIWithHome( - context.Background(), - []string{showsCommandName, createCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, "--type is required") - - var stderr bytes.Buffer - writeCLIError(&stderr, err) - - assert.Equal(t, stderr.String(), "shows create: invalid arguments\n"+ - " --title is required\n"+ - " --slug is required\n"+ - " --type is required\n"+ - " --language is required\n") -} - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/internal/cli/media.go b/internal/cli/media.go deleted file mode 100644 index dd24124..0000000 --- a/internal/cli/media.go +++ /dev/null @@ -1,279 +0,0 @@ -package cli - -import ( - "context" - "errors" - "fmt" - "path/filepath" - "time" - - "github.com/asticode/go-astiav" -) - -const ( - mediaFastStart = "+faststart" - mediaMovFlags = "movflags" -) - -// A mediaInput owns its demuxer and a single lookahead packet. Only downloaded -// local files enter FFmpeg; HTTP and cancellation remain owned by the importer. -type mediaInput struct { - format *astiav.FormatContext - stream *astiav.Stream - packet *astiav.Packet - ready bool -} - -func openMediaInput(path string, kind astiav.MediaType) (*mediaInput, error) { - input := &mediaInput{format: astiav.AllocFormatContext(), packet: astiav.AllocPacket()} - if input.format == nil || input.packet == nil { - input.close() - return nil, errors.New("allocate native media input") - } - if err := input.format.OpenInput(path, nil, nil); err != nil { - input.close() - return nil, fmt.Errorf("open media %q: %w", path, err) - } - if err := input.format.FindStreamInfo(nil); err != nil { - input.close() - return nil, fmt.Errorf("inspect media %q: %w", path, err) - } - for _, stream := range input.format.Streams() { - if stream.CodecParameters().MediaType() == kind { - input.stream = stream - return input, nil - } - } - input.close() - return nil, fmt.Errorf("media %q has no %s stream", path, kind) -} - -func (input *mediaInput) close() { - if input.packet != nil { - input.packet.Free() - } - if input.format != nil { - input.format.CloseInput() - input.format.Free() - } -} - -func (input *mediaInput) next(ctx context.Context) error { - input.ready = false - input.packet.Unref() - for { - if err := ctx.Err(); err != nil { - return err - } - if err := input.format.ReadFrame(input.packet); err != nil { - if errors.Is(err, astiav.ErrEof) { - return nil - } - return fmt.Errorf("read media packet: %w", err) - } - if input.packet.StreamIndex() == input.stream.Index() { - input.ready = true - return nil - } - input.packet.Unref() - } -} - -func (input *mediaInput) timestamp() int64 { - timestamp := input.packet.Dts() - if timestamp == astiav.NoPtsValue { - timestamp = input.packet.Pts() - } - if timestamp == astiav.NoPtsValue { - timestamp = 0 - } - return astiav.RescaleQ(timestamp, input.stream.TimeBase(), astiav.NewRational(1, int(time.Second/time.Microsecond))) -} - -type mediaOutput struct { - format *astiav.FormatContext - io *astiav.IOContext -} - -func openMediaOutput(path string) (*mediaOutput, error) { - format, err := astiav.AllocOutputFormatContext(nil, "", path) - if err != nil { - return nil, fmt.Errorf("allocate output %q: %w", path, err) - } - output := &mediaOutput{format: format} - if !format.OutputFormat().Flags().Has(astiav.IOFormatFlagNofile) { - output.io, err = astiav.OpenIOContext(path, astiav.NewIOContextFlags(astiav.IOContextFlagWrite), nil, nil) - if err != nil { - format.Free() - return nil, fmt.Errorf("open output %q: %w", path, err) - } - format.SetPb(output.io) - } - return output, nil -} - -func (output *mediaOutput) close() error { - var err error - if output.io != nil { - err = output.io.Close() - } - output.format.Free() - return err -} - -func (output *mediaOutput) copyStream(input *mediaInput) (*astiav.Stream, error) { - stream := output.format.NewStream(nil) - if stream == nil { - return nil, errors.New("allocate output stream") - } - if err := input.stream.CodecParameters().Copy(stream.CodecParameters()); err != nil { - return nil, err - } - stream.CodecParameters().SetCodecTag(0) - stream.SetTimeBase(input.stream.TimeBase()) - return stream, nil -} - -func (output *mediaOutput) header(options map[string]string) error { - dictionary := astiav.NewDictionary() - defer dictionary.Free() - for key, value := range options { - if err := dictionary.Set(key, value, 0); err != nil { - return err - } - } - if err := output.format.WriteHeader(dictionary); err != nil { - return fmt.Errorf("write media header: %w", err) - } - return nil -} - -func (output *mediaOutput) write(input *mediaInput, stream *astiav.Stream) error { - input.packet.RescaleTs(input.stream.TimeBase(), stream.TimeBase()) - input.packet.SetStreamIndex(stream.Index()) - input.packet.SetPos(-1) - if err := output.format.WriteInterleavedFrame(input.packet); err != nil { - return fmt.Errorf("write media packet: %w", err) - } - return nil -} - -func muxMedia(ctx context.Context, videoPath, audioPath, destination string) (result error) { - video, err := openMediaInput(videoPath, astiav.MediaTypeVideo) - if err != nil { - return err - } - defer video.close() - audio, err := openMediaInput(audioPath, astiav.MediaTypeAudio) - if err != nil { - return err - } - defer audio.close() - output, err := openMediaOutput(destination) - if err != nil { - return err - } - defer func() { result = errors.Join(result, output.close()) }() - videoStream, err := output.copyStream(video) - if err != nil { - return err - } - audioStream, err := output.copyStream(audio) - if err != nil { - return err - } - if err := output.header(map[string]string{mediaMovFlags: mediaFastStart}); err != nil { - return err - } - if err := video.next(ctx); err != nil { - return err - } - if err := audio.next(ctx); err != nil { - return err - } - if !video.ready || !audio.ready { - return errors.New("downloaded media stream is empty") - } - return interleaveMedia(ctx, output, video, audio, videoStream, audioStream) -} - -func interleaveMedia( - ctx context.Context, output *mediaOutput, video, audio *mediaInput, - videoStream, audioStream *astiav.Stream, -) error { - // Reading both inputs in timestamp order keeps muxer buffering bounded even - // for long recordings. Never enqueue the entire video before the audio. - for video.ready || audio.ready { - input, stream := audio, audioStream - if video.ready && (!audio.ready || video.timestamp() <= audio.timestamp()) { - input, stream = video, videoStream - } - if err := output.write(input, stream); err != nil { - return err - } - if err := input.next(ctx); err != nil { - return err - } - } - return output.format.WriteTrailer() -} - -func remuxMedia( - ctx context.Context, source, destination string, kind astiav.MediaType, options map[string]string, -) (result error) { - input, err := openMediaInput(source, kind) - if err != nil { - return err - } - defer input.close() - output, err := openMediaOutput(destination) - if err != nil { - return err - } - defer func() { result = errors.Join(result, output.close()) }() - stream, err := output.copyStream(input) - if err != nil { - return err - } - if err := output.header(options); err != nil { - return err - } - count := 0 - for { - if err := input.next(ctx); err != nil { - return err - } - if !input.ready { - break - } - if err := output.write(input, stream); err != nil { - return err - } - count++ - } - if count == 0 { - return errors.New("downloaded media stream is empty") - } - return output.format.WriteTrailer() -} - -func mediaDimensions(source string) (int, int, error) { - input, err := openMediaInput(source, astiav.MediaTypeVideo) - if err != nil { - return 0, 0, err - } - defer input.close() - parameters := input.stream.CodecParameters() - width, height := parameters.Width(), parameters.Height() - if width <= 0 || height <= 0 { - return 0, 0, fmt.Errorf("invalid video dimensions %dx%d", width, height) - } - return width, height, nil -} - -func packageMediaTrack(ctx context.Context, source, root string, kind astiav.MediaType) error { - return remuxMedia(ctx, source, filepath.Join(root, "index.m3u8"), kind, map[string]string{ - "hls_time": "6", "hls_playlist_type": "vod", "hls_segment_type": "fmp4", - "hls_fmp4_init_filename": "init.mp4", "hls_segment_filename": filepath.Join(root, "segment-%05d.m4s"), - }) -} diff --git a/internal/cli/media_audio.go b/internal/cli/media_audio.go deleted file mode 100644 index 6028f36..0000000 --- a/internal/cli/media_audio.go +++ /dev/null @@ -1,291 +0,0 @@ -package cli - -import ( - "context" - "errors" - "fmt" - - "github.com/asticode/go-astiav" -) - -const ( - mediaAudioRate = 48000 - mediaStereoBitrate = 128000 -) - -func prepareMediaAudio(ctx context.Context, source, destination string) error { - if err := ctx.Err(); err != nil { - return err - } - input, err := openMediaInput(source, astiav.MediaTypeAudio) - if err != nil { - return err - } - defer input.close() - if input.stream.CodecParameters().CodecID() == astiav.CodecIDAac { - return remuxMedia(ctx, source, destination, astiav.MediaTypeAudio, map[string]string{mediaMovFlags: mediaFastStart}) - } - return transcodeMediaAudio(ctx, input, destination) -} - -type mediaAudioTranscoder struct { - decoder, encoder *astiav.CodecContext - resampler *astiav.SoftwareResampleContext - fifo *astiav.AudioFifo - output *mediaOutput - stream *astiav.Stream - timestamp int64 -} - -func (transcoder *mediaAudioTranscoder) close() { - if transcoder.decoder != nil { - transcoder.decoder.Free() - } - if transcoder.encoder != nil { - transcoder.encoder.Free() - } - if transcoder.resampler != nil { - transcoder.resampler.Free() - } - if transcoder.fifo != nil { - transcoder.fifo.Free() - } -} - -func (transcoder *mediaAudioTranscoder) openCodecs(input *mediaInput) error { - decoder := astiav.FindDecoder(input.stream.CodecParameters().CodecID()) - encoder := astiav.FindEncoder(astiav.CodecIDAac) - if decoder == nil || encoder == nil { - return errors.New("bundled audio codec is unavailable") - } - transcoder.decoder = astiav.AllocCodecContext(decoder) - transcoder.encoder = astiav.AllocCodecContext(encoder) - if transcoder.decoder == nil || transcoder.encoder == nil { - return errors.New("allocate audio codec") - } - if err := input.stream.CodecParameters().ToCodecContext(transcoder.decoder); err != nil { - return err - } - if err := transcoder.decoder.Open(decoder, nil); err != nil { - return err - } - if transcoder.decoder.SampleRate() <= 0 || transcoder.decoder.ChannelLayout().Channels() <= 0 { - return errors.New("invalid input audio rate or channel layout") - } - layout := astiav.ChannelLayoutStereo - bitrate := int64(mediaStereoBitrate) - if transcoder.decoder.ChannelLayout().Channels() == 1 { - layout = astiav.ChannelLayoutMono - bitrate = 64000 - } - transcoder.encoder.SetChannelLayout(layout) - transcoder.encoder.SetSampleRate(mediaAudioRate) - transcoder.encoder.SetSampleFormat(astiav.SampleFormatFltp) - transcoder.encoder.SetBitRate(bitrate) - transcoder.encoder.SetTimeBase(astiav.NewRational(1, mediaAudioRate)) - transcoder.encoder.SetFlags(astiav.NewCodecContextFlags(astiav.CodecContextFlagGlobalHeader)) - return transcoder.encoder.Open(encoder, nil) -} - -func (transcoder *mediaAudioTranscoder) openOutput() error { - transcoder.stream = transcoder.output.format.NewStream(nil) - if transcoder.stream == nil { - return errors.New("allocate AAC output stream") - } - transcoder.stream.SetTimeBase(transcoder.encoder.TimeBase()) - if err := transcoder.encoder.ToCodecParameters(transcoder.stream.CodecParameters()); err != nil { - return err - } - if err := transcoder.output.header(map[string]string{mediaMovFlags: mediaFastStart}); err != nil { - return err - } - transcoder.resampler = astiav.AllocSoftwareResampleContext() - transcoder.fifo = astiav.AllocAudioFifo( - transcoder.encoder.SampleFormat(), transcoder.encoder.ChannelLayout().Channels(), 1, - ) - if transcoder.resampler == nil || transcoder.fifo == nil { - return errors.New("allocate audio resampler") - } - return nil -} - -func transcodeMediaAudio(ctx context.Context, input *mediaInput, destination string) (result error) { - transcoder := &mediaAudioTranscoder{} - defer transcoder.close() - if err := transcoder.openCodecs(input); err != nil { - return fmt.Errorf("open audio codecs: %w", err) - } - output, err := openMediaOutput(destination) - if err != nil { - return err - } - transcoder.output = output - defer func() { result = errors.Join(result, output.close()) }() - if err := transcoder.openOutput(); err != nil { - return err - } - return transcoder.run(ctx, input) -} - -func (transcoder *mediaAudioTranscoder) run(ctx context.Context, input *mediaInput) error { - for { - if err := input.next(ctx); err != nil { - return err - } - if !input.ready { - break - } - if err := transcoder.decode(ctx, input.packet); err != nil { - return err - } - } - if err := transcoder.decode(ctx, nil); err != nil { - return err - } - return transcoder.finish(ctx) -} - -func (transcoder *mediaAudioTranscoder) finish(ctx context.Context) error { - for { - if err := ctx.Err(); err != nil { - return err - } - samples, err := transcoder.resample(nil) - if err != nil { - return err - } - if samples == 0 { - break - } - if err := transcoder.encodeReady(ctx, false); err != nil { - return err - } - } - if err := transcoder.encodeReady(ctx, true); err != nil { - return err - } - if transcoder.timestamp == 0 { - return errors.New("downloaded audio stream is empty") - } - if err := transcoder.encoder.SendFrame(nil); err != nil { - return err - } - if err := transcoder.drainEncoder(); err != nil { - return err - } - return transcoder.output.format.WriteTrailer() -} - -func mediaCodecWaiting(err error) bool { - return errors.Is(err, astiav.ErrEagain) || errors.Is(err, astiav.ErrEof) -} - -func (transcoder *mediaAudioTranscoder) decode(ctx context.Context, packet *astiav.Packet) error { - if err := transcoder.decoder.SendPacket(packet); err != nil { - return err - } - frame := astiav.AllocFrame() - if frame == nil { - return errors.New("allocate decoded audio frame") - } - defer frame.Free() - for { - if err := ctx.Err(); err != nil { - return err - } - if err := transcoder.decoder.ReceiveFrame(frame); err != nil { - if mediaCodecWaiting(err) { - return nil - } - return fmt.Errorf("decode audio frame: %w", err) - } - if _, err := transcoder.resample(frame); err != nil { - return err - } - frame.Unref() - if err := transcoder.encodeReady(ctx, false); err != nil { - return err - } - } -} - -func (transcoder *mediaAudioTranscoder) resample(input *astiav.Frame) (int, error) { - frame := astiav.AllocFrame() - if frame == nil { - return 0, errors.New("allocate resampled audio frame") - } - defer frame.Free() - frame.SetChannelLayout(transcoder.encoder.ChannelLayout()) - frame.SetSampleFormat(transcoder.encoder.SampleFormat()) - frame.SetSampleRate(mediaAudioRate) - // Let swresample allocate enough space for the input and retained delay. - if err := transcoder.resampler.ConvertFrame(input, frame); err != nil { - return 0, fmt.Errorf("resample audio: %w", err) - } - if frame.NbSamples() > 0 { - if _, err := transcoder.fifo.Write(frame); err != nil { - return 0, err - } - } - return frame.NbSamples(), nil -} - -func (transcoder *mediaAudioTranscoder) encodeReady(ctx context.Context, finish bool) error { - size := transcoder.encoder.FrameSize() - for transcoder.fifo.Size() >= size || (finish && transcoder.fifo.Size() > 0) { - if err := ctx.Err(); err != nil { - return err - } - if err := transcoder.encodeFrame(min(size, transcoder.fifo.Size())); err != nil { - return err - } - } - return nil -} - -func (transcoder *mediaAudioTranscoder) encodeFrame(samples int) error { - frame := astiav.AllocFrame() - if frame == nil { - return errors.New("allocate encoder audio frame") - } - defer frame.Free() - frame.SetChannelLayout(transcoder.encoder.ChannelLayout()) - frame.SetSampleFormat(transcoder.encoder.SampleFormat()) - frame.SetSampleRate(mediaAudioRate) - frame.SetNbSamples(samples) - if err := frame.AllocBuffer(0); err != nil { - return err - } - if _, err := transcoder.fifo.Read(frame); err != nil { - return err - } - frame.SetPts(transcoder.timestamp) - transcoder.timestamp += int64(samples) - if err := transcoder.encoder.SendFrame(frame); err != nil { - return err - } - return transcoder.drainEncoder() -} - -func (transcoder *mediaAudioTranscoder) drainEncoder() error { - packet := astiav.AllocPacket() - if packet == nil { - return errors.New("allocate encoded audio packet") - } - defer packet.Free() - for { - if err := transcoder.encoder.ReceivePacket(packet); err != nil { - if mediaCodecWaiting(err) { - return nil - } - return fmt.Errorf("encode AAC packet: %w", err) - } - packet.RescaleTs(transcoder.encoder.TimeBase(), transcoder.stream.TimeBase()) - packet.SetStreamIndex(transcoder.stream.Index()) - packet.SetPos(-1) - if err := transcoder.output.format.WriteInterleavedFrame(packet); err != nil { - return err - } - packet.Unref() - } -} diff --git a/internal/cli/media_test.go b/internal/cli/media_test.go deleted file mode 100644 index 767fd66..0000000 --- a/internal/cli/media_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/asticode/go-astiav" - "gotest.tools/v3/assert" -) - -func TestNativeMediaProducesAVCAACAndHLS(t *testing.T) { - t.Parallel() - for _, audio := range []string{"aac.m4a", "opus.webm"} { - t.Run(audio, func(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) - defer cancel() - directory := t.TempDir() - prepared := filepath.Join(directory, "audio.m4a") - assert.NilError(t, prepareMediaAudio(ctx, filepath.Join("testdata", "media", audio), prepared)) - destination := filepath.Join(directory, "video.mp4") - assert.NilError(t, muxMedia(ctx, filepath.Join("testdata", "media", "video.mp4"), prepared, destination)) - duration, err := packageYouTubeVideo(ctx, directory) - assert.NilError(t, err) - assert.Assert(t, duration >= 1 && duration <= 4, "unexpected package duration %d", duration) - assertNativePackage(t, directory) - }) - } -} - -func assertNativePackage(t *testing.T, directory string) { - t.Helper() - destination := filepath.Join(directory, "video.mp4") - audio, err := openMediaInput(destination, astiav.MediaTypeAudio) - assert.NilError(t, err) - defer audio.close() - assert.Equal(t, audio.stream.CodecParameters().CodecID(), astiav.CodecIDAac) - video, err := openMediaInput(destination, astiav.MediaTypeVideo) - assert.NilError(t, err) - defer video.close() - assert.Equal(t, video.stream.CodecParameters().CodecID(), astiav.CodecIDH264) - assert.Equal(t, video.stream.CodecParameters().Width(), 1280) - assert.Equal(t, video.stream.CodecParameters().Height(), 720) - data, err := os.ReadFile(destination) - assert.NilError(t, err) - assert.Assert(t, len(data) > 100_000 && len(data) < 2_000_000) - moov, mdat := bytes.Index(data, []byte("moov")), bytes.Index(data, []byte("mdat")) - assert.Assert(t, moov > 0 && mdat > moov, "MP4 must be fast-start") - for _, kind := range []string{youtubeVideoKind, youtubeAudioKind} { - root := filepath.Join(directory, "hls", kind) - playlist, err := os.ReadFile(filepath.Join(root, "index.m3u8")) - assert.NilError(t, err) - assert.Assert(t, strings.Contains(string(playlist), "#EXT-X-ENDLIST")) - for _, name := range []string{"init.mp4", "segment-00000.m4s"} { - info, err := os.Stat(filepath.Join(root, name)) - assert.NilError(t, err) - assert.Assert(t, info.Size() > 100 && info.Size() < 2_000_000) - } - } -} - -func TestNativeMediaCancellationAndInvalidInput(t *testing.T) { - t.Parallel() - ctx, cancel := context.WithCancel(t.Context()) - cancel() - output := filepath.Join(t.TempDir(), "audio.m4a") - assert.ErrorIs(t, prepareMediaAudio(ctx, "testdata/media/opus.webm", output), context.Canceled) - _, err := os.Stat(output) - assert.Assert(t, os.IsNotExist(err), "cancelled preparation must not create output") - invalid := filepath.Join(t.TempDir(), "invalid.mp4") - assert.NilError(t, os.WriteFile(invalid, []byte("not media"), 0o600)) - assert.ErrorContains(t, prepareMediaAudio(t.Context(), invalid, output), "open media") -} diff --git a/internal/cli/members.go b/internal/cli/members.go deleted file mode 100644 index 298d422..0000000 --- a/internal/cli/members.go +++ /dev/null @@ -1,576 +0,0 @@ -package cli - -import ( - "context" - "errors" - "flag" - "fmt" - "io" - "net/http" - "strings" - "time" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -const ( - emailFlagName = "email" - memberFlagName = "member" - roleFlagName = "role" -) - -func runMembersCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - if len(args) == 0 { - writeMembersUsage(stderr) - return errors.New("members command is required") - } - switch args[0] { - case helpCommandName, shortHelpFlag, longHelpFlag: - return runMembersHelp(args, stdout) - case listCommandName: - return runMembersList(ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig) - case inviteCommandName: - return runMembersInvite(ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig) - case roleCommandName: - return runMembersRole(ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig) - case removeCommandName: - return runMembersRemove(ctx, args[1:], stdout, stderr, home, configPath, configExplicit, defaultConfig) - default: - writeMembersUsage(stderr) - return fmt.Errorf("unknown members command %q", args[0]) - } -} - -func runMembersHelp(args []string, stdout io.Writer) error { - if len(args) == 1 { - writeMembersUsage(stdout) - return nil - } - if len(args) != nestedCommandArgs || !writeNestedCommandUsage(membersCommandName, args[1], stdout) { - return fmt.Errorf("members help received unknown command %q", args[1]) - } - return nil -} - -func runMembersList( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox members list", flag.ContinueOnError) - flags.SetOutput(stderr) - showSlug := "" - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&showSlug, showFlagName, "", "show slug; omit to list team members") - flags.Usage = func() { writeMembersListUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse members list flags: %w", err) - } - if flags.NArg() != 0 { - return fmt.Errorf("members list received unexpected argument %q", flags.Arg(0)) - } - client, config, err := teamManagementClient( - home, - configPath, - configExplicit || flagWasSet(flags, configFlagName), - defaultConfig, - ) - if err != nil { - return err - } - showSlug = strings.TrimSpace(showSlug) - if showSlug != "" { - return listShowMembers(ctx, client, config.apiOrigin, showSlug, stdout) - } - return listTeamMembers(ctx, client, config.apiOrigin, stdout) -} - -func listShowMembers( - ctx context.Context, - client *publicapi.Client, - apiOrigin string, - showSlug string, - stdout io.Writer, -) error { - response, err := client.ListShowMembers(ctx, publicapi.ListShowMembersParams{ShowSlug: showSlug}) - if err != nil { - return fmt.Errorf("list show members: %w", err) - } - if response == nil { - return fmt.Errorf("list show members against %q returned no response", apiOrigin) - } - if response.Status200 == nil { - return teamManagementResponseError( - "list show members", response.StatusCode, response.Status400, - ) - } - if err := writeShowMembers(stdout, response.Status200.Members); err != nil { - return err - } - return writePendingShowInvitations(stdout, response.Status200.Invitations) -} - -func listTeamMembers( - ctx context.Context, - client *publicapi.Client, - apiOrigin string, - stdout io.Writer, -) error { - response, err := client.ListTeamMembers(ctx) - if err != nil { - return fmt.Errorf("list members: %w", err) - } - if response == nil { - return fmt.Errorf("list members against %q returned no response", apiOrigin) - } - if response.Status200 == nil { - return teamManagementResponseError( - "list members", response.StatusCode, response.Status400, - ) - } - if err := writeTeamMembers(stdout, response.Status200.Members); err != nil { - return err - } - return writePendingTeamInvitations(stdout, response.Status200.Invitations) -} - -func writeShowMembers(stdout io.Writer, members []publicapi.ShowMember) error { - for _, member := range members { - if _, err := fmt.Fprintf( - stdout, - "%s\t%s\t%s\t%s\n", - member.AccessSource, - member.Role, - member.User.Email, - member.User.Id, - ); err != nil { - return fmt.Errorf("print show member %q: %w", member.User.Id, err) - } - } - return nil -} - -func writePendingShowInvitations(stdout io.Writer, invitations []publicapi.PendingShowInvitation) error { - for _, invitation := range invitations { - expires := time.UnixMilli(invitation.ExpiresAt).UTC().Format(time.RFC3339) - if _, err := fmt.Fprintf( - stdout, - "pending\t%s\t%s\t%s\t%s\n", - invitation.Role, - invitation.Email, - invitation.Id, - expires, - ); err != nil { - return fmt.Errorf("print show invitation %q: %w", invitation.Id, err) - } - } - return nil -} - -func writeTeamMembers(stdout io.Writer, members []publicapi.TeamMember) error { - for _, member := range members { - if _, err := fmt.Fprintf(stdout, "%s\t%s\t%s\n", member.Role, member.User.Email, member.User.Id); err != nil { - return fmt.Errorf("print member %q: %w", member.User.Id, err) - } - } - return nil -} - -func writePendingTeamInvitations(stdout io.Writer, invitations []publicapi.PendingTeamInvitation) error { - for _, invitation := range invitations { - expires := time.UnixMilli(invitation.ExpiresAt).UTC().Format(time.RFC3339) - if _, err := fmt.Fprintf( - stdout, - "pending\t%s\t%s\t%s\t%s\n", - invitation.Role, - invitation.Email, - invitation.Id, - expires, - ); err != nil { - return fmt.Errorf("print invitation %q: %w", invitation.Id, err) - } - } - return nil -} - -func runMembersInvite( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox members invite", flag.ContinueOnError) - flags.SetOutput(stderr) - email, role, showSlug := "", "", "" - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&email, emailFlagName, "", "email address to invite (required)") - flags.StringVar(&role, roleFlagName, "", "member role: read or write (required)") - flags.StringVar(&showSlug, showFlagName, "", "show slug; omit to invite a team member") - flags.Usage = func() { writeMembersInviteUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse members invite flags: %w", err) - } - if flags.NArg() != 0 { - return fmt.Errorf("members invite received unexpected argument %q", flags.Arg(0)) - } - email = strings.ToLower(strings.TrimSpace(email)) - if email == "" { - return errors.New("members invite: --email is required") - } - if err := validateMemberRole(role, "members invite"); err != nil { - return err - } - client, _, err := teamManagementClient( - home, - configPath, - configExplicit || flagWasSet(flags, configFlagName), - defaultConfig, - ) - if err != nil { - return err - } - showSlug = strings.TrimSpace(showSlug) - if showSlug != "" { - invitation, inviteErr := inviteShowMember(ctx, client, showSlug, email, role) - if inviteErr != nil { - return inviteErr - } - _, err = fmt.Fprintf(stdout, "Invited %s to %s as %s.\n", invitation.Email, showSlug, invitation.Role) - return err - } - invitation, err := inviteTeamMember(ctx, client, email, role) - if err != nil { - return err - } - _, err = fmt.Fprintf(stdout, "Invited %s as %s.\n", invitation.Email, invitation.Role) - return err -} - -func inviteShowMember( - ctx context.Context, - client *publicapi.Client, - showSlug string, - email string, - role string, -) (publicapi.PendingShowInvitation, error) { - response, err := client.CreateShowInvitation(ctx, publicapi.CreateShowInvitationParams{ - ShowSlug: showSlug, - Body: publicapi.CreateShowInvitation{ - Email: email, - Role: publicapi.AssignableTeamRole(role), - }, - }) - if err != nil { - return publicapi.PendingShowInvitation{}, fmt.Errorf("invite show member %q: %w", email, err) - } - if response == nil { - return publicapi.PendingShowInvitation{}, errors.New("invite show member returned no response") - } - if response.Status201 == nil { - return publicapi.PendingShowInvitation{}, teamManagementResponseError( - "invite show member", response.StatusCode, response.Status400, - ) - } - return *response.Status201, nil -} - -func inviteTeamMember( - ctx context.Context, - client *publicapi.Client, - email string, - role string, -) (publicapi.PendingTeamInvitation, error) { - response, err := client.CreateTeamInvitation(ctx, publicapi.CreateTeamInvitationParams{ - Body: publicapi.CreateTeamInvitation{ - Email: email, - Role: publicapi.AssignableTeamRole(role), - }, - }) - if err != nil { - return publicapi.PendingTeamInvitation{}, fmt.Errorf("invite member %q: %w", email, err) - } - if response == nil { - return publicapi.PendingTeamInvitation{}, errors.New("invite member returned no response") - } - if response.Status201 == nil { - return publicapi.PendingTeamInvitation{}, teamManagementResponseError( - "invite member", response.StatusCode, response.Status400, - ) - } - return *response.Status201, nil -} - -func runMembersRole( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox members role", flag.ContinueOnError) - flags.SetOutput(stderr) - memberID, role, showSlug := "", "", "" - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&memberID, memberFlagName, "", "member user ID (required)") - flags.StringVar(&role, roleFlagName, "", "member role: read or write (required)") - flags.StringVar(&showSlug, showFlagName, "", "show slug; omit to change a team member") - flags.Usage = func() { writeMembersRoleUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse members role flags: %w", err) - } - if flags.NArg() != 0 { - return fmt.Errorf("members role received unexpected argument %q", flags.Arg(0)) - } - if !strings.HasPrefix(memberID, "usr_") { - return errors.New("members role: --member must be a user ID") - } - if err := validateMemberRole(role, "members role"); err != nil { - return err - } - client, _, err := teamManagementClient( - home, - configPath, - configExplicit || flagWasSet(flags, configFlagName), - defaultConfig, - ) - if err != nil { - return err - } - showSlug = strings.TrimSpace(showSlug) - if showSlug != "" { - return updateShowMemberRole(ctx, client, showSlug, memberID, role, stdout) - } - return updateTeamMemberRole(ctx, client, memberID, role, stdout) -} - -func updateShowMemberRole( - ctx context.Context, - client *publicapi.Client, - showSlug string, - memberID string, - role string, - stdout io.Writer, -) error { - response, err := client.UpdateShowMemberRole(ctx, publicapi.UpdateShowMemberRoleParams{ - ShowSlug: showSlug, - UserId: memberID, - Body: publicapi.UpdateShowMemberRole{ - Role: publicapi.AssignableTeamRole(role), - }, - }) - if err != nil { - return fmt.Errorf("change show member %q role: %w", memberID, err) - } - if response == nil { - return errors.New("change show member role returned no response") - } - if response.StatusCode != http.StatusNoContent { - return teamManagementResponseError( - "change show member role", response.StatusCode, response.Status400, - ) - } - _, err = fmt.Fprintf(stdout, "Changed show member %s to %s.\n", memberID, role) - return err -} - -func updateTeamMemberRole( - ctx context.Context, - client *publicapi.Client, - memberID string, - role string, - stdout io.Writer, -) error { - response, err := client.UpdateTeamMemberRole(ctx, publicapi.UpdateTeamMemberRoleParams{ - UserId: memberID, - Body: publicapi.UpdateTeamMemberRole{ - Role: publicapi.AssignableTeamRole(role), - }, - }) - if err != nil { - return fmt.Errorf("change member %q role: %w", memberID, err) - } - if response == nil { - return errors.New("change member role returned no response") - } - if response.StatusCode != http.StatusNoContent { - return teamManagementResponseError( - "change member role", response.StatusCode, response.Status400, - ) - } - _, err = fmt.Fprintf(stdout, "Changed member %s to %s.\n", memberID, role) - return err -} - -func runMembersRemove( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox members remove", flag.ContinueOnError) - flags.SetOutput(stderr) - memberID, showSlug, confirmed := "", "", false - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&memberID, memberFlagName, "", "member user ID (required)") - flags.BoolVar(&confirmed, yesFlagName, false, "confirm removal (required)") - flags.StringVar(&showSlug, showFlagName, "", "show slug; omit to remove a team member") - flags.Usage = func() { writeMembersRemoveUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse members remove flags: %w", err) - } - if flags.NArg() != 0 { - return fmt.Errorf("members remove received unexpected argument %q", flags.Arg(0)) - } - if !strings.HasPrefix(memberID, "usr_") { - return errors.New("members remove: --member must be a user ID") - } - if !confirmed { - return errors.New("members remove: --yes is required") - } - client, _, err := teamManagementClient( - home, - configPath, - configExplicit || flagWasSet(flags, configFlagName), - defaultConfig, - ) - if err != nil { - return err - } - showSlug = strings.TrimSpace(showSlug) - if showSlug != "" { - return removeShowMember(ctx, client, showSlug, memberID, stdout) - } - return removeTeamMember(ctx, client, memberID, stdout) -} - -func removeShowMember( - ctx context.Context, - client *publicapi.Client, - showSlug string, - memberID string, - stdout io.Writer, -) error { - response, err := client.RemoveShowMember(ctx, publicapi.RemoveShowMemberParams{ - ShowSlug: showSlug, - UserId: memberID, - }) - if err != nil { - return fmt.Errorf("remove show member %q: %w", memberID, err) - } - if response == nil { - return errors.New("remove show member returned no response") - } - if response.StatusCode != http.StatusNoContent { - return teamManagementResponseError( - "remove show member", response.StatusCode, response.Status400, - ) - } - _, err = fmt.Fprintf(stdout, "Removed show member %s.\n", memberID) - return err -} - -func removeTeamMember( - ctx context.Context, - client *publicapi.Client, - memberID string, - stdout io.Writer, -) error { - response, err := client.RemoveTeamMember(ctx, publicapi.RemoveTeamMemberParams{UserId: memberID}) - if err != nil { - return fmt.Errorf("remove member %q: %w", memberID, err) - } - if response == nil { - return errors.New("remove member returned no response") - } - if response.StatusCode != http.StatusNoContent { - return teamManagementResponseError( - "remove member", response.StatusCode, response.Status400, - ) - } - _, err = fmt.Fprintf(stdout, "Removed member %s.\n", memberID) - return err -} - -func teamManagementClient( - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) (*publicapi.Client, loadedCLIConfig, error) { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return nil, loadedCLIConfig{}, err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return nil, loadedCLIConfig{}, errors.New("team members: not logged in; run listenbox login") - } - client, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return nil, loadedCLIConfig{}, err - } - return client, config, nil -} - -func validateMemberRole(role string, command string) error { - if role != "read" && role != "write" { - return fmt.Errorf("%s: --role must be read or write", command) - } - return nil -} - -func teamManagementResponseError( - action string, - status int, - validation *publicapi.ValidationErr, -) error { - switch { - case validation != nil: - return fmt.Errorf("%s: %s", action, validation.Message) - case status == http.StatusUnauthorized: - return fmt.Errorf("%s: authentication failed; run listenbox login", action) - case status == http.StatusForbidden: - return fmt.Errorf("%s: team owner permission is required", action) - case status == http.StatusConflict || status == http.StatusNotFound: - return fmt.Errorf("%s: target is unavailable", action) - default: - return fmt.Errorf("%s returned HTTP status %d", action, status) - } -} diff --git a/internal/cli/show_delete.go b/internal/cli/show_delete.go deleted file mode 100644 index 32fce2f..0000000 --- a/internal/cli/show_delete.go +++ /dev/null @@ -1,184 +0,0 @@ -//nolint:lll -package cli - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "strings" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -const ( - showDeletionProgressEventType = "progress" - showDeletionTerminalEventType = "terminal" - showDeletionCompletedStatus = "completed" - showDeletionFailedStatus = "failed" - processingStatus = "processing" - maxShowDeletionPercent = 100 -) - -func deleteShow( - ctx context.Context, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, - slug string, -) error { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("delete show: not logged in; run listenbox login") - } - client, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return err - } - response, err := client.CreateShowDeletion(ctx, publicapi.CreateShowDeletionParams{ShowSlug: slug}) - if err != nil { - return fmt.Errorf("delete show %q: %w", slug, err) - } - created, err := validateShowDeletionResponse(slug, response) - if err != nil { - return err - } - eventsResponse, err := client.ShowDeletionEvents(ctx, publicapi.ShowDeletionEventsParams{ - ShowDeletionRunId: created.ShowDeletionRunId, - }) - if err != nil { - return fmt.Errorf("read show deletion event stream for %q: %w", slug, err) - } - stream, err := validateShowDeletionEventsResponse(slug, eventsResponse) - if err != nil { - return err - } - return consumeShowDeletionStream(ctx, stdout, stderr, slug, stream) -} - -func validateShowDeletionResponse(slug string, response *publicapi.CreateShowDeletionResponse) (*publicapi.CreatedPublicShowDeletion, error) { - if response == nil { - return nil, fmt.Errorf("delete show %q returned no response", slug) - } - if response.Status202 != nil { - return response.Status202, nil - } - switch { - case response.Status400 != nil: - return nil, fmt.Errorf("delete show %q: %s", slug, response.Status400.Message) - case response.StatusCode == http.StatusUnauthorized: - return nil, fmt.Errorf("delete show %q: authentication failed; run listenbox login", slug) - case response.StatusCode == http.StatusForbidden: - return nil, fmt.Errorf("delete show %q: only the current show owner may delete it", slug) - case response.StatusCode == http.StatusNotFound: - return nil, fmt.Errorf("delete show %q: show not found", slug) - default: - return nil, fmt.Errorf("delete show %q returned HTTP status %d", slug, response.StatusCode) - } -} - -func validateShowDeletionEventsResponse(slug string, response *publicapi.ShowDeletionEventsResponse) (*publicapi.SSEStream[publicapi.ShowDeletionEvent], error) { - if response == nil || response.Status200 == nil { - status := 0 - if response != nil { - status = response.StatusCode - } - return nil, fmt.Errorf("show deletion event stream for %q returned HTTP status %d", slug, status) - } - return response.Status200, nil -} - -func consumeShowDeletionStream( - ctx context.Context, - stdout io.Writer, - stderr io.Writer, - slug string, - stream *publicapi.SSEStream[publicapi.ShowDeletionEvent], -) error { - defer func() { _ = stream.Close() }() - progress := newImportProgress(stderr, writerIsTerminal(stderr)) - defer progress.cleanFailureLine() - for { - event, ok, err := stream.Next(ctx) - if err != nil { - return fmt.Errorf("read show deletion stream for %q: %w", slug, err) - } - if !ok { - return fmt.Errorf("show deletion stream for %q ended before a terminal event", slug) - } - done, err := handleShowDeletionEvent(stdout, slug, progress, event) - if err != nil { - return err - } - if done { - return nil - } - } -} - -func handleShowDeletionEvent( - stdout io.Writer, - slug string, - progress *importProgress, - event publicapi.ShowDeletionEvent, -) (bool, error) { - if current := event.ShowDeletionProgressEvent; current != nil { - if !validShowDeletionProgressEvent(*current) { - return false, fmt.Errorf("show deletion stream for %q emitted malformed progress event", slug) - } - percent := uint32(current.Percent) //nolint:gosec // validated non-negative and bounded above - if err := progress.update(percent); err != nil { - return false, err - } - return false, nil - } - if event.ShowDeletionTerminalEvent == nil { - return false, fmt.Errorf("show deletion stream for %q emitted an empty event variant", slug) - } - return handleShowDeletionTerminalEvent(stdout, slug, progress, *event.ShowDeletionTerminalEvent) -} - -func validShowDeletionProgressEvent(event publicapi.ShowDeletionProgressEvent) bool { - if event.Type != showDeletionProgressEventType || event.Percent < 0 || event.Percent > maxShowDeletionPercent { - return false - } - if event.Status != "queued" && event.Status != processingStatus { - return false - } - return strings.TrimSpace(event.CurrentStep) != "" -} - -func handleShowDeletionTerminalEvent( - stdout io.Writer, - slug string, - progress *importProgress, - event publicapi.ShowDeletionTerminalEvent, -) (bool, error) { - if completed := event.ShowDeletionCompletedEvent; completed != nil { - if completed.Type != showDeletionTerminalEventType || - completed.Status != showDeletionCompletedStatus || completed.ShowSlug != slug { - return false, fmt.Errorf("show deletion stream for %q emitted malformed completed terminal event", slug) - } - if err := progress.complete(); err != nil { - return false, err - } - if _, err := fmt.Fprintf(stdout, "Deleted show %q\n", slug); err != nil { - return false, fmt.Errorf("print deleted show %q: %w", slug, err) - } - return true, nil - } - failed := event.ShowDeletionFailedEvent - if failed == nil || failed.Type != showDeletionTerminalEventType || failed.Status != showDeletionFailedStatus || - strings.TrimSpace(failed.ErrorCode) == "" || strings.TrimSpace(failed.ErrorMessage) == "" { - return false, fmt.Errorf("show deletion stream for %q emitted malformed failed terminal event", slug) - } - return false, fmt.Errorf("delete show %q failed (%s): %s", slug, failed.ErrorCode, failed.ErrorMessage) -} diff --git a/internal/cli/show_delete_command.go b/internal/cli/show_delete_command.go deleted file mode 100644 index 5c003b5..0000000 --- a/internal/cli/show_delete_command.go +++ /dev/null @@ -1,58 +0,0 @@ -package cli - -import ( - "context" - "errors" - "flag" - "fmt" - "io" -) - -func runShowsDeleteCommand( - ctx context.Context, - args []string, - stdout io.Writer, - stderr io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - flags := flag.NewFlagSet("listenbox shows delete", flag.ContinueOnError) - flags.SetOutput(stderr) - slug := "" - yes := false - flags.StringVar(&configPath, configFlagName, configPath, "path to CLI config file") - flags.StringVar(&slug, showFlagName, "", "global show slug (required)") - flags.BoolVar(&yes, yesFlagName, false, "confirm permanent deletion (required)") - flags.Usage = func() { writeShowsDeleteUsage(stderr) } - if err := flags.Parse(args); err != nil { - if errors.Is(err, flag.ErrHelp) { - return nil - } - return fmt.Errorf("parse shows delete flags: %w", err) - } - configExplicit = configExplicit || flagWasSet(flags, configFlagName) - if flags.NArg() != 0 { - return fmt.Errorf("shows delete received unexpected argument %q", flags.Arg(0)) - } - if slug == "" { - return errors.New("shows delete: --show is required") - } - if !validShowSlug(slug) { - return fmt.Errorf("shows delete: invalid --show %q", slug) - } - if !flagWasSet(flags, yesFlagName) || !yes { - return errors.New("shows delete: --yes is required") - } - return deleteShow( - ctx, - stdout, - stderr, - home, - configPath, - configExplicit, - defaultConfig, - slug, - ) -} diff --git a/internal/cli/shows.go b/internal/cli/shows.go deleted file mode 100644 index 145da5a..0000000 --- a/internal/cli/shows.go +++ /dev/null @@ -1,305 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "crypto/rand" - "encoding/hex" - "errors" - "fmt" - "image/jpeg" - "image/png" - "io" - "net/http" - "os" - "path/filepath" - "time" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -const ( - showArtworkUploadTimeout = 2 * time.Minute - showIDRandomByteLength = 8 -) - -func listShows( - ctx context.Context, - stdout io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, -) error { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("list shows: not logged in; run listenbox login") - } - client, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return err - } - response, err := client.ListShows(ctx) - if err != nil { - return fmt.Errorf("list shows: %w", err) - } - if response == nil { - return fmt.Errorf("list shows against %q returned no response", config.apiOrigin) - } - return handleListShowsResponse(stdout, response) -} - -func handleListShowsResponse(stdout io.Writer, response *publicapi.ListShowsResponse) error { - switch { - case response.Status200 != nil: - for _, show := range *response.Status200 { - if _, err := fmt.Fprintln(stdout, show.Slug); err != nil { - return fmt.Errorf("print show slug %q: %w", show.Slug, err) - } - } - return nil - case response.Status400 != nil: - return fmt.Errorf("list shows: %s", response.Status400.Message) - case response.StatusCode == http.StatusUnauthorized: - return errors.New("list shows: authentication failed; run listenbox login") - default: - return fmt.Errorf("list shows returned HTTP status %d", response.StatusCode) - } -} - -func createShow( - ctx context.Context, - stdout io.Writer, - home string, - configPath string, - configExplicit bool, - defaultConfig loadedCLIConfig, - title string, - slug string, - sourceKind publicapi.ShowSourceKind, - artworkPath string, - language string, -) error { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("create show: not logged in; run listenbox login") - } - client, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return err - } - showID, err := mintShowID() - if err != nil { - return fmt.Errorf("create show %q: %w", slug, err) - } - var imageAssetID *publicapi.ImageAssetID - if artworkPath != "" { - uploadClient := &http.Client{Timeout: showArtworkUploadTimeout} - uploadedID, err := uploadShowArtwork( - ctx, - client, - uploadClient, - showID, - artworkPath, - ) - if err != nil { - return fmt.Errorf("create show %q artwork: %w", slug, err) - } - imageAssetID = &uploadedID - } - response, err := client.CreateShow(ctx, publicapi.CreateShowParams{ - Body: publicapi.CreateShow{ - Id: showID, - ImageAssetId: imageAssetID, - Title: title, - Slug: slug, - SourceKind: sourceKind, - Language: language, - }, - }) - if err != nil { - return fmt.Errorf("create show %q: %w", slug, err) - } - if response == nil { - return fmt.Errorf("create show %q against %q returned no response", slug, config.apiOrigin) - } - return handleCreateShowResponse(stdout, config.dashboardOrigin, slug, response) -} - -func uploadShowArtwork( - ctx context.Context, - client *publicapi.Client, - uploadClient *http.Client, - showID publicapi.ShowID, - path string, -) (publicapi.ImageAssetID, error) { - raw, err := os.ReadFile(path) - if err != nil { - return "", fmt.Errorf("read %q: %w", path, err) - } - contentType, ok := showArtworkContentType(raw) - if !ok { - return "", errors.New("artwork must be a JPEG or PNG image") - } - if err := validateShowArtworkDimensions(raw); err != nil { - return "", err - } - fileName := filepath.Base(path) - presign, err := client.CreateImageUploadPresign( - ctx, - publicapi.CreateImageUploadPresignParams{ - Body: publicapi.CreateImageUploadPresign{ - ByteLength: int64(len(raw)), - ContentType: contentType, - FileName: &fileName, - ShowId: showID, - }, - }, - ) - if err != nil { - return "", fmt.Errorf("prepare upload: %w", err) - } - if presign.Status201 == nil { - return "", fmt.Errorf("prepare upload returned HTTP %d", presign.StatusCode) - } - if err := putShowArtwork(ctx, uploadClient, presign.Status201, raw, contentType); err != nil { - return "", err - } - - completed, err := client.CompleteImageUpload( - ctx, - publicapi.CompleteImageUploadParams{ - ImageAssetId: presign.Status201.ImageAssetId, - Body: publicapi.CompleteImageUpload{ - ObjectKey: presign.Status201.ObjectKey, - }, - }, - ) - if err != nil { - return "", fmt.Errorf("verify upload: %w", err) - } - if completed.Status201 == nil { - return "", fmt.Errorf("verify upload returned HTTP %d", completed.StatusCode) - } - return completed.Status201.Id, nil -} - -func mintShowID() (publicapi.ShowID, error) { - randomBytes := make([]byte, showIDRandomByteLength) - if _, err := rand.Read(randomBytes); err != nil { - return "", fmt.Errorf("mint show ID: %w", err) - } - return "shw_" + hex.EncodeToString(randomBytes), nil -} - -func validateShowArtworkDimensions(raw []byte) error { - width, height, err := showArtworkDimensions(raw) - if err != nil { - return err - } - if width != height || width < 1400 || width > 3000 { - return fmt.Errorf( - "upload a square image between 1400 and 3000 pixels. Attempted resolution: %d × %d pixels", - width, - height, - ) - } - return nil -} - -func showArtworkDimensions(raw []byte) (int, int, error) { - if len(raw) >= 3 && raw[0] == 0xff && raw[1] == 0xd8 && raw[2] == 0xff { - config, err := jpeg.DecodeConfig(bytes.NewReader(raw)) - if err != nil { - return 0, 0, fmt.Errorf("read artwork dimensions: %w", err) - } - return config.Width, config.Height, nil - } - config, err := png.DecodeConfig(bytes.NewReader(raw)) - if err != nil { - return 0, 0, fmt.Errorf("read artwork dimensions: %w", err) - } - return config.Width, config.Height, nil -} - -func putShowArtwork( - ctx context.Context, - client *http.Client, - presign *publicapi.CreatedImageUploadPresign, - raw []byte, - contentType string, -) error { - request, err := http.NewRequestWithContext( - ctx, - presign.Method, - presign.UploadUrl, - bytes.NewReader(raw), - ) - if err != nil { - return fmt.Errorf("create upload request: %w", err) - } - request.ContentLength = int64(len(raw)) - request.Header.Set("Content-Type", contentType) - response, err := client.Do(request) - if err != nil { - return fmt.Errorf("upload artwork: %w", err) - } - defer response.Body.Close() - if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { - return fmt.Errorf("upload artwork returned HTTP %d", response.StatusCode) - } - return nil -} - -func showArtworkContentType(raw []byte) (string, bool) { - if len(raw) >= 3 && raw[0] == 0xff && raw[1] == 0xd8 && raw[2] == 0xff { - return "image/jpeg", true - } - pngSignature := []byte{0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a} - if len(raw) >= len(pngSignature) && bytes.Equal(raw[:len(pngSignature)], pngSignature) { - return "image/png", true - } - return "", false -} - -func handleCreateShowResponse( - stdout io.Writer, - dashboardOrigin string, - slug string, - response *publicapi.CreateShowResponse, -) error { - switch { - case response.Status201 != nil: - showURL, err := showManagementURL(dashboardOrigin, response.Status201.TeamId, response.Status201.Id) - if err != nil { - return fmt.Errorf("create show %q returned invalid show location: %w", slug, err) - } - if _, err := fmt.Fprintf( - stdout, - "Created show %q\nOpen in Listenbox: %s\n", - response.Status201.Slug, - showURL, - ); err != nil { - return fmt.Errorf("print created show slug %q: %w", response.Status201.Slug, err) - } - return nil - case response.Status400 != nil: - return fmt.Errorf("create show %q: %s", slug, response.Status400.Message) - case response.StatusCode == http.StatusUnauthorized: - return fmt.Errorf("create show %q: authentication failed; run listenbox login", slug) - case response.StatusCode == http.StatusForbidden: - return fmt.Errorf("create show %q: API key lacks show:create scope", slug) - case response.StatusCode == http.StatusConflict: - return fmt.Errorf("create show: slug %q already exists", slug) - default: - return fmt.Errorf("create show %q returned HTTP status %d", slug, response.StatusCode) - } -} diff --git a/internal/cli/shows_test.go b/internal/cli/shows_test.go deleted file mode 100644 index e7da900..0000000 --- a/internal/cli/shows_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "regexp" - "testing" - - "gotest.tools/v3/assert" -) - -//nolint:funlen // Complete CLI request and response contract stays visible in one test. -func TestShowsCreateSendsRequiredPodcastType(t *testing.T) { - t.Parallel() - - for _, podcastType := range []string{testAudioPodcastType, testVideoPodcastType} { - t.Run(podcastType, func(t *testing.T) { - t.Parallel() - - requests := make(chan map[string]any, 1) - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - assert.Equal(t, request.URL.Path, "/s/shows") - assert.Equal(t, request.Header.Get("Authorization"), "Bearer "+testCLIAPIKey) - var body map[string]any - assert.NilError(t, json.NewDecoder(request.Body).Decode(&body)) - requests <- body - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusCreated) - _, err := fmt.Fprintf( - writer, - `{"id":"shw_0123456789abcdef","team_id":"team_0123456789abcdef",`+ - `"title":"CLI Show","slug":"`+testCLIShowSlug+`","source_kind":%q,"language":"en"}`, - podcastType, - ) - assert.NilError(t, err) - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL, "https://dashboard.test:9443") - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{ - configFlagArgument, - configPath, - showsCommandName, - createCommandName, - titleFlagArgument, - "CLI Show", - slugFlagArgument, - testCLIShowSlug, - typeFlagArgument, - podcastType, - languageFlagArgument, - "en", - }, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.NilError(t, err) - wantOutput := "Created show \"cli-show\"\n" + - "Open in Listenbox: https://dashboard.test:9443/" + - "team_0123456789abcdef/shows/shw_0123456789abcdef\n" - assert.Equal(t, stdout.String(), wantOutput) - body := <-requests - showID, ok := body["id"].(string) - assert.Assert(t, ok) - assert.Assert(t, regexp.MustCompile(`^shw_[a-f0-9]{16}$`).MatchString(showID)) - assert.Equal(t, body["title"], "CLI Show") - assert.Equal(t, body["slug"], testCLIShowSlug) - assert.Equal(t, body["source_kind"], podcastType) - assert.Equal(t, body["language"], "en") - }) - } -} - -func TestShowsListPrintsAccessibleSlugs(t *testing.T) { - t.Parallel() - - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - assert.Equal(t, request.Method, http.MethodGet) - assert.Equal(t, request.URL.Path, "/s/shows") - assert.Equal(t, request.Header.Get("Authorization"), "Bearer "+testCLIAPIKey) - writer.Header().Set("Content-Type", "application/json") - _, err := fmt.Fprint( - writer, - `[{"id":"shw_second","team_id":"team_test","title":"Second","slug":"second","source_kind":"video","language":"en"},`+ - `{"id":"shw_first","team_id":"team_test","title":"First","slug":"first","source_kind":"audio","language":"en"}]`, - ) - assert.NilError(t, err) - })) - defer server.Close() - - home := t.TempDir() - configPath := writeTestCLIConfig(t, home, server.URL, "https://dashboard.test") - assert.NilError(t, writeStoredAuth(defaultAuthPath(home), storedAuth{APIKey: testCLIAPIKey})) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, showsCommandName, listCommandName}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.NilError(t, err) - assert.Equal(t, stdout.String(), "second\nfirst\n") -} diff --git a/internal/cli/testdata/media/README.md b/internal/cli/testdata/media/README.md deleted file mode 100644 index a0b3cf8..0000000 --- a/internal/cli/testdata/media/README.md +++ /dev/null @@ -1,10 +0,0 @@ -Synthetic, two-second media fixtures copied from Listenbox's deterministic -YouTube E2E catalogue (listenbox/listenbox2, apps/fixtures/e2e/youtube). -No network or external executable is needed by the native media tests. - -- video.mp4: dash-range/720/manifest-stream0.mp4, AVC 1280x720. -- aac.m4a: dash-range/720/manifest-stream1.mp4, AAC audio. -- opus.webm: audio/opus.webm, Opus audio. - -Tests exercise AAC remux, Opus decode/resample/AAC encode, bounded two-input -muxing, fast-start MP4, HLS output, cancellation, and malformed input. diff --git a/internal/cli/testdata/media/aac.m4a b/internal/cli/testdata/media/aac.m4a deleted file mode 100644 index 6c8b609f210b2d970289052cb3fb338aac8aa18e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33993 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 diff --git a/internal/cli/testdata/media/opus.webm b/internal/cli/testdata/media/opus.webm deleted file mode 100644 index 2e8d43bf17cb502f778a30827e4dcdfc31602d4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44326 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 Yun4UPDTIJZ{dXYXH4pIJRS5k50Gu`E+yDRo diff --git a/internal/cli/testdata/media/video.mp4 b/internal/cli/testdata/media/video.mp4 deleted file mode 100644 index 893f2cf737e142746d45b41bf95d41183ea88285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 773076 zcmaI+1yo#Jvn~qb9^BpC-QC?KxHRtW?(PsIxVyUqcY+0X2m~j%-{yV4z0dcb|BhRu z*Q%6F8qHy|KB7qU=NtC&c-%Cqn@kHKQ+Ps%klrp_Wxn| zzdVJ3u8_bB?B_o*uyt|$2l?MlA^szQ#@m@$83S#^c4q(g+WG$p(HQ!N zF<6?}I{zmJh`W^;;D6*#t_jo~q>b&(YyqD>pgeX~_U1qV-QDh=r2pL}x!J!qd^2aj zr_U#S+7aBGZHfOQKe@P?*aGDm7gv}68OWzkuCco*aA5z?f9L!Mfc#vxG*JA!KI#9@ z<6ro{?${q6K#dESRSeV&gy>@z1lIz{t5=1UA-h14fky-6^9J639fD&73Ib($x<5(U z|MvL!nFHC;*u@dZKg)bx$S$t`>^4yRhk&|^>HjD|jt5u`=`g$MR1H|DJ_U$NwTw5Xcob5DV}BTWd@jXonjV?aP4fSeosKYaCH&S?Mw0t3Wpt^UnTBmXbw!1&kBDhUDt z1+-H~{EM@f{U`42Uz`efc0Vg{0PUZ7Xb}I^X@T|r!7u((ulYZ9{`|+j@-OEK0`>}w zWA6Xgk5&Pw0Ev$GUppsoUqAci3i*#8(D_rRv;On~_6_Xkvkv>(zc@|RKOAzIzz#Sm zpsgAT^5dg?L*h@*?^Wrxbov$IRkDjqPgYKLVg_PU2WJ2=3nvG$GaD-_E3pY12PZR! zG0-5*2y|dlP?nHlU?&#Q6a#vi0Zf4gF$YI4TY$MMF)K4O8v`pdD+ds?bai#)Wn%L1 z@L>FW${lQt?HL`MEto#5Fj~6W*#dnW99^v(>|JAO$Atpflsmk8?hO{#MZ&o zMu3HvnU|T^*xuOI%LO37?8(N!2E46MYimd?P4 z&qTHk4mQS?K*jL?U@o>+rodkQ2Vf?)cmAh_sg<3v>t`QU_O1YDTVtRT5I3=Pb2j!e zG4sN&sCTh znphdTd={~C2K=)w4}g`0rK<_h=HLjhH?(kY1X}+E9f6@X054!}0XF9UNQS^~-p}1~ zF$LHIOx-^F_^0;ghB^aWEP=YSsp0=F_A}7gRKV2PoY>9;xT~Lw0xANmY>dprPM=E= zU}oe5ijJRo|KY}-0-QWRz{M5dD8No^

-R@HhY`$POI36L9*UvjhnQ0nz>mhzteE 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

@EVWRZ{;^L_7oY%j#rr1J83xp@Y6L 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

**=0E14Ni{fK4 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

6cjcQ~`%VA!D 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

xCw@ffH=fsvj}8#D+*zf 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�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

{*9VE?Tt?_2tff*_OWsCdn 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

imrq&J7w3)b#`{yBK%2GSp}ppVZX08Hee9yJoZ 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

*g=-pEz1#uk<$p zJ$#QSstrGN>I~J55ZOd@G0gfdSzcar3+-0ox{FOE!X);ONpxr@-_%22RyBbYV}IXy zj@PagQO5!T z-2Te~WCE}JAHZcshTGX>^fVd*a}jOOsYu zK)wLt2h}CO)y}_~*TWXNxsy3+p2$OR{{$tHZSxZL9kho(^#yggQ7aqT_wOGQ%kGuy z@v7+b8oy~niH-47^RF5?8X}73|EREmHbAM}0|S;lt-=Z)Yq@|bOgoe6LEB(LR2I_f ziAjvH5F(50@YU{q6S^l2F>XQ~R}h76_%;Y>1m~Hb;**d(2?oTOnAeNcPDl{{U)Dfu z%b>nTRx3F*u3}t6{9x3UhyioRHpr`}w_od2c}=EjM>QB~Q5g_yj))Fl=T-x&ze>y* zy(pUv0B}!kK!G%%vR(q!y|IvLf>AAADy42PCmOkerWp(0xB=9fEcsLJcCqJX6~qBQ zK{JI!ojv6O&-JD5u8^)48XbrFm9mIkW+=f!%iG`INYf8?btv#=iWc zBx+3>dsT3%(oNNcg_d%Day)fY8c1GNtM*Syl%TV_;Di_XVuG(s|1?@e$Qi!KDf0u$ z8|4%`sd&wO4jc>IV@57WNY#W8t8

&n(6^Qa1uKJsO0w+RJa^=0N?0FP991ZMG4B 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≀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\n", name, email) - } - if err != nil { - return fmt.Errorf("print auth status for user %q: %w", email, err) - } - return nil -} diff --git a/internal/cli/whoami_test.go b/internal/cli/whoami_test.go deleted file mode 100644 index d0dfbda..0000000 --- a/internal/cli/whoami_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "fmt" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "testing" - - publicapi "github.com/listenbox/listenbox-cli/publicapi" - "gotest.tools/v3/assert" -) - -func TestAuthStatusPrintsStoredAuthorizationIdentity(t *testing.T) { - t.Parallel() - - credential := "apk_test." + t.Name() - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - assert.Equal(t, request.URL.Path, "/s/whoami") - assert.Equal(t, request.Header.Get("Authorization"), "Bearer "+credential) - writer.Header().Set("Content-Type", "application/json") - _, err := fmt.Fprint( - writer, - `{"email":"john@example.com","id":"apk_test","name":"John",`+ - `"team_id":"lb_team_test","scopes":["show:create","show:update"]}`, - ) - assert.NilError(t, err) - })) - defer server.Close() - - home := t.TempDir() - writeAuthStatusTestAuth(t, home, credential) - configPath := writeTestCLIConfig(t, home, server.URL) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, authCommandName, statusCommandName}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.NilError(t, err) - assert.Equal( - t, - stdout.String(), - "Logged in as John \n", - ) -} - -func TestAuthStatusPrintsEmailWhenNameIsAbsent(t *testing.T) { - t.Parallel() - - credential := "apk_test." + t.Name() - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { - writer.Header().Set("Content-Type", "application/json") - _, err := fmt.Fprint( - writer, - `{"email":"john@example.com","id":"apk_test","team_id":"lb_team_test","scopes":["show:create"]}`, - ) - assert.NilError(t, err) - })) - defer server.Close() - - home := t.TempDir() - writeAuthStatusTestAuth(t, home, credential) - configPath := writeTestCLIConfig(t, home, server.URL) - var stdout bytes.Buffer - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, authCommandName, statusCommandName}, - &stdout, - &bytes.Buffer{}, - home, - ) - assert.NilError(t, err) - assert.Equal(t, stdout.String(), "Logged in as john@example.com\n") -} - -func TestPrintAuthStatusRejectsMissingEmail(t *testing.T) { - t.Parallel() - - var stdout bytes.Buffer - err := printAuthStatus(&stdout, publicapi.APIKeyWhoami{}) - assert.ErrorContains(t, err, "user email") - assert.Equal(t, stdout.String(), "") -} - -func TestAuthStatusRequiresStoredAuthorization(t *testing.T) { - t.Parallel() - - err := runCLIWithHome( - context.Background(), - []string{authCommandName, statusCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - t.TempDir(), - ) - assert.ErrorContains(t, err, "not logged in; run listenbox login") -} - -func TestAuthStatusReportsRejectedStoredAuthorization(t *testing.T) { - t.Parallel() - - server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { - writer.WriteHeader(http.StatusUnauthorized) - })) - defer server.Close() - - home := t.TempDir() - writeAuthStatusTestAuth(t, home, "apk_test.expired-secret") - configPath := writeTestCLIConfig(t, home, server.URL) - err := runCLIWithHome( - context.Background(), - []string{configFlagArgument, configPath, authCommandName, statusCommandName}, - &bytes.Buffer{}, - &bytes.Buffer{}, - home, - ) - assert.ErrorContains(t, err, "authentication failed; run listenbox login") -} - -func writeAuthStatusTestAuth(t *testing.T, home string, credential string) { - t.Helper() - directory := filepath.Dir(defaultAuthPath(home)) - assert.NilError(t, os.MkdirAll(directory, authDirectoryMode)) - assert.NilError( - t, - os.WriteFile(defaultAuthPath(home), []byte(fmt.Sprintf(`{"api_key":%q}`, credential)), authFileMode), - ) -} diff --git a/internal/cli/youtube.go b/internal/cli/youtube.go deleted file mode 100644 index 09e267b..0000000 --- a/internal/cli/youtube.go +++ /dev/null @@ -1,459 +0,0 @@ -//nolint:lll // Media manifests and generated API mappings are kept together for review. -package cli - -import ( - "context" - "crypto/sha256" - "encoding/hex" - "errors" - "fmt" - "io" - "io/fs" - "math" - "net/http" - "net/url" - "os" - "path/filepath" - "slices" - "strconv" - "strings" - "time" - - "github.com/asticode/go-astiav" - youtube "github.com/kkdai/youtube/v2" - publicapi "github.com/listenbox/listenbox-cli/publicapi" -) - -const ( - youtubeTransferTimeout = 30 * time.Minute - youtubeCleanupTimeout = 10 * time.Second - youtubeFileMode = 0o600 - youtubeDirectoryMode = 0o700 - youtubeResponseLimit = 64 << 10 - youtubeVideoKind = "video" - youtubeAudioKind = "audio" -) - -func isYouTubeSource(source string) bool { - parsed, err := url.Parse(source) - if err != nil { - return false - } - switch strings.ToLower(parsed.Hostname()) { - case "youtube.com", "www.youtube.com", "m.youtube.com", "youtu.be": - return true - default: - return false - } -} - -//nolint:cyclop // Local download, media processing and upload failures retain explicit recovery boundaries. -func importYouTube(ctx context.Context, stdout, stderr io.Writer, home, configPath string, configExplicit bool, defaultConfig loadedCLIConfig, sourceURL, slug string, slugExplicit bool) error { - config, err := loadCLIConfig(configPath, configExplicit, home, defaultConfig) - if err != nil { - return err - } - auth, ok := readUsableStoredAuth(defaultAuthPath(home)) - if !ok { - return errors.New("import YouTube: not logged in; run listenbox login") - } - api, err := newPublicClient(config.apiOrigin, auth.APIKey, true) - if err != nil { - return err - } - client := &http.Client{Timeout: youtubeTransferTimeout} - extractor := &youtube.Client{HTTPClient: client} - title, videos, err := discoverYouTube(ctx, extractor, sourceURL) - if err != nil { - return err - } - if !slugExplicit { - digest := sha256.Sum256([]byte(sourceURL)) - slug = "youtube-" + hex.EncodeToString(digest[:8]) - } - show, err := ensureYouTubeShow(ctx, api, title, slug) - if err != nil { - return err - } - progress := newImportProgress(stderr, writerIsTerminal(stderr)) - defer progress.cleanFailureLine() - for index, id := range videos { - if err := importYouTubeVideo(ctx, extractor, client, api, slug, id, stdout, config.printTraceIDs); err != nil { - return fmt.Errorf("import YouTube video %q: %w", id, err) - } - //nolint:gosec // The quotient is bounded to 0..100 by the playlist loop. - if err := progress.update(uint32((index + 1) * 100 / len(videos))); err != nil { - return err - } - } - if err := progress.complete(); err != nil { - return err - } - if _, err := fmt.Fprintln(stdout, slug); err != nil { - return err - } - managementURL, err := showManagementURL(config.dashboardOrigin, show.TeamId, show.Id) - if err != nil { - return err - } - _, err = fmt.Fprintf(stderr, "Open in Listenbox: %s\n", managementURL) - return err -} - -func discoverYouTube(ctx context.Context, client *youtube.Client, sourceURL string) (string, []string, error) { - parsed, err := url.Parse(sourceURL) - if err != nil || parsed.Scheme != "https" || parsed.User != nil || parsed.Port() != "" { - return "", nil, fmt.Errorf("invalid YouTube source URL %q", sourceURL) - } - if parsed.Query().Get("list") != "" { - playlist, err := client.GetPlaylistContext(ctx, sourceURL) - if err != nil { - return "", nil, fmt.Errorf("read YouTube playlist: %w", err) - } - ids := make([]string, 0, len(playlist.Videos)) - seen := make(map[string]bool, len(playlist.Videos)) - for _, entry := range playlist.Videos { - if entry.ID != "" && !seen[entry.ID] { - ids = append(ids, entry.ID) - seen[entry.ID] = true - } - } - if len(ids) == 0 { - return "", nil, errors.New("YouTube playlist has no public videos") - } - return playlist.Title, ids, nil - } - video, err := client.GetVideoContext(ctx, sourceURL) - if err != nil { - return "", nil, fmt.Errorf("read YouTube video: %w", err) - } - return video.Title, []string{video.ID}, nil -} - -func ensureYouTubeShow(ctx context.Context, client *publicapi.Client, title, slug string) (publicapi.Show, error) { - listed, err := client.ListShows(ctx) - if err != nil { - return publicapi.Show{}, err - } - if listed == nil || listed.Status200 == nil { - return publicapi.Show{}, errors.New("cannot list writable shows") - } - for _, show := range *listed.Status200 { - if show.Slug == slug { - if show.SourceKind != youtubeVideoKind { - return publicapi.Show{}, fmt.Errorf("show %q is not a video show", slug) - } - return show, nil - } - } - id, err := mintShowID() - if err != nil { - return publicapi.Show{}, err - } - created, err := client.CreateShow(ctx, publicapi.CreateShowParams{Body: publicapi.CreateShow{Id: id, Title: title, Slug: slug, SourceKind: youtubeVideoKind, Language: "en"}}) - if err != nil { - return publicapi.Show{}, err - } - if created == nil { - return publicapi.Show{}, errors.New("create YouTube show returned no response") - } - if created.Status201 == nil { - return publicapi.Show{}, handleCreateShowResponse(io.Discard, "", slug, created) - } - return *created.Status201, nil -} - -//nolint:cyclop,funlen // Local download, media processing and upload failures retain explicit recovery boundaries. -func importYouTubeVideo(ctx context.Context, extractor *youtube.Client, client *http.Client, api *publicapi.Client, slug, id string, stdout io.Writer, printTraceIDs bool) (retErr error) { - video, err := extractor.GetVideoContext(ctx, id) - if err != nil { - return err - } - directory, err := os.MkdirTemp("", "listenbox-youtube-") - if err != nil { - return err - } - defer func() { _ = os.RemoveAll(directory) }() - if err := downloadYouTubeVideo(ctx, extractor, video, directory); err != nil { - return err - } - duration, err := packageYouTubeVideo(ctx, directory) - if err != nil { - return err - } - objects, err := describeYouTubePackage(directory) - if err != nil { - return err - } - response, err := api.CreateEpisodePackage(ctx, publicapi.CreateEpisodePackageParams{Body: publicapi.CreateEpisodePackage{ - ShowSlug: slug, SourceUrl: "https://www.youtube.com/watch?v=" + id, Title: video.Title, Description: &video.Description, - DurationSeconds: duration, Objects: objects, - }}) - if err != nil { - return err - } - if response != nil && response.Status402 != nil { - admission := response.Status402 - return fmt.Errorf("video storage quota: %d seconds requested, %d remaining; %s", admission.RequestedSeconds, admission.RemainingSeconds, admission.PricingUrl) - } - if response == nil || response.Status201 == nil { - status := 0 - if response != nil { - status = response.StatusCode - } - return fmt.Errorf("reserve prepared media: HTTP %d", status) - } - session := *response.Status201 - if session.Status == importCompletedStatus { - return nil - } - defer func() { - if retErr == nil { - return - } - cleanupCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), youtubeCleanupTimeout) - defer cancel() - response, err := api.CancelEpisodePackage(cleanupCtx, publicapi.CancelEpisodePackageParams{UploadSessionId: session.UploadSessionId}) - // A completed package survives an acknowledgement loss. - if err != nil { - retErr = errors.Join(retErr, fmt.Errorf("cancel pending package: %w", err)) - } else if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusConflict { - retErr = errors.Join(retErr, fmt.Errorf("cancel pending package: HTTP %d", response.StatusCode)) - } - }() - - if printTraceIDs { - if err := printImportTraceID(stdout, response.Raw.Header.Get("X-Trace-Id")); err != nil { - return err - } - } - for index, object := range objects { - if err := uploadYouTubeObject(ctx, api, client, session, int64(index), object, directory); err != nil { - return err - } - } - completed, err := api.CompleteEpisodePackage(ctx, publicapi.CompleteEpisodePackageParams{UploadSessionId: session.UploadSessionId}) - if err != nil { - return err - } - if completed == nil || completed.Status200 == nil || completed.Status200.Status != importCompletedStatus { - return errors.New("prepared media did not complete") - } - return nil -} - -//nolint:cyclop // Local download, media processing and upload failures retain explicit recovery boundaries. -func downloadYouTubeVideo(ctx context.Context, client *youtube.Client, video *youtube.Video, directory string) error { - formats := slices.Clone(video.Formats) - slices.SortFunc(formats, func(a, b youtube.Format) int { return b.Height - a.Height }) - var selected *youtube.Format - for index := range formats { - if strings.Contains(formats[index].MimeType, "avc1") && formats[index].Height <= 1080 { - selected = &formats[index] - break - } - } - if selected == nil { - return errors.New("YouTube video has no AVC rendition at or below 1080p") - } - videoPath := filepath.Join(directory, "source-video.mp4") - if err := downloadYouTubeStream(ctx, client, video, selected, videoPath); err != nil { - return err - } - audioPath := videoPath - if selected.AudioChannels == 0 { - var audio *youtube.Format - for index := range formats { - candidate := &formats[index] - if strings.HasPrefix(candidate.MimeType, "audio/") && (audio == nil || candidate.Bitrate > audio.Bitrate) { - audio = candidate - } - } - if audio == nil { - return errors.New("YouTube video has no audio stream") - } - audioPath = filepath.Join(directory, "source-audio") - if err := downloadYouTubeStream(ctx, client, video, audio, audioPath); err != nil { - return err - } - } - preparedAudio := filepath.Join(directory, "audio.m4a") - if err := prepareMediaAudio(ctx, audioPath, preparedAudio); err != nil { - return err - } - return muxMedia(ctx, videoPath, preparedAudio, filepath.Join(directory, "video.mp4")) -} - -func downloadYouTubeStream(ctx context.Context, client *youtube.Client, video *youtube.Video, format *youtube.Format, destination string) error { - streamURL, err := client.GetStreamURLContext(ctx, video, format) - if err != nil { - return err - } - request, err := http.NewRequestWithContext(ctx, http.MethodGet, streamURL, nil) - if err != nil { - return err - } - request.Header.Set("Origin", "https://youtube.com") - response, err := client.HTTPClient.Do(request) - if err != nil { - return err - } - defer response.Body.Close() - if response.StatusCode != http.StatusOK { - return fmt.Errorf("download YouTube media: HTTP %d", response.StatusCode) - } - file, err := os.OpenFile(destination, os.O_CREATE|os.O_EXCL|os.O_WRONLY, youtubeFileMode) - if err != nil { - return err - } - _, copyErr := io.Copy(file, response.Body) - return errors.Join(copyErr, file.Close()) -} - -func packageYouTubeVideo(ctx context.Context, directory string) (int64, error) { - source := filepath.Join(directory, "video.mp4") - width, height, err := mediaDimensions(source) - if err != nil { - return 0, err - } - for _, kind := range []string{youtubeVideoKind, youtubeAudioKind} { - root := filepath.Join(directory, "hls", kind) - if err := os.MkdirAll(root, youtubeDirectoryMode); err != nil { - return 0, err - } - mediaType := astiav.MediaTypeVideo - if kind == youtubeAudioKind { - mediaType = astiav.MediaTypeAudio - } - err := packageMediaTrack(ctx, source, root, mediaType) - if err != nil { - return 0, err - } - } - master := fmt.Sprintf("#EXTM3U\n#EXT-X-VERSION:7\n#EXT-X-INDEPENDENT-SEGMENTS\n#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",NAME=\"Audio\",DEFAULT=YES,AUTOSELECT=YES,URI=\"audio/index.m3u8\"\n#EXT-X-STREAM-INF:BANDWIDTH=12000000,RESOLUTION=%dx%d,AUDIO=\"audio\"\nvideo/index.m3u8\n", width, height) - if err := os.WriteFile(filepath.Join(directory, "hls", "master.m3u8"), []byte(master), youtubeFileMode); err != nil { - return 0, err - } - videoDuration, err := localPlaylistDuration(filepath.Join(directory, "hls", youtubeVideoKind, "index.m3u8")) - if err != nil { - return 0, err - } - audioDuration, err := localPlaylistDuration(filepath.Join(directory, "hls", youtubeAudioKind, "index.m3u8")) - if err != nil { - return 0, err - } - return int64(math.Ceil(min(videoDuration, audioDuration))), nil -} - -func localPlaylistDuration(file string) (float64, error) { - content, err := os.ReadFile(file) - if err != nil { - return 0, err - } - var total float64 - for line := range strings.SplitSeq(string(content), "\n") { - if value, ok := strings.CutPrefix(line, "#EXTINF:"); ok { - seconds, _, _ := strings.Cut(value, ",") - duration, err := strconv.ParseFloat(seconds, 64) - if err != nil || math.IsNaN(duration) || math.IsInf(duration, 0) || duration <= 0 { - return 0, fmt.Errorf("invalid HLS segment duration %q", seconds) - } - total += duration - } - } - if total <= 0 { - return 0, fmt.Errorf("playlist %q has no segments", file) - } - return total, nil -} - -func describeYouTubePackage(directory string) ([]publicapi.PreparedMediaObject, error) { - objects := []publicapi.PreparedMediaObject{} - root, err := os.OpenRoot(directory) - if err != nil { - return nil, err - } - defer root.Close() - err = filepath.WalkDir(directory, func(file string, entry fs.DirEntry, walkErr error) error { - if walkErr != nil { - return walkErr - } - if entry.IsDir() { - return nil - } - name, err := filepath.Rel(directory, file) - if err != nil { - return err - } - name = filepath.ToSlash(name) - if name != "video.mp4" && !strings.HasPrefix(name, "hls/") { - return nil - } - contentType := "video/mp4" - if strings.HasSuffix(name, ".m3u8") { - contentType = "application/vnd.apple.mpegurl" - } else if strings.HasPrefix(name, "hls/audio/") { - contentType = "audio/mp4" - } - source, err := root.Open(name) - if err != nil { - return err - } - hash := sha256.New() - length, err := io.Copy(hash, source) - closeErr := source.Close() - if err := errors.Join(err, closeErr); err != nil { - return err - } - objects = append(objects, publicapi.PreparedMediaObject{Name: name, ContentType: contentType, ByteLength: length, Sha256: hex.EncodeToString(hash.Sum(nil))}) - return nil - }) - return objects, err -} - -//nolint:cyclop // Local download, media processing and upload failures retain explicit recovery boundaries. -func uploadYouTubeObject(ctx context.Context, api *publicapi.Client, client *http.Client, session publicapi.EpisodePackage, ordinal int64, object publicapi.PreparedMediaObject, directory string) error { - file, err := os.Open(filepath.Join(directory, filepath.FromSlash(object.Name))) - if err != nil { - return err - } - defer file.Close() - if session.PartSize < 5<<20 { - return fmt.Errorf("invalid upload part size %d", session.PartSize) - } - for start, number := int64(0), int32(1); start < object.ByteLength; start, number = start+session.PartSize, number+1 { - response, err := api.PresignEpisodePackageParts(ctx, publicapi.PresignEpisodePackagePartsParams{UploadSessionId: session.UploadSessionId, ObjectIndex: ordinal, Body: publicapi.PresignEpisodeUploadSessionParts{PartNumbers: []int32{number}}}) - if err != nil { - return err - } - if response == nil || response.Status200 == nil { - return errors.New("cannot sign media object upload") - } - if len(response.Status200.Parts) == 0 { - return nil - } - if len(response.Status200.Parts) != 1 { - return errors.New("invalid signed part response") - } - part := response.Status200.Parts[0] - length := min(session.PartSize, object.ByteLength-start) - request, err := http.NewRequestWithContext(ctx, http.MethodPut, part.UploadUrl, io.NewSectionReader(file, start, length)) - if err != nil { - return err - } - request.ContentLength = length - uploaded, err := client.Do(request) - if err != nil { - return err - } - _, readErr := io.Copy(io.Discard, io.LimitReader(uploaded.Body, youtubeResponseLimit)) - closeErr := uploaded.Body.Close() - if err := errors.Join(readErr, closeErr); err != nil { - return err - } - if uploaded.StatusCode != http.StatusOK { - return fmt.Errorf("upload media object %q: HTTP %d", object.Name, uploaded.StatusCode) - } - } - return nil -} diff --git a/internal/cliconfig/models.go b/internal/cliconfig/models.go deleted file mode 100644 index d528742..0000000 --- a/internal/cliconfig/models.go +++ /dev/null @@ -1,7 +0,0 @@ -package cliopenapi - -type CLIConfig struct { - ApiOrigin string `json:"api_origin"` - DashboardOrigin string `json:"dashboard_origin"` - PrintTraceIds bool `json:"print_trace_ids"` -} diff --git a/js/youtube.ts b/js/youtube.ts new file mode 100644 index 0000000..ef99cf7 --- /dev/null +++ b/js/youtube.ts @@ -0,0 +1,211 @@ +import { + Constants, + Innertube, + Log, + Platform, + Player, + YTNodes, +} from "youtubei.js" +import type { Misc } from "youtubei.js" + +declare function hostFetch(request: string): Promise +declare const Buffer: { + from( + value: string | ArrayBuffer, + encoding?: string, + ): { toString(encoding: string): string } +} + +async function fetchThroughRust(input: RequestInfo | URL, init?: RequestInit) { + const request = new Request(input, init) + const body = + request.method === "GET" || request.method === "HEAD" + ? null + : Buffer.from(await request.arrayBuffer()).toString("base64") + const result: { + error?: string + body: string + status: number + headers: Record + } = JSON.parse( + await hostFetch( + JSON.stringify({ + url: request.url, + method: request.method, + headers: [...request.headers], + body, + }), + ), + ) + if (result.error) throw new Error(result.error) + return new Response( + Buffer.from(result.body, "base64") as unknown as BodyInit, + { + status: result.status, + headers: result.headers, + }, + ) +} + +Platform.load({ + runtime: "unknown", + server: true, + fetch: fetchThroughRust, + Request, + Response, + Headers, + FormData, + File, + ReadableStream, + CustomEvent, + uuidv4: () => crypto.randomUUID(), + sha1Hash: async (text: string) => + Buffer.from( + await crypto.subtle.digest("SHA-1", new TextEncoder().encode(text)), + ).toString("hex"), + eval: async (data, env) => + new Function(...Object.keys(env), data.output)(...Object.values(env)), +} as Parameters[0]) +Log.setLevel(Log.Level.NONE) + +let client: Promise | undefined +const cache = new Map() + +function session() { + client ??= Innertube.create({ + lang: "en", + location: "US", + retrieve_player: false, + generate_session_locally: true, + cache: { + cache_dir: "", + get: async key => cache.get(key), + set: async (key, value) => { + cache.set(key, value) + }, + remove: async key => { + cache.delete(key) + }, + }, + }) + return client +} + +async function stream(yt: Innertube, format: Misc.Format, cpn: string) { + if ( + format.cipher || + format.signature_cipher || + (format.url && new URL(format.url).searchParams.has("n")) + ) { + yt.session.player ??= await Player.create( + yt.session.cache, + fetchThroughRust, + ) + } + const url = new URL(await format.decipher(yt.session.player)) + url.searchParams.set("cpn", cpn) + return { + url: url.toString(), + user_agent: Constants.CLIENTS.VISIONOS.USER_AGENT, + } +} + +export async function call(method: string, json: string): Promise { + try { + const args: { id: string } = JSON.parse(json) + const yt = await session() + if (method === "playlist") { + let page = await yt.getPlaylist(args.id) + const title = page.info.title + const videos: string[] = [] + const seen = new Set() + const pages = new Set() + for (;;) { + const ids: string[] = [] + for (const item of page.items) { + let id: string + if (item.is(YTNodes.PlaylistVideo)) { + if ( + !item.is_playable || + item.is_live || + item.is_upcoming || + item.upcoming + ) + continue + id = item.id + } else if ( + item.is(YTNodes.LockupView) && + ["VIDEO", "SHORT"].includes(item.content_type) + ) { + if (!item.metadata?.title.toString()) continue + id = item.content_id + } else { + throw new Error("Unsupported playlist item; listing is incomplete") + } + ids.push(id) + if (!seen.has(id)) { + seen.add(id) + videos.push(id) + } + } + const fingerprint = ids.join(",") + if (pages.has(fingerprint) || pages.size >= 10000) + throw new Error("Repeated YouTube playlist page") + pages.add(fingerprint) + if (!page.has_continuation) break + page = await page.getContinuation() + } + if (!videos.length) + throw new Error("YouTube playlist has no public videos") + return JSON.stringify({ title, videos }) + } + if (method !== "media") throw new Error("Unknown YouTube operation") + const info = await yt.getBasicInfo(args.id, { client: "VISIONOS" }) + if ( + info.playability_status?.status !== "OK" || + info.basic_info.is_live || + info.basic_info.is_upcoming + ) { + throw new Error( + `YouTube playback unavailable: ${info.playability_status?.reason ?? "not a public recorded video"}`, + ) + } + const formats = [ + ...(info.streaming_data?.formats ?? []), + ...(info.streaming_data?.adaptive_formats ?? []), + ].filter( + format => + !format.drm_families?.length && + !format.is_type_otf && + !!(format.url || format.cipher || format.signature_cipher), + ) + const video = formats + .filter( + format => + format.has_video && + format.mime_type.includes("avc1") && + (format.height ?? 0) > 0 && + format.height! <= 1080, + ) + .sort((left, right) => right.height! - left.height!)[0] + if (!video) + throw new Error("YouTube video has no AVC rendition at or below 1080p") + const audio = video.has_audio + ? undefined + : formats + .filter(format => format.has_audio && !format.has_video) + .sort((left, right) => right.bitrate - left.bitrate)[0] + if (!video.has_audio && !audio) + throw new Error("YouTube video has no audio stream") + return JSON.stringify({ + title: info.basic_info.title ?? args.id, + description: info.basic_info.short_description ?? "", + video: await stream(yt, video, info.cpn), + audio: audio ? await stream(yt, audio, info.cpn) : null, + }) + } catch (error) { + return JSON.stringify({ + bridge_error: error instanceof Error ? error.message : String(error), + }) + } +} diff --git a/moon.yml b/moon.yml index abf80fd..8040c09 100644 --- a/moon.yml +++ b/moon.yml @@ -1,108 +1,106 @@ +language: rust toolchains: default: system fileGroups: sources: + - Cargo.toml + - Cargo.lock + - rust-toolchain.toml + - src/**/*.rs - prepare-ffmpeg.ts - - ffmpeg-env.sh - - go.mod - - go.sum - - ./**/*.go - - ./**/*.yaml + - js/**/*.ts + - tsconfig.json + - package.json + - nub.lock + - .cargo/config.toml + - moon.yml tasks: native-ffmpeg: - description: Build pinned static FFmpeg libraries for go-astiav. + description: Build pinned FFmpeg 9 libraries for static linking through ffmpeg-the-third. inputs: - prepare-ffmpeg.ts command: node prepare-ffmpeg.ts outputs: - - .cache/ffmpeg + - .cache/ffmpeg/**/* options: cache: true - build: - deps: - - ~:mod-download - - ~:native-ffmpeg + bundle-youtube: inputs: - - "@group(sources)" - script: |- - set -euo pipefail - source ffmpeg-env.sh - go build -trimpath -ldflags="-s -w" -o dist/listenbox ./cmd/listenbox + - js/**/*.ts + - package.json + - nub.lock + command: nubx esbuild js/youtube.ts --bundle --platform=browser --format=esm --keep-names --outfile=.cache/youtube.js outputs: - - dist/listenbox - mod-download: - inputs: - - go.mod - - go.sum - script: |- - set -euo pipefail - go mod download + - .cache/youtube.js options: cache: true - test: + prerequisites: deps: - - ~:mod-download - ~:native-ffmpeg + - ~:bundle-youtube + inputs: [] + options: + internal: true + build: + deps: + - ~:prerequisites inputs: - - moon.yml - - ffmpeg-env.sh - - go.mod - - go.sum - - ./**/*.go - - ./**/*.yaml - - internal/cli/testdata/**/* + - "@group(sources)" script: |- set -euo pipefail - source ffmpeg-env.sh - go test -count=1 -tags=dev -timeout=30s ./... - options: - cache: true + pkgx +llvm.org +cmake.org cargo build --locked + mkdir -p dist + cp target/debug/listenbox dist/listenbox + cp .cache/ffmpeg/NOTICE.txt dist/FFmpeg-NOTICE.txt + outputs: + - dist/listenbox + - dist/FFmpeg-NOTICE.txt lint: deps: - - ~:mod-download - - ~:native-ffmpeg + - ~:prerequisites inputs: - - moon.yml - - ffmpeg-env.sh - - go.mod - - go.sum - - .golangci.yaml - - ./**/*.go - - ./**/*.yaml - - internal/cli/testdata/**/* + - "@group(sources)" script: |- set -euo pipefail - source ffmpeg-env.sh - if [[ "${CI:-}" == "true" ]]; then - test -z "$(gofmt -l .)" - else - gofmt -w . - fi - go vet -tags=dev ./... - mkdir -p .cache/golangci-lint-tmp - GOLANGCI_LINT_CACHE="${GOLANGCI_LINT_CACHE:-$PWD/.cache/golangci-lint}" \ - TMPDIR="$PWD/.cache/golangci-lint-tmp" \ - go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.1 run --timeout=15m --build-tags=dev ./... - options: - cache: true + nubx oxfmt js/ prepare-ffmpeg.ts tsconfig.json package.json + nubx tsc -p tsconfig.json + cargo fmt --all + pkgx +llvm.org +cmake.org cargo clippy --locked --all-targets -- -D warnings + test: + deps: + - ~:prerequisites + inputs: + - "@group(sources)" + command: pkgx +llvm.org +cmake.org cargo test --locked smoke: - description: Smoke-test the CLI executable. deps: - ~:build + inputs: + - "@group(sources)" command: dist/listenbox help - options: - cache: true + build-release: + deps: [~:prerequisites] + inputs: ["@group(sources)"] + script: |- + set -euo pipefail + pkgx +llvm.org +cmake.org cargo build --locked --release + mkdir -p dist/release + cp target/release/listenbox dist/release/listenbox + cp .cache/ffmpeg/NOTICE.txt dist/release/FFmpeg-NOTICE.txt + outputs: + - dist/release/listenbox + - dist/release/FFmpeg-NOTICE.txt package: - description: Package the CLI with its native media runtime and license notices. deps: - - ~:build - script: |- - cp .cache/ffmpeg/NOTICE.txt dist/ffmpeg-NOTICE.txt - tar -czf dist/listenbox.tar.gz -C dist listenbox ffmpeg-NOTICE.txt -C .. LICENSE + - ~:build-release + inputs: + - "@group(sources)" + - LICENSE + - THIRD-PARTY-NOTICES.txt + script: tar -czf dist/listenbox.tar.gz -C dist/release listenbox FFmpeg-NOTICE.txt -C ../.. LICENSE THIRD-PARTY-NOTICES.txt outputs: - dist/listenbox.tar.gz check: - description: Lint, test, and smoke-test the CLI. deps: - ~:lint - ~:test @@ -111,17 +109,7 @@ tasks: inputs: [] options: runInCI: false - mod-tidy: - options: - runInCI: false - script: |- - set -euo pipefail - go mod tidy outdated: + command: cargo update --dry-run options: runInCI: false - script: >- - set -euo pipefail - - go list -u -m -f '{{if and (not .Main) (not .Indirect) .Update}}{{.Path}} {{.Version}} => - {{.Update.Version}}{{end}}' all diff --git a/nub.lock b/nub.lock new file mode 100644 index 0000000..9ec9c7a --- /dev/null +++ b/nub.lock @@ -0,0 +1,770 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + youtubei.js: + specifier: 18.0.0 + version: 18.0.0 + devDependencies: + '@types/node': + specifier: ^26.6.2 + version: 26.6.2 + esbuild: + specifier: 0.28.1 + version: 0.28.1 + oxfmt: + specifier: ^0.68.0 + version: 0.68.0 + typescript: + specifier: 7.0.2 + version: 7.0.2 + +packages: + + '@bufbuild/protobuf@2.15.0': + resolution: {integrity: sha512-DAheWUkVr/SJTWCc+lg9dhY0eN4SaWlf4+bG1KzHeXbnqt0AfB/NX0Z+VunGlM1ki1B4zVvye27MpKh/svySUA==} + + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@oxfmt/binding-android-arm-eabi@0.68.0': + resolution: {integrity: sha512-dhfYPbzv/h9JgHjNkl2R6sOjUfxDyLGOZVb3g8/ScaTNwwJcYgmHh8kcYFDUhinuy1QAoANCWUvw1jlk+z6gAg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.68.0': + resolution: {integrity: sha512-v3Njdi6qY0O/5eGfg01ww2w6gTn2mUvZ72Bnx1/UN53A9wruh3Nk6otc3WkgJLkXD4Qgz1SOcVQieH1oD03V9Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.68.0': + resolution: {integrity: sha512-ei4MCMzHFREmZwPJ7KuWUB4kBuHdsgDrnXGJVcEAopU7fj7S42I8BChdFILWdHvhFqR08FLJtOfbZIr2CDw0cA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.68.0': + resolution: {integrity: sha512-UrKgzZxYhwB9DSvTX+vdgl9M32wLUNKJcAKIoiyx/Kzn/zveqi7W6kYVcRroFMhS3Kwz0KhTk3WBeSuQn4YCTg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.68.0': + resolution: {integrity: sha512-6jrEKgpJbilM1QaRv7hEtKXr4p4AK4jvvyOtajwyhu0kOz3e0O7OLnSTk6tBotRqCcUC4ehZRJ1Zx+Y99wieLw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': + resolution: {integrity: sha512-YOIVnKOBaLeGullskS179N12hjSAdFYnzLjOaKiLhAKNWgnShq9w4xRdtmUm6BlnP65l2/EA9Aw/KlftNxDM7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.68.0': + resolution: {integrity: sha512-xW5XoEHVNqydPBv2KXvk9lmEzyAlOQHVEazKoXUuAacqekjya+OdiaFjjEBl0oJD02raG8g3TRl9OVCh9PDIHA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.68.0': + resolution: {integrity: sha512-QCvYwVVQieu6oyJglAgV9vH/YMDxZyR4cwVSYtoq9oOXd5N+D3TDUBjNwxFrLn5AdcJZOcvn/7IB17vJGp+2Og==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.68.0': + resolution: {integrity: sha512-4TVz5iFQ8ndrHnhX50UXiz9BIWAtUSOHJ6Nus4qWFfJBXq/Ed/krXbF/ehJu42BXM5tIvBs99jNIM22s9agY5A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.68.0': + resolution: {integrity: sha512-qLe3ao0RP84bnPxBvRI+GnlK/jybo538NWu0Xrm+zYeTmJtpzqLhnnd5BH21NafqKnplbGZjtN1cnrOlWF73lw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.68.0': + resolution: {integrity: sha512-Yvyl7a6gbb0vM6r925KW2dO+/CmXySO5TVbcX7o/uZJ+d108HFOG0TxIyHApmn5USuj5mhDPxzhiVwOlGP7uSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.68.0': + resolution: {integrity: sha512-mJlFuFVCxzrYM5sFStN433D/s/mb6Wq4aCQAM02vs/OudHywnaSAd2rb1vlYUJtqdYIciJtiasuxvfbYkv5fLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.68.0': + resolution: {integrity: sha512-RlfSg++qs1hbKltRR6lYvV9EoI3MdlfSQD9w1hdHVYjHqjIn1tkH4FWOpMSmjKGN20zr+nI+W9o4ARogCDudGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.68.0': + resolution: {integrity: sha512-nyzRB9U+dlYUKu3pMo3afHzZBUv/oTHZMG36ZfJViNVfOIzp70Q4GS8FFRGgYJ/p0zcyDCgpBvYISOdJOMh+jQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.68.0': + resolution: {integrity: sha512-iCx3sbZRIvGrL1RafphEiUKBaW1lc0/tAjKOIB/Wjw2+STRBEdu5+fH1Gc1faEWEmc2k5Ks4iUUV54Zd5C9a1A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.68.0': + resolution: {integrity: sha512-x2X5AZez7OgyLLFpwIgItoXBUqudDM7yiaTsxv8R8vKQ6e81l0jVw0NFeUCXzcl1sAJq8h+tC8N4mY8EiMeL4w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.68.0': + resolution: {integrity: sha512-AHVPjXkenLPQUh6kB8zSC8pX2ct9r4T1Edk9r/RNJyov6wPsS5uAfYtipwG4chn6+3bPFG5rI/3DxEeH8vib1w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.68.0': + resolution: {integrity: sha512-n09SjEk5VH7z8Hl4WVP7hho+cCwGViENkQFiM45vbW85dJd7kEhWaHRUobaPzEmrWyu6uumd4EuNfNyDKLtzDA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.68.0': + resolution: {integrity: sha512-gPe+dJLXaPuWPWqlpklDAJp0k+K9KhQPYiQLHfb+i2rmFuUGfJ/5Qlj6tr1mO6of5g0DiLjG/XCFHIaPhotqqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@types/node@26.6.2': + resolution: {integrity: sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + pnpapi: '*' + peerDependenciesMeta: + pnpapi: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + meriyah@7.3.3: + resolution: {integrity: sha512-uE5cnoNj+UYhoMdZDuymCzr5TzEuE3ZF2C4kn6Z76rLhkwnvZ/+6dUZnM41T3fifyuhW0+n8vK7eq6DKJxUfPA==} + engines: {node: '>=20.0.0'} + + oxfmt@0.68.0: + resolution: {integrity: sha512-Z0XMofcXCGUXbcpBHnWyUiX93BGiw1B+lcHNbQDWEtOhX06ewoFfu4zXkyiLhRrNnMq0twqXRHUcJetf+GsiQQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} + engines: {node: ^20.0.0 || >=22.0.0} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true + + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + + youtubei.js@18.0.0: + resolution: {integrity: sha512-7Tztl5QzNL4nIhiAxa4P/0C8cDDsUe0j4iaUeNiaA+ZfSe/l+HkHxXyoWlQXNW7IPi2n6AW23ysVMJLDh+AwWQ==} + +snapshots: + + '@bufbuild/protobuf@2.15.0': {} + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@oxfmt/binding-android-arm-eabi@0.68.0': + optional: true + + '@oxfmt/binding-android-arm64@0.68.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.68.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.68.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.68.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.68.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.68.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.68.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.68.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.68.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.68.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.68.0': + optional: true + + '@types/node@26.6.2': + dependencies: + undici-types: 8.9.0 + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + fflate@0.8.3: {} + + meriyah@7.3.3: {} + + oxfmt@0.68.0: + dependencies: + tinypool: 2.1.2 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.68.0 + '@oxfmt/binding-android-arm64': 0.68.0 + '@oxfmt/binding-darwin-arm64': 0.68.0 + '@oxfmt/binding-darwin-x64': 0.68.0 + '@oxfmt/binding-freebsd-x64': 0.68.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.68.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.68.0 + '@oxfmt/binding-linux-arm64-gnu': 0.68.0 + '@oxfmt/binding-linux-arm64-musl': 0.68.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.68.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.68.0 + '@oxfmt/binding-linux-riscv64-musl': 0.68.0 + '@oxfmt/binding-linux-s390x-gnu': 0.68.0 + '@oxfmt/binding-linux-x64-gnu': 0.68.0 + '@oxfmt/binding-linux-x64-musl': 0.68.0 + '@oxfmt/binding-openharmony-arm64': 0.68.0 + '@oxfmt/binding-win32-arm64-msvc': 0.68.0 + '@oxfmt/binding-win32-ia32-msvc': 0.68.0 + '@oxfmt/binding-win32-x64-msvc': 0.68.0 + + tinypool@2.1.2: {} + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + undici-types@8.9.0: {} + + youtubei.js@18.0.0: + dependencies: + '@bufbuild/protobuf': 2.15.0 + fflate: 0.8.3 + meriyah: 7.3.3 diff --git a/package.json b/package.json new file mode 100644 index 0000000..f1ead99 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "@listenbox/cli", + "private": true, + "type": "module", + "dependencies": { + "youtubei.js": "18.0.0" + }, + "devDependencies": { + "@types/node": "^26.6.2", + "esbuild": "0.28.1", + "oxfmt": "^0.68.0", + "typescript": "7.0.2" + } +} diff --git a/prepare-ffmpeg.ts b/prepare-ffmpeg.ts index 2ed17df..45c1b57 100644 --- a/prepare-ffmpeg.ts +++ b/prepare-ffmpeg.ts @@ -47,9 +47,9 @@ await run( "--disable-shared", "--enable-static", "--enable-pic", - "--enable-avdevice", - "--enable-avfilter", - "--enable-swscale", + "--disable-avdevice", + "--disable-avfilter", + "--disable-swscale", "--enable-swresample", "--enable-protocol=file", "--enable-demuxer=mov,matroska,ogg,aac", @@ -70,5 +70,5 @@ await copyFile( ) await writeFile( join(prefix, "NOTICE.txt"), - `FFmpeg ${version}\nSource: https://ffmpeg.org/releases/ffmpeg-${version}.tar.xz\nSHA-256: ${checksum}\nConfiguration: see prepare-ffmpeg.ts\n${await readFile(join(source, "COPYING.LGPLv2.1"), "utf8")}`, + `FFmpeg ${version}\nSource: https://ffmpeg.org/releases/ffmpeg-${version}.tar.xz\nSHA-256: ${checksum}\nConfiguration: see apps/cli/prepare-ffmpeg.ts\n${await readFile(join(source, "COPYING.LGPLv2.1"), "utf8")}`, ) diff --git a/publicapi/client.go b/publicapi/client.go deleted file mode 100644 index 7160d20..0000000 --- a/publicapi/client.go +++ /dev/null @@ -1,4544 +0,0 @@ -package publicapi - -import ( - "bufio" - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "math/rand" - "net/http" - "net/url" - "strings" - "sync" - "time" -) - -const ( - maxDecodedBodyBytes = 4 << 20 - maxDiagnosticBodyBytes = 64 << 10 - maxSSEEventBytes = 4 << 20 - defaultResponseTimeout = 30 * time.Second - defaultSSEIdleTimeout = 60 * time.Second - defaultSSEMaxRetries = 5 - defaultSSEReconnectBaseDelay = 200 * time.Millisecond - maxSSERetries = 100 -) - -type HTTPClient interface { - Do(*http.Request) (*http.Response, error) -} - -type HTTPClientFunc func(*http.Request) (*http.Response, error) - -func (f HTTPClientFunc) Do(req *http.Request) (*http.Response, error) { - return f(req) -} - -// RequestEditorFn mutates a request immediately before transport execution. -// The context is the operation context, including its response timeout. -type RequestEditorFn func(context.Context, *http.Request) error - -type Option func(*ClientOptions) - -type ClientOptions struct { - BaseURL string - httpClient HTTPClient - requestEditors []RequestEditorFn - responseTimeout time.Duration - sseIdleTimeout time.Duration - sseMaxRetries int - sseReconnectBaseDelay time.Duration - sseReconnectOnStreamEnd bool -} - -// WithHTTPClient replaces the default http.DefaultClient without mutating a -// caller-owned http.Client. -func WithHTTPClient(client HTTPClient) Option { - return func(options *ClientOptions) { - options.httpClient = client - } -} - -// WithRequestEditorFn appends a client-level request editor. Editors run in -// registration order and must be safe for concurrent use when Client is shared. -func WithRequestEditorFn(editor RequestEditorFn) Option { - return func(options *ClientOptions) { - options.requestEditors = append(options.requestEditors, editor) - } -} - -// WithResponseTimeout bounds response headers and, for ordinary responses, -// reading and decoding the response body. For SSE it only bounds connection -// establishment. Zero disables the timeout. -func WithResponseTimeout(timeout time.Duration) Option { - return func(options *ClientOptions) { - options.responseTimeout = timeout - } -} - -// WithSSEIdleTimeout bounds silence between SSE chunks. Every received chunk, -// including a heartbeat comment, resets it. Zero disables the timeout. -func WithSSEIdleTimeout(timeout time.Duration) Option { - return func(options *ClientOptions) { - options.sseIdleTimeout = timeout - } -} - -// WithSSEMaxRetries bounds consecutive reconnect failures for generated GET -// event streams. Zero disables reconnect attempts; the hard cap is 100. -func WithSSEMaxRetries(retries int) Option { - return func(options *ClientOptions) { - options.sseMaxRetries = retries - } -} - -// WithSSEReconnectBaseDelay configures the first reconnect backoff. -func WithSSEReconnectBaseDelay(delay time.Duration) Option { - return func(options *ClientOptions) { - options.sseReconnectBaseDelay = delay - } -} - -// WithSSEReconnectOnStreamEnd controls whether a clean EOF reconnects a -// generated event stream. It defaults to true for compatibility with streams -// that use EOF as a transient disconnect; terminal-aware proxies can disable it. -func WithSSEReconnectOnStreamEnd(reconnect bool) Option { - return func(options *ClientOptions) { - options.sseReconnectOnStreamEnd = reconnect - } -} - -type Client struct { - baseURL string - httpClient HTTPClient - requestEditors []RequestEditorFn - responseTimeout time.Duration - sseIdleTimeout time.Duration - sseMaxRetries int - sseReconnectBaseDelay time.Duration - sseReconnectOnStreamEnd bool -} - -func NewClient(config ClientOptions, options ...Option) (*Client, error) { - configuredBaseURL := strings.TrimSpace(config.BaseURL) - if configuredBaseURL == "" { - return nil, fmt.Errorf("base URL must not be empty: %q", config.BaseURL) - } - parsed, err := url.Parse(configuredBaseURL) - if err != nil { - return nil, fmt.Errorf("parse base URL %q: %w", config.BaseURL, err) - } - if parsed.Scheme == "" || parsed.Host == "" { - return nil, fmt.Errorf("base URL %q must include scheme and host", config.BaseURL) - } - config.httpClient = http.DefaultClient - config.responseTimeout = defaultResponseTimeout - config.sseIdleTimeout = defaultSSEIdleTimeout - config.sseMaxRetries = defaultSSEMaxRetries - config.sseReconnectBaseDelay = defaultSSEReconnectBaseDelay - config.sseReconnectOnStreamEnd = true - for _, option := range options { - if option == nil { - return nil, fmt.Errorf("client option must not be nil") - } - option(&config) - } - if config.httpClient == nil { - return nil, fmt.Errorf("HTTP client must not be nil") - } - if config.responseTimeout < 0 { - return nil, fmt.Errorf("response timeout must not be negative") - } - if config.sseIdleTimeout < 0 { - return nil, fmt.Errorf("SSE idle timeout must not be negative") - } - if config.sseMaxRetries < 0 || config.sseMaxRetries > maxSSERetries { - return nil, fmt.Errorf("SSE max retries must be between 0 and %d", maxSSERetries) - } - if config.sseReconnectBaseDelay < 0 { - return nil, fmt.Errorf("SSE reconnect base delay must not be negative") - } - for index, editor := range config.requestEditors { - if editor == nil { - return nil, fmt.Errorf("request editor at index %d must not be nil", index) - } - } - return &Client{ - baseURL: strings.TrimRight(parsed.String(), "/"), - httpClient: config.httpClient, - requestEditors: append([]RequestEditorFn(nil), config.requestEditors...), - responseTimeout: config.responseTimeout, - sseIdleTimeout: config.sseIdleTimeout, - sseMaxRetries: config.sseMaxRetries, - sseReconnectBaseDelay: config.sseReconnectBaseDelay, - sseReconnectOnStreamEnd: config.sseReconnectOnStreamEnd, - }, nil -} - -func (c *Client) do(ctx context.Context, request *http.Request) (*http.Response, error) { - for index, editor := range c.requestEditors { - if err := editor(ctx, request); err != nil { - return nil, fmt.Errorf("request editor at index %d: %w", index, err) - } - } - // Keep caller cancellation and response timeout authoritative even if an - // editor replaces the request value or its context. - request = request.Clone(ctx) - return c.httpClient.Do(request) -} - -type ResponseTimeoutError struct { - Duration time.Duration -} - -func (err *ResponseTimeoutError) Error() string { - return fmt.Sprintf("response timed out after %s", err.Duration) -} - -func (err *ResponseTimeoutError) Timeout() bool { return true } - -type responseLifecycle struct { - cancel context.CancelCauseFunc - timer *time.Timer - closeOnce sync.Once -} - -func (c *Client) responseContext(parent context.Context) (context.Context, *responseLifecycle) { - ctx, cancel := context.WithCancelCause(parent) - lifecycle := &responseLifecycle{cancel: cancel} - if c.responseTimeout > 0 { - lifecycle.timer = time.AfterFunc(c.responseTimeout, func() { - cancel(&ResponseTimeoutError{Duration: c.responseTimeout}) - }) - } - return ctx, lifecycle -} - -func (lifecycle *responseLifecycle) stopTimeout() { - if lifecycle.timer != nil { - lifecycle.timer.Stop() - } -} - -func (lifecycle *responseLifecycle) close() { - lifecycle.closeOnce.Do(func() { - lifecycle.stopTimeout() - lifecycle.cancel(context.Canceled) - }) -} - -type UnexpectedStatusError struct { - Method string - URL string - StatusCode int - Body string -} - -func (err *UnexpectedStatusError) Error() string { - if err.Body == "" { - return fmt.Sprintf("%s %s returned unexpected HTTP status %d", err.Method, err.URL, err.StatusCode) - } - return fmt.Sprintf("%s %s returned unexpected HTTP status %d: %s", err.Method, err.URL, err.StatusCode, err.Body) -} - -type sseAttempt struct { - response *http.Response - lifecycle *responseLifecycle -} - -func retryableSSEStatus(status int) bool { - return status == http.StatusBadGateway || status == http.StatusServiceUnavailable || status == http.StatusGatewayTimeout -} - -func sseRetryDelay(base time.Duration, failure int, serverDelay *time.Duration) time.Duration { - if serverDelay != nil { - return *serverDelay - } - scale := 1.0 - for index := 1; index < failure && scale < 1<<20; index++ { - scale *= 2 - } - return time.Duration(float64(base) * scale * (0.9 + rand.Float64()*0.2)) -} - -func waitForSSERetry(ctx context.Context, delay time.Duration) error { - if err := sseContextError(ctx); err != nil { - return err - } - timer := time.NewTimer(maxDuration(delay, 0)) - defer timer.Stop() - select { - case <-ctx.Done(): - return sseContextError(ctx) - case <-timer.C: - return nil - } -} - -func sseContextError(ctx context.Context) error { - if cause := context.Cause(ctx); cause != nil { - return cause - } - return ctx.Err() -} - -func maxDuration(value time.Duration, minimum time.Duration) time.Duration { - if value < minimum { - return minimum - } - return value -} - -func (c *Client) openReconnectingSSE( - ctx context.Context, - open func(context.Context) (*http.Response, *responseLifecycle, error), -) (*sseAttempt, error) { - failures := 0 - for { - if err := sseContextError(ctx); err != nil { - return nil, err - } - response, lifecycle, err := open(ctx) - if err != nil { - if ctx.Err() != nil { - return nil, sseContextError(ctx) - } - if failures >= c.sseMaxRetries { - return nil, err - } - failures++ - if err := waitForSSERetry(ctx, sseRetryDelay(c.sseReconnectBaseDelay, failures, nil)); err != nil { - return nil, err - } - continue - } - attempt := &sseAttempt{response: response, lifecycle: lifecycle} - if !retryableSSEStatus(response.StatusCode) { - return attempt, nil - } - if failures >= c.sseMaxRetries { - return attempt, nil - } - _ = response.Body.Close() - lifecycle.close() - failures++ - if err := waitForSSERetry(ctx, sseRetryDelay(c.sseReconnectBaseDelay, failures, nil)); err != nil { - return nil, err - } - } -} - -type reconnectingSSEBody struct { - ctx context.Context - open func(context.Context) (*http.Response, *responseLifecycle, error) - maxRetries int - baseDelay time.Duration - idleTimeout time.Duration - reconnectOnStreamEnd bool - - mu sync.Mutex - current io.ReadCloser - currentLifecycle *responseLifecycle - closed bool - terminalErr error - failures int - serverDelay *time.Duration - pending []byte - ready [][]byte -} - -func newReconnectingSSEBody( - attempt *sseAttempt, - ctx context.Context, - open func(context.Context) (*http.Response, *responseLifecycle, error), - maxRetries int, - baseDelay time.Duration, - idleTimeout time.Duration, - reconnectOnStreamEnd bool, -) io.ReadCloser { - return &reconnectingSSEBody{ - ctx: ctx, - open: open, - maxRetries: maxRetries, - baseDelay: baseDelay, - idleTimeout: idleTimeout, - reconnectOnStreamEnd: reconnectOnStreamEnd, - current: newSSEIdleBody(attempt.response.Body, idleTimeout), - currentLifecycle: attempt.lifecycle, - } -} - -func newSSEIdleBody(body io.ReadCloser, timeout time.Duration) io.ReadCloser { - if body == nil { - body = http.NoBody - } - return newIdleTimeoutBody(body, timeout, nil) -} - -func (body *reconnectingSSEBody) Read(buffer []byte) (int, error) { - if len(buffer) == 0 { - return 0, nil - } - for { - if read := body.popReady(buffer); read > 0 { - return read, nil - } - body.mu.Lock() - if body.closed { - err := body.terminalErr - body.mu.Unlock() - if err == nil { - return 0, io.EOF - } - return 0, err - } - current := body.current - body.mu.Unlock() - if err := sseContextError(body.ctx); err != nil { - body.fail(err) - return 0, err - } - if current == nil { - err := fmt.Errorf("SSE response has no readable body") - body.fail(err) - return 0, err - } - chunk := make([]byte, 32<<10) - read, err := current.Read(chunk) - if read > 0 { - body.push(chunk[:read]) - if ready := body.popReady(buffer); ready > 0 { - return ready, nil - } - } - if err == nil { - continue - } - if err == io.EOF && !body.reconnectOnStreamEnd { - _ = body.Close() - return 0, io.EOF - } - if reconnectErr := body.reconnect(err); reconnectErr != nil { - body.fail(reconnectErr) - return 0, reconnectErr - } - } -} - -func (body *reconnectingSSEBody) push(chunk []byte) { - body.mu.Lock() - defer body.mu.Unlock() - body.pending = append(body.pending, chunk...) - for { - end := sseFrameEnd(body.pending) - if end < 0 { - if len(body.pending) > maxSSEEventBytes { - body.terminalErr = fmt.Errorf("SSE event exceeds %d bytes", maxSSEEventBytes) - } - return - } - frame := append([]byte(nil), body.pending[:end]...) - body.pending = append([]byte(nil), body.pending[end:]...) - if retry, ok := sseRetryValue(frame); ok { - body.serverDelay = &retry - } - body.failures = 0 - body.ready = append(body.ready, frame) - } -} - -func (body *reconnectingSSEBody) popReady(buffer []byte) int { - body.mu.Lock() - defer body.mu.Unlock() - if len(body.ready) == 0 { - return 0 - } - frame := body.ready[0] - read := copy(buffer, frame) - if read == len(frame) { - body.ready = body.ready[1:] - } else { - body.ready[0] = frame[read:] - } - return read -} - -func (body *reconnectingSSEBody) reconnect(lastErr error) error { - for { - body.mu.Lock() - if body.closed { - err := body.terminalErr - body.mu.Unlock() - if err == nil { - return io.ErrClosedPipe - } - return err - } - if body.failures >= body.maxRetries { - body.mu.Unlock() - return lastErr - } - body.failures++ - failure := body.failures - serverDelay := body.serverDelay - body.serverDelay = nil - current := body.current - lifecycle := body.currentLifecycle - body.current = nil - body.currentLifecycle = nil - body.pending = nil - body.mu.Unlock() - if current != nil { - _ = current.Close() - } - if lifecycle != nil { - lifecycle.close() - } - if err := waitForSSERetry(body.ctx, sseRetryDelay(body.baseDelay, failure, serverDelay)); err != nil { - return err - } - response, nextLifecycle, err := body.open(body.ctx) - if err != nil { - lastErr = err - continue - } - if retryableSSEStatus(response.StatusCode) { - _ = response.Body.Close() - nextLifecycle.close() - lastErr = fmt.Errorf("SSE reconnect returned HTTP %d", response.StatusCode) - body.mu.Lock() - if body.failures >= body.maxRetries { - body.mu.Unlock() - return lastErr - } - body.failures++ - failure = body.failures - body.mu.Unlock() - if err := waitForSSERetry(body.ctx, sseRetryDelay(body.baseDelay, failure, nil)); err != nil { - return err - } - continue - } - if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { - _ = response.Body.Close() - nextLifecycle.close() - return &UnexpectedStatusError{ - Method: response.Request.Method, - URL: response.Request.URL.String(), - StatusCode: response.StatusCode, - } - } - nextBody := newSSEIdleBody(response.Body, body.idleTimeout) - body.mu.Lock() - if body.closed { - body.mu.Unlock() - _ = nextBody.Close() - nextLifecycle.close() - return io.ErrClosedPipe - } - body.current = nextBody - body.currentLifecycle = nextLifecycle - body.mu.Unlock() - return nil - } -} - -func (body *reconnectingSSEBody) fail(err error) { - body.mu.Lock() - if !body.closed { - body.terminalErr = err - body.closed = true - } - body.mu.Unlock() -} - -func (body *reconnectingSSEBody) Close() error { - body.mu.Lock() - if body.closed { - err := body.terminalErr - body.mu.Unlock() - return err - } - body.closed = true - current := body.current - lifecycle := body.currentLifecycle - body.current = nil - body.currentLifecycle = nil - body.mu.Unlock() - if current != nil { - _ = current.Close() - } - if lifecycle != nil { - lifecycle.close() - } - return nil -} - -func sseFrameEnd(bytes []byte) int { - for index := 1; index < len(bytes); index++ { - if bytes[index] == '\n' && (bytes[index-1] == '\n' || (bytes[index-1] == '\r' && index >= 2 && bytes[index-2] == '\n')) { - return index + 1 - } - } - return -1 -} - -func sseRetryValue(frame []byte) (time.Duration, bool) { - for _, line := range strings.Split(strings.ReplaceAll(string(frame), "\r\n", "\n"), "\n") { - if !strings.HasPrefix(line, "retry:") { - continue - } - var millis int64 - if _, err := fmt.Sscanf(strings.TrimSpace(strings.TrimPrefix(line, "retry:")), "%d", &millis); err == nil && millis >= 0 { - return time.Duration(millis) * time.Millisecond, true - } - } - return 0, false -} - -type OpenapiResponse struct { - StatusCode int - Raw *http.Response - Status200 *OpenAPIJSONDocument - Status400 *ValidationErr -} - -func (c *Client) NewOpenapiRequest(ctx context.Context) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build Openapi request: context must not be nil") - } - path := "/" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build Openapi URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build Openapi request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) Openapi(ctx context.Context) (*OpenapiResponse, error) { - - req, err := c.NewOpenapiRequest(ctx) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute Openapi request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute Openapi request: HTTP client returned nil response") - } - - result := &OpenapiResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded OpenAPIJSONDocument - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode Openapi status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode Openapi status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 304: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected Openapi response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type HeadOpenapiResponse struct { - StatusCode int - Raw *http.Response -} - -func (c *Client) NewHeadOpenapiRequest(ctx context.Context) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build HeadOpenapi request: context must not be nil") - } - path := "/" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build HeadOpenapi URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "HEAD", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build HeadOpenapi request: %w", err) - } - req.Header.Set("Accept", "*/*") - return req, nil -} - -func (c *Client) HeadOpenapi(ctx context.Context) (*HeadOpenapiResponse, error) { - - req, err := c.NewHeadOpenapiRequest(ctx) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute HeadOpenapi request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute HeadOpenapi request: HTTP client returned nil response") - } - - result := &HeadOpenapiResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200, 304, 400: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected HeadOpenapi response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateCLIAuthorizationParams struct { - Body CreateCLIAuthorization -} - -type CreateCLIAuthorizationResponse struct { - StatusCode int - Raw *http.Response - Status201 *CreatedCLIAuthorization - Status400 *ValidationErr -} - -func (c *Client) NewCreateCLIAuthorizationRequest(ctx context.Context, params CreateCLIAuthorizationParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateCLIAuthorization request: context must not be nil") - } - path := "/cli/authorizations" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateCLIAuthorization URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CreateCLIAuthorization JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CreateCLIAuthorization request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CreateCLIAuthorization(ctx context.Context, params CreateCLIAuthorizationParams) (*CreateCLIAuthorizationResponse, error) { - - req, err := c.NewCreateCLIAuthorizationRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateCLIAuthorization request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateCLIAuthorization request: HTTP client returned nil response") - } - - result := &CreateCLIAuthorizationResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded CreatedCLIAuthorization - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateCLIAuthorization status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateCLIAuthorization status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateCLIAuthorization response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CliAuthorizationEventsParams struct { - Code CLIAuthorizationCode -} - -type CliAuthorizationEventsResponse struct { - StatusCode int - Raw *http.Response - Status200 *SSEStream[CLIAuthorizationEvent] - Status400 *ValidationErr -} - -func (c *Client) NewCliAuthorizationEventsRequest(ctx context.Context, params CliAuthorizationEventsParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CliAuthorizationEvents request: context must not be nil") - } - if params.Code == "" { - return nil, fmt.Errorf("build CliAuthorizationEvents request: required parameter code is empty") - } - path := "/cli/authorizations/{code}/events" - - path = strings.ReplaceAll(path, "{code}", url.PathEscape(fmt.Sprint(params.Code))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CliAuthorizationEvents URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build CliAuthorizationEvents request: %w", err) - } - req.Header.Set("Accept", "text/event-stream") - return req, nil -} - -func (c *Client) CliAuthorizationEvents(ctx context.Context, params CliAuthorizationEventsParams) (*CliAuthorizationEventsResponse, error) { - - var requestForError *http.Request - open := func(attemptCtx context.Context) (*http.Response, *responseLifecycle, error) { - req, err := c.NewCliAuthorizationEventsRequest(attemptCtx, params) - if err != nil { - return nil, nil, err - } - requestForError = req - responseCtx, lifecycle := c.responseContext(attemptCtx) - req = req.Clone(responseCtx) - res, err := c.do(responseCtx, req) - if err != nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute CliAuthorizationEvents request: %w", err) - } - if res == nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute CliAuthorizationEvents request: HTTP client returned nil response") - } - if res.Request == nil { - res.Request = req - } - return res, lifecycle, nil - } - attempt, err := c.openReconnectingSSE(ctx, open) - if err != nil { - return nil, err - } - res := attempt.response - lifecycle := attempt.lifecycle - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - - result := &CliAuthorizationEventsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - lifecycle.stopTimeout() - keepLifecycle = true - body := newReconnectingSSEBody( - attempt, - ctx, - open, - c.sseMaxRetries, - c.sseReconnectBaseDelay, - c.sseIdleTimeout, - c.sseReconnectOnStreamEnd, - ) - res.Body = body - result.Status200 = newReconnectingSSEStream[CLIAuthorizationEvent](body, lifecycle.close) - - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CliAuthorizationEvents status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CliAuthorizationEvents response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: requestForError.Method, - URL: requestForError.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type EpisodeDeletionEventsParams struct { - EpisodeDeletionRunId EpisodeDeletionRunID -} - -type EpisodeDeletionEventsResponse struct { - StatusCode int - Raw *http.Response - Status200 *SSEStream[EpisodeDeletionEvent] - Status400 *ValidationErr -} - -func (c *Client) NewEpisodeDeletionEventsRequest(ctx context.Context, params EpisodeDeletionEventsParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build EpisodeDeletionEvents request: context must not be nil") - } - if params.EpisodeDeletionRunId == "" { - return nil, fmt.Errorf("build EpisodeDeletionEvents request: required parameter episode_deletion_run_id is empty") - } - path := "/s/episode-deletions/{episode_deletion_run_id}/events" - - path = strings.ReplaceAll(path, "{episode_deletion_run_id}", url.PathEscape(fmt.Sprint(params.EpisodeDeletionRunId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build EpisodeDeletionEvents URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build EpisodeDeletionEvents request: %w", err) - } - req.Header.Set("Accept", "text/event-stream") - return req, nil -} - -func (c *Client) EpisodeDeletionEvents(ctx context.Context, params EpisodeDeletionEventsParams) (*EpisodeDeletionEventsResponse, error) { - - var requestForError *http.Request - open := func(attemptCtx context.Context) (*http.Response, *responseLifecycle, error) { - req, err := c.NewEpisodeDeletionEventsRequest(attemptCtx, params) - if err != nil { - return nil, nil, err - } - requestForError = req - responseCtx, lifecycle := c.responseContext(attemptCtx) - req = req.Clone(responseCtx) - res, err := c.do(responseCtx, req) - if err != nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute EpisodeDeletionEvents request: %w", err) - } - if res == nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute EpisodeDeletionEvents request: HTTP client returned nil response") - } - if res.Request == nil { - res.Request = req - } - return res, lifecycle, nil - } - attempt, err := c.openReconnectingSSE(ctx, open) - if err != nil { - return nil, err - } - res := attempt.response - lifecycle := attempt.lifecycle - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - - result := &EpisodeDeletionEventsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - lifecycle.stopTimeout() - keepLifecycle = true - body := newReconnectingSSEBody( - attempt, - ctx, - open, - c.sseMaxRetries, - c.sseReconnectBaseDelay, - c.sseIdleTimeout, - c.sseReconnectOnStreamEnd, - ) - res.Body = body - result.Status200 = newReconnectingSSEStream[EpisodeDeletionEvent](body, lifecycle.close) - - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode EpisodeDeletionEvents status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected EpisodeDeletionEvents response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: requestForError.Method, - URL: requestForError.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateEpisodePackageParams struct { - Body CreateEpisodePackage -} - -type CreateEpisodePackageResponse struct { - StatusCode int - Raw *http.Response - Status201 *EpisodePackage - Status400 *ValidationErr - Status402 *VideoAdmissionError -} - -func (c *Client) NewCreateEpisodePackageRequest(ctx context.Context, params CreateEpisodePackageParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateEpisodePackage request: context must not be nil") - } - path := "/s/episode-packages" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateEpisodePackage URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CreateEpisodePackage JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CreateEpisodePackage request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CreateEpisodePackage(ctx context.Context, params CreateEpisodePackageParams) (*CreateEpisodePackageResponse, error) { - - req, err := c.NewCreateEpisodePackageRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateEpisodePackage request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateEpisodePackage request: HTTP client returned nil response") - } - - result := &CreateEpisodePackageResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded EpisodePackage - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodePackage status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodePackage status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 402: - var decoded VideoAdmissionError - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodePackage status 402 response: %w", err) - } - _ = res.Body.Close() - result.Status402 = &decoded - return result, nil - case 401, 404, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateEpisodePackage response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CancelEpisodePackageParams struct { - UploadSessionId UploadSessionID -} - -type CancelEpisodePackageResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewCancelEpisodePackageRequest(ctx context.Context, params CancelEpisodePackageParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CancelEpisodePackage request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build CancelEpisodePackage request: required parameter upload_session_id is empty") - } - path := "/s/episode-packages/{upload_session_id}" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CancelEpisodePackage URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "DELETE", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build CancelEpisodePackage request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) CancelEpisodePackage(ctx context.Context, params CancelEpisodePackageParams) (*CancelEpisodePackageResponse, error) { - - req, err := c.NewCancelEpisodePackageRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CancelEpisodePackage request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CancelEpisodePackage request: HTTP client returned nil response") - } - - result := &CancelEpisodePackageResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CancelEpisodePackage status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CancelEpisodePackage response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CompleteEpisodePackageParams struct { - UploadSessionId UploadSessionID -} - -type CompleteEpisodePackageResponse struct { - StatusCode int - Raw *http.Response - Status200 *EpisodePackage - Status400 *ValidationErr -} - -func (c *Client) NewCompleteEpisodePackageRequest(ctx context.Context, params CompleteEpisodePackageParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CompleteEpisodePackage request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build CompleteEpisodePackage request: required parameter upload_session_id is empty") - } - path := "/s/episode-packages/{upload_session_id}/complete" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CompleteEpisodePackage URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build CompleteEpisodePackage request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) CompleteEpisodePackage(ctx context.Context, params CompleteEpisodePackageParams) (*CompleteEpisodePackageResponse, error) { - - req, err := c.NewCompleteEpisodePackageRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CompleteEpisodePackage request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CompleteEpisodePackage request: HTTP client returned nil response") - } - - result := &CompleteEpisodePackageResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded EpisodePackage - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CompleteEpisodePackage status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CompleteEpisodePackage status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CompleteEpisodePackage response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type PresignEpisodePackagePartsParams struct { - UploadSessionId UploadSessionID - ObjectIndex int64 - Body PresignEpisodeUploadSessionParts -} - -type PresignEpisodePackagePartsResponse struct { - StatusCode int - Raw *http.Response - Status200 *PresignedEpisodeUploadParts - Status400 *ValidationErr -} - -func (c *Client) NewPresignEpisodePackagePartsRequest(ctx context.Context, params PresignEpisodePackagePartsParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build PresignEpisodePackageParts request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build PresignEpisodePackageParts request: required parameter upload_session_id is empty") - } - path := "/s/episode-packages/{upload_session_id}/objects/{object_index}/parts" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - path = strings.ReplaceAll(path, "{object_index}", url.PathEscape(fmt.Sprint(params.ObjectIndex))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build PresignEpisodePackageParts URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode PresignEpisodePackageParts JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build PresignEpisodePackageParts request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) PresignEpisodePackageParts(ctx context.Context, params PresignEpisodePackagePartsParams) (*PresignEpisodePackagePartsResponse, error) { - - req, err := c.NewPresignEpisodePackagePartsRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute PresignEpisodePackageParts request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute PresignEpisodePackageParts request: HTTP client returned nil response") - } - - result := &PresignEpisodePackagePartsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded PresignedEpisodeUploadParts - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode PresignEpisodePackageParts status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode PresignEpisodePackageParts status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected PresignEpisodePackageParts response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateEpisodeUploadSessionParams struct { - Body CreateEpisodeUploadSession -} - -type CreateEpisodeUploadSessionResponse struct { - StatusCode int - Raw *http.Response - Status200 *EpisodeUploadSession - Status201 *EpisodeUploadSession - Status400 *ValidationErr -} - -func (c *Client) NewCreateEpisodeUploadSessionRequest(ctx context.Context, params CreateEpisodeUploadSessionParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateEpisodeUploadSession request: context must not be nil") - } - path := "/s/episode-upload-sessions" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateEpisodeUploadSession URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CreateEpisodeUploadSession JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CreateEpisodeUploadSession request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CreateEpisodeUploadSession(ctx context.Context, params CreateEpisodeUploadSessionParams) (*CreateEpisodeUploadSessionResponse, error) { - - req, err := c.NewCreateEpisodeUploadSessionRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateEpisodeUploadSession request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateEpisodeUploadSession request: HTTP client returned nil response") - } - - result := &CreateEpisodeUploadSessionResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded EpisodeUploadSession - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodeUploadSession status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 201: - var decoded EpisodeUploadSession - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodeUploadSession status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodeUploadSession status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404, 409, 410: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateEpisodeUploadSession response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type GetEpisodeUploadSessionParams struct { - UploadSessionId UploadSessionID -} - -type GetEpisodeUploadSessionResponse struct { - StatusCode int - Raw *http.Response - Status200 *EpisodeUploadSession - Status400 *ValidationErr -} - -func (c *Client) NewGetEpisodeUploadSessionRequest(ctx context.Context, params GetEpisodeUploadSessionParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build GetEpisodeUploadSession request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build GetEpisodeUploadSession request: required parameter upload_session_id is empty") - } - path := "/s/episode-upload-sessions/{upload_session_id}" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build GetEpisodeUploadSession URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build GetEpisodeUploadSession request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) GetEpisodeUploadSession(ctx context.Context, params GetEpisodeUploadSessionParams) (*GetEpisodeUploadSessionResponse, error) { - - req, err := c.NewGetEpisodeUploadSessionRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute GetEpisodeUploadSession request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute GetEpisodeUploadSession request: HTTP client returned nil response") - } - - result := &GetEpisodeUploadSessionResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded EpisodeUploadSession - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode GetEpisodeUploadSession status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode GetEpisodeUploadSession status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 404, 410: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected GetEpisodeUploadSession response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type UpdateEpisodeUploadSessionParams struct { - UploadSessionId UploadSessionID - Body PatchEpisodeUploadSession -} - -type UpdateEpisodeUploadSessionResponse struct { - StatusCode int - Raw *http.Response - Status200 *EpisodeUploadSession - Status400 *ValidationErr -} - -func (c *Client) NewUpdateEpisodeUploadSessionRequest(ctx context.Context, params UpdateEpisodeUploadSessionParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build UpdateEpisodeUploadSession request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build UpdateEpisodeUploadSession request: required parameter upload_session_id is empty") - } - path := "/s/episode-upload-sessions/{upload_session_id}" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build UpdateEpisodeUploadSession URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode UpdateEpisodeUploadSession JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "PUT", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build UpdateEpisodeUploadSession request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) UpdateEpisodeUploadSession(ctx context.Context, params UpdateEpisodeUploadSessionParams) (*UpdateEpisodeUploadSessionResponse, error) { - - req, err := c.NewUpdateEpisodeUploadSessionRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute UpdateEpisodeUploadSession request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute UpdateEpisodeUploadSession request: HTTP client returned nil response") - } - - result := &UpdateEpisodeUploadSessionResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded EpisodeUploadSession - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateEpisodeUploadSession status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateEpisodeUploadSession status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 404, 409, 410: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected UpdateEpisodeUploadSession response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CancelEpisodeUploadSessionParams struct { - UploadSessionId UploadSessionID -} - -type CancelEpisodeUploadSessionResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewCancelEpisodeUploadSessionRequest(ctx context.Context, params CancelEpisodeUploadSessionParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CancelEpisodeUploadSession request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build CancelEpisodeUploadSession request: required parameter upload_session_id is empty") - } - path := "/s/episode-upload-sessions/{upload_session_id}" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CancelEpisodeUploadSession URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "DELETE", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build CancelEpisodeUploadSession request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) CancelEpisodeUploadSession(ctx context.Context, params CancelEpisodeUploadSessionParams) (*CancelEpisodeUploadSessionResponse, error) { - - req, err := c.NewCancelEpisodeUploadSessionRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CancelEpisodeUploadSession request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CancelEpisodeUploadSession request: HTTP client returned nil response") - } - - result := &CancelEpisodeUploadSessionResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CancelEpisodeUploadSession status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 404, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CancelEpisodeUploadSession response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CompleteEpisodeUploadSessionParams struct { - UploadSessionId UploadSessionID - Body CompleteEpisodeUploadSession -} - -type CompleteEpisodeUploadSessionResponse struct { - StatusCode int - Raw *http.Response - Status200 *Episode - Status201 *Episode - Status400 *ValidationErr -} - -func (c *Client) NewCompleteEpisodeUploadSessionRequest(ctx context.Context, params CompleteEpisodeUploadSessionParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CompleteEpisodeUploadSession request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build CompleteEpisodeUploadSession request: required parameter upload_session_id is empty") - } - path := "/s/episode-upload-sessions/{upload_session_id}/complete" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CompleteEpisodeUploadSession URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CompleteEpisodeUploadSession JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CompleteEpisodeUploadSession request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CompleteEpisodeUploadSession(ctx context.Context, params CompleteEpisodeUploadSessionParams) (*CompleteEpisodeUploadSessionResponse, error) { - - req, err := c.NewCompleteEpisodeUploadSessionRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CompleteEpisodeUploadSession request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CompleteEpisodeUploadSession request: HTTP client returned nil response") - } - - result := &CompleteEpisodeUploadSessionResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded Episode - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CompleteEpisodeUploadSession status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 201: - var decoded Episode - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CompleteEpisodeUploadSession status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CompleteEpisodeUploadSession status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404, 409, 410: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CompleteEpisodeUploadSession response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type EpisodeUploadSessionEventsParams struct { - UploadSessionId UploadSessionID -} - -type EpisodeUploadSessionEventsResponse struct { - StatusCode int - Raw *http.Response - Status200 *SSEStream[EpisodeProcessingEvent] - Status400 *ValidationErr -} - -func (c *Client) NewEpisodeUploadSessionEventsRequest(ctx context.Context, params EpisodeUploadSessionEventsParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build EpisodeUploadSessionEvents request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build EpisodeUploadSessionEvents request: required parameter upload_session_id is empty") - } - path := "/s/episode-upload-sessions/{upload_session_id}/events" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build EpisodeUploadSessionEvents URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build EpisodeUploadSessionEvents request: %w", err) - } - req.Header.Set("Accept", "text/event-stream") - return req, nil -} - -func (c *Client) EpisodeUploadSessionEvents(ctx context.Context, params EpisodeUploadSessionEventsParams) (*EpisodeUploadSessionEventsResponse, error) { - - var requestForError *http.Request - open := func(attemptCtx context.Context) (*http.Response, *responseLifecycle, error) { - req, err := c.NewEpisodeUploadSessionEventsRequest(attemptCtx, params) - if err != nil { - return nil, nil, err - } - requestForError = req - responseCtx, lifecycle := c.responseContext(attemptCtx) - req = req.Clone(responseCtx) - res, err := c.do(responseCtx, req) - if err != nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute EpisodeUploadSessionEvents request: %w", err) - } - if res == nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute EpisodeUploadSessionEvents request: HTTP client returned nil response") - } - if res.Request == nil { - res.Request = req - } - return res, lifecycle, nil - } - attempt, err := c.openReconnectingSSE(ctx, open) - if err != nil { - return nil, err - } - res := attempt.response - lifecycle := attempt.lifecycle - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - - result := &EpisodeUploadSessionEventsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - lifecycle.stopTimeout() - keepLifecycle = true - body := newReconnectingSSEBody( - attempt, - ctx, - open, - c.sseMaxRetries, - c.sseReconnectBaseDelay, - c.sseIdleTimeout, - c.sseReconnectOnStreamEnd, - ) - res.Body = body - result.Status200 = newReconnectingSSEStream[EpisodeProcessingEvent](body, lifecycle.close) - - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode EpisodeUploadSessionEvents status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected EpisodeUploadSessionEvents response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: requestForError.Method, - URL: requestForError.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type PresignEpisodeUploadSessionPartsParams struct { - UploadSessionId UploadSessionID - Body PresignEpisodeUploadSessionParts -} - -type PresignEpisodeUploadSessionPartsResponse struct { - StatusCode int - Raw *http.Response - Status201 *PresignedEpisodeUploadParts - Status400 *ValidationErr -} - -func (c *Client) NewPresignEpisodeUploadSessionPartsRequest(ctx context.Context, params PresignEpisodeUploadSessionPartsParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build PresignEpisodeUploadSessionParts request: context must not be nil") - } - if params.UploadSessionId == "" { - return nil, fmt.Errorf("build PresignEpisodeUploadSessionParts request: required parameter upload_session_id is empty") - } - path := "/s/episode-upload-sessions/{upload_session_id}/parts/presign" - - path = strings.ReplaceAll(path, "{upload_session_id}", url.PathEscape(fmt.Sprint(params.UploadSessionId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build PresignEpisodeUploadSessionParts URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode PresignEpisodeUploadSessionParts JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build PresignEpisodeUploadSessionParts request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) PresignEpisodeUploadSessionParts(ctx context.Context, params PresignEpisodeUploadSessionPartsParams) (*PresignEpisodeUploadSessionPartsResponse, error) { - - req, err := c.NewPresignEpisodeUploadSessionPartsRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute PresignEpisodeUploadSessionParts request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute PresignEpisodeUploadSessionParts request: HTTP client returned nil response") - } - - result := &PresignEpisodeUploadSessionPartsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded PresignedEpisodeUploadParts - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode PresignEpisodeUploadSessionParts status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode PresignEpisodeUploadSessionParts status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 402, 404, 409, 410: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected PresignEpisodeUploadSessionParts response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateEpisodeDeletionParams struct { - EpisodeId EpisodeID -} - -type CreateEpisodeDeletionResponse struct { - StatusCode int - Raw *http.Response - Status202 *CreatedPublicEpisodeDeletion - Status400 *ValidationErr -} - -func (c *Client) NewCreateEpisodeDeletionRequest(ctx context.Context, params CreateEpisodeDeletionParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateEpisodeDeletion request: context must not be nil") - } - if params.EpisodeId == "" { - return nil, fmt.Errorf("build CreateEpisodeDeletion request: required parameter episode_id is empty") - } - path := "/s/episodes/{episode_id}/deletions" - - path = strings.ReplaceAll(path, "{episode_id}", url.PathEscape(fmt.Sprint(params.EpisodeId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateEpisodeDeletion URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build CreateEpisodeDeletion request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) CreateEpisodeDeletion(ctx context.Context, params CreateEpisodeDeletionParams) (*CreateEpisodeDeletionResponse, error) { - - req, err := c.NewCreateEpisodeDeletionRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateEpisodeDeletion request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateEpisodeDeletion request: HTTP client returned nil response") - } - - result := &CreateEpisodeDeletionResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 202: - var decoded CreatedPublicEpisodeDeletion - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodeDeletion status 202 response: %w", err) - } - _ = res.Body.Close() - result.Status202 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateEpisodeDeletion status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateEpisodeDeletion response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateImageUploadPresignParams struct { - Body CreateImageUploadPresign -} - -type CreateImageUploadPresignResponse struct { - StatusCode int - Raw *http.Response - Status201 *CreatedImageUploadPresign -} - -func (c *Client) NewCreateImageUploadPresignRequest(ctx context.Context, params CreateImageUploadPresignParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateImageUploadPresign request: context must not be nil") - } - path := "/s/image-uploads/presign" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateImageUploadPresign URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CreateImageUploadPresign JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CreateImageUploadPresign request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CreateImageUploadPresign(ctx context.Context, params CreateImageUploadPresignParams) (*CreateImageUploadPresignResponse, error) { - - req, err := c.NewCreateImageUploadPresignRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateImageUploadPresign request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateImageUploadPresign request: HTTP client returned nil response") - } - - result := &CreateImageUploadPresignResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded CreatedImageUploadPresign - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateImageUploadPresign status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400, 401, 402, 403, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateImageUploadPresign response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CompleteImageUploadParams struct { - ImageAssetId ImageAssetID - Body CompleteImageUpload -} - -type CompleteImageUploadResponse struct { - StatusCode int - Raw *http.Response - Status201 *ImageAsset -} - -func (c *Client) NewCompleteImageUploadRequest(ctx context.Context, params CompleteImageUploadParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CompleteImageUpload request: context must not be nil") - } - if params.ImageAssetId == "" { - return nil, fmt.Errorf("build CompleteImageUpload request: required parameter image_asset_id is empty") - } - path := "/s/image-uploads/{image_asset_id}/complete" - - path = strings.ReplaceAll(path, "{image_asset_id}", url.PathEscape(fmt.Sprint(params.ImageAssetId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CompleteImageUpload URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CompleteImageUpload JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CompleteImageUpload request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CompleteImageUpload(ctx context.Context, params CompleteImageUploadParams) (*CompleteImageUploadResponse, error) { - - req, err := c.NewCompleteImageUploadRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CompleteImageUpload request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CompleteImageUpload request: HTTP client returned nil response") - } - - result := &CompleteImageUploadResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded ImageAsset - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CompleteImageUpload status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400, 401, 403, 404, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CompleteImageUpload response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type ImportRSSParams struct { - Body ImportRSSRequest -} - -type ImportRSSResponse struct { - StatusCode int - Raw *http.Response - Status202 *CreatedPublicRSSImport - Status400 *ValidationErr - Status402 *RSSImportEntitlementError -} - -func (c *Client) NewImportRSSRequest(ctx context.Context, params ImportRSSParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build ImportRSS request: context must not be nil") - } - path := "/s/rss-imports" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build ImportRSS URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode ImportRSS JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build ImportRSS request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) ImportRSS(ctx context.Context, params ImportRSSParams) (*ImportRSSResponse, error) { - - req, err := c.NewImportRSSRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute ImportRSS request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute ImportRSS request: HTTP client returned nil response") - } - - result := &ImportRSSResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 202: - var decoded CreatedPublicRSSImport - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ImportRSS status 202 response: %w", err) - } - _ = res.Body.Close() - result.Status202 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ImportRSS status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 402: - var decoded RSSImportEntitlementError - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ImportRSS status 402 response: %w", err) - } - _ = res.Body.Close() - result.Status402 = &decoded - return result, nil - case 401, 403, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected ImportRSS response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type ImportRSSRunEventsParams struct { - ImportRunId ImportRunID -} - -type ImportRSSRunEventsResponse struct { - StatusCode int - Raw *http.Response - Status200 *SSEStream[PublicRSSImportEvent] - Status400 *ValidationErr -} - -func (c *Client) NewImportRSSRunEventsRequest(ctx context.Context, params ImportRSSRunEventsParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build ImportRSSRunEvents request: context must not be nil") - } - if params.ImportRunId == "" { - return nil, fmt.Errorf("build ImportRSSRunEvents request: required parameter import_run_id is empty") - } - path := "/s/rss-imports/{import_run_id}/events" - - path = strings.ReplaceAll(path, "{import_run_id}", url.PathEscape(fmt.Sprint(params.ImportRunId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build ImportRSSRunEvents URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build ImportRSSRunEvents request: %w", err) - } - req.Header.Set("Accept", "text/event-stream") - return req, nil -} - -func (c *Client) ImportRSSRunEvents(ctx context.Context, params ImportRSSRunEventsParams) (*ImportRSSRunEventsResponse, error) { - - var requestForError *http.Request - open := func(attemptCtx context.Context) (*http.Response, *responseLifecycle, error) { - req, err := c.NewImportRSSRunEventsRequest(attemptCtx, params) - if err != nil { - return nil, nil, err - } - requestForError = req - responseCtx, lifecycle := c.responseContext(attemptCtx) - req = req.Clone(responseCtx) - res, err := c.do(responseCtx, req) - if err != nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute ImportRSSRunEvents request: %w", err) - } - if res == nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute ImportRSSRunEvents request: HTTP client returned nil response") - } - if res.Request == nil { - res.Request = req - } - return res, lifecycle, nil - } - attempt, err := c.openReconnectingSSE(ctx, open) - if err != nil { - return nil, err - } - res := attempt.response - lifecycle := attempt.lifecycle - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - - result := &ImportRSSRunEventsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - lifecycle.stopTimeout() - keepLifecycle = true - body := newReconnectingSSEBody( - attempt, - ctx, - open, - c.sseMaxRetries, - c.sseReconnectBaseDelay, - c.sseIdleTimeout, - c.sseReconnectOnStreamEnd, - ) - res.Body = body - result.Status200 = newReconnectingSSEStream[PublicRSSImportEvent](body, lifecycle.close) - - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ImportRSSRunEvents status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected ImportRSSRunEvents response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: requestForError.Method, - URL: requestForError.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type ShowDeletionEventsParams struct { - ShowDeletionRunId ShowDeletionRunID -} - -type ShowDeletionEventsResponse struct { - StatusCode int - Raw *http.Response - Status200 *SSEStream[ShowDeletionEvent] - Status400 *ValidationErr -} - -func (c *Client) NewShowDeletionEventsRequest(ctx context.Context, params ShowDeletionEventsParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build ShowDeletionEvents request: context must not be nil") - } - if params.ShowDeletionRunId == "" { - return nil, fmt.Errorf("build ShowDeletionEvents request: required parameter show_deletion_run_id is empty") - } - path := "/s/show-deletions/{show_deletion_run_id}/events" - - path = strings.ReplaceAll(path, "{show_deletion_run_id}", url.PathEscape(fmt.Sprint(params.ShowDeletionRunId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build ShowDeletionEvents URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build ShowDeletionEvents request: %w", err) - } - req.Header.Set("Accept", "text/event-stream") - return req, nil -} - -func (c *Client) ShowDeletionEvents(ctx context.Context, params ShowDeletionEventsParams) (*ShowDeletionEventsResponse, error) { - - var requestForError *http.Request - open := func(attemptCtx context.Context) (*http.Response, *responseLifecycle, error) { - req, err := c.NewShowDeletionEventsRequest(attemptCtx, params) - if err != nil { - return nil, nil, err - } - requestForError = req - responseCtx, lifecycle := c.responseContext(attemptCtx) - req = req.Clone(responseCtx) - res, err := c.do(responseCtx, req) - if err != nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute ShowDeletionEvents request: %w", err) - } - if res == nil { - lifecycle.close() - return nil, nil, fmt.Errorf("execute ShowDeletionEvents request: HTTP client returned nil response") - } - if res.Request == nil { - res.Request = req - } - return res, lifecycle, nil - } - attempt, err := c.openReconnectingSSE(ctx, open) - if err != nil { - return nil, err - } - res := attempt.response - lifecycle := attempt.lifecycle - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - - result := &ShowDeletionEventsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - lifecycle.stopTimeout() - keepLifecycle = true - body := newReconnectingSSEBody( - attempt, - ctx, - open, - c.sseMaxRetries, - c.sseReconnectBaseDelay, - c.sseIdleTimeout, - c.sseReconnectOnStreamEnd, - ) - res.Body = body - result.Status200 = newReconnectingSSEStream[ShowDeletionEvent](body, lifecycle.close) - - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ShowDeletionEvents status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected ShowDeletionEvents response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: requestForError.Method, - URL: requestForError.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type ListShowsResponse struct { - StatusCode int - Raw *http.Response - Status200 *[]Show - Status400 *ValidationErr -} - -func (c *Client) NewListShowsRequest(ctx context.Context) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build ListShows request: context must not be nil") - } - path := "/s/shows" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build ListShows URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build ListShows request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) ListShows(ctx context.Context) (*ListShowsResponse, error) { - - req, err := c.NewListShowsRequest(ctx) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute ListShows request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute ListShows request: HTTP client returned nil response") - } - - result := &ListShowsResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded []Show - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListShows status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListShows status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected ListShows response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateShowParams struct { - Body CreateShow -} - -type CreateShowResponse struct { - StatusCode int - Raw *http.Response - Status201 *Show - Status400 *ValidationErr -} - -func (c *Client) NewCreateShowRequest(ctx context.Context, params CreateShowParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateShow request: context must not be nil") - } - path := "/s/shows" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateShow URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CreateShow JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CreateShow request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CreateShow(ctx context.Context, params CreateShowParams) (*CreateShowResponse, error) { - - req, err := c.NewCreateShowRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateShow request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateShow request: HTTP client returned nil response") - } - - result := &CreateShowResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded Show - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateShow status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateShow status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateShow response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateShowDeletionParams struct { - ShowSlug ShowSlug -} - -type CreateShowDeletionResponse struct { - StatusCode int - Raw *http.Response - Status202 *CreatedPublicShowDeletion - Status400 *ValidationErr -} - -func (c *Client) NewCreateShowDeletionRequest(ctx context.Context, params CreateShowDeletionParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateShowDeletion request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build CreateShowDeletion request: required parameter show_slug is empty") - } - path := "/s/shows/{show_slug}/deletions" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateShowDeletion URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build CreateShowDeletion request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) CreateShowDeletion(ctx context.Context, params CreateShowDeletionParams) (*CreateShowDeletionResponse, error) { - - req, err := c.NewCreateShowDeletionRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateShowDeletion request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateShowDeletion request: HTTP client returned nil response") - } - - result := &CreateShowDeletionResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 202: - var decoded CreatedPublicShowDeletion - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateShowDeletion status 202 response: %w", err) - } - _ = res.Body.Close() - result.Status202 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateShowDeletion status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateShowDeletion response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type ListEpisodesParams struct { - ShowSlug ShowSlug - Limit *EpisodePageLimit - Cursor *EpisodePageCursor -} - -type ListEpisodesResponse struct { - StatusCode int - Raw *http.Response - Status200 *EpisodePage - Status400 *ValidationErr -} - -func (c *Client) NewListEpisodesRequest(ctx context.Context, params ListEpisodesParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build ListEpisodes request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build ListEpisodes request: required parameter show_slug is empty") - } - path := "/s/shows/{show_slug}/episodes" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build ListEpisodes URL: %w", err) - } - query := endpoint.Query() - if params.Limit != nil { - query.Set("limit", fmt.Sprint(*params.Limit)) - } - if params.Cursor != nil { - query.Set("cursor", fmt.Sprint(*params.Cursor)) - } - endpoint.RawQuery = query.Encode() - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build ListEpisodes request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) ListEpisodes(ctx context.Context, params ListEpisodesParams) (*ListEpisodesResponse, error) { - - req, err := c.NewListEpisodesRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute ListEpisodes request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute ListEpisodes request: HTTP client returned nil response") - } - - result := &ListEpisodesResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded EpisodePage - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListEpisodes status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListEpisodes status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected ListEpisodes response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateShowInvitationParams struct { - ShowSlug ShowSlug - Body CreateShowInvitation -} - -type CreateShowInvitationResponse struct { - StatusCode int - Raw *http.Response - Status201 *PendingShowInvitation - Status400 *ValidationErr -} - -func (c *Client) NewCreateShowInvitationRequest(ctx context.Context, params CreateShowInvitationParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateShowInvitation request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build CreateShowInvitation request: required parameter show_slug is empty") - } - path := "/s/shows/{show_slug}/invitations" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateShowInvitation URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CreateShowInvitation JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CreateShowInvitation request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CreateShowInvitation(ctx context.Context, params CreateShowInvitationParams) (*CreateShowInvitationResponse, error) { - - req, err := c.NewCreateShowInvitationRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateShowInvitation request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateShowInvitation request: HTTP client returned nil response") - } - - result := &CreateShowInvitationResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded PendingShowInvitation - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateShowInvitation status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateShowInvitation status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateShowInvitation response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type UpdateShowInvitationRoleParams struct { - ShowSlug ShowSlug - InvitationId ShowInvitationID - Body UpdateShowMemberRole -} - -type UpdateShowInvitationRoleResponse struct { - StatusCode int - Raw *http.Response - Status200 *PendingShowInvitation - Status400 *ValidationErr -} - -func (c *Client) NewUpdateShowInvitationRoleRequest(ctx context.Context, params UpdateShowInvitationRoleParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build UpdateShowInvitationRole request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build UpdateShowInvitationRole request: required parameter show_slug is empty") - } - if params.InvitationId == "" { - return nil, fmt.Errorf("build UpdateShowInvitationRole request: required parameter invitation_id is empty") - } - path := "/s/shows/{show_slug}/invitations/{invitation_id}" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - path = strings.ReplaceAll(path, "{invitation_id}", url.PathEscape(fmt.Sprint(params.InvitationId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build UpdateShowInvitationRole URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode UpdateShowInvitationRole JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "PUT", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build UpdateShowInvitationRole request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) UpdateShowInvitationRole(ctx context.Context, params UpdateShowInvitationRoleParams) (*UpdateShowInvitationRoleResponse, error) { - - req, err := c.NewUpdateShowInvitationRoleRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute UpdateShowInvitationRole request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute UpdateShowInvitationRole request: HTTP client returned nil response") - } - - result := &UpdateShowInvitationRoleResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded PendingShowInvitation - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateShowInvitationRole status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateShowInvitationRole status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected UpdateShowInvitationRole response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type RevokeShowInvitationParams struct { - ShowSlug ShowSlug - InvitationId ShowInvitationID -} - -type RevokeShowInvitationResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewRevokeShowInvitationRequest(ctx context.Context, params RevokeShowInvitationParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build RevokeShowInvitation request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build RevokeShowInvitation request: required parameter show_slug is empty") - } - if params.InvitationId == "" { - return nil, fmt.Errorf("build RevokeShowInvitation request: required parameter invitation_id is empty") - } - path := "/s/shows/{show_slug}/invitations/{invitation_id}" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - path = strings.ReplaceAll(path, "{invitation_id}", url.PathEscape(fmt.Sprint(params.InvitationId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build RevokeShowInvitation URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "DELETE", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build RevokeShowInvitation request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) RevokeShowInvitation(ctx context.Context, params RevokeShowInvitationParams) (*RevokeShowInvitationResponse, error) { - - req, err := c.NewRevokeShowInvitationRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute RevokeShowInvitation request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute RevokeShowInvitation request: HTTP client returned nil response") - } - - result := &RevokeShowInvitationResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode RevokeShowInvitation status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected RevokeShowInvitation response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type ListShowMembersParams struct { - ShowSlug ShowSlug -} - -type ListShowMembersResponse struct { - StatusCode int - Raw *http.Response - Status200 *ShowMemberCollection - Status400 *ValidationErr -} - -func (c *Client) NewListShowMembersRequest(ctx context.Context, params ListShowMembersParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build ListShowMembers request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build ListShowMembers request: required parameter show_slug is empty") - } - path := "/s/shows/{show_slug}/members" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build ListShowMembers URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build ListShowMembers request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) ListShowMembers(ctx context.Context, params ListShowMembersParams) (*ListShowMembersResponse, error) { - - req, err := c.NewListShowMembersRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute ListShowMembers request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute ListShowMembers request: HTTP client returned nil response") - } - - result := &ListShowMembersResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded ShowMemberCollection - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListShowMembers status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListShowMembers status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected ListShowMembers response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type UpdateShowMemberRoleParams struct { - ShowSlug ShowSlug - UserId UserID - Body UpdateShowMemberRole -} - -type UpdateShowMemberRoleResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewUpdateShowMemberRoleRequest(ctx context.Context, params UpdateShowMemberRoleParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build UpdateShowMemberRole request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build UpdateShowMemberRole request: required parameter show_slug is empty") - } - if params.UserId == "" { - return nil, fmt.Errorf("build UpdateShowMemberRole request: required parameter user_id is empty") - } - path := "/s/shows/{show_slug}/members/{user_id}" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - path = strings.ReplaceAll(path, "{user_id}", url.PathEscape(fmt.Sprint(params.UserId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build UpdateShowMemberRole URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode UpdateShowMemberRole JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "PUT", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build UpdateShowMemberRole request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) UpdateShowMemberRole(ctx context.Context, params UpdateShowMemberRoleParams) (*UpdateShowMemberRoleResponse, error) { - - req, err := c.NewUpdateShowMemberRoleRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute UpdateShowMemberRole request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute UpdateShowMemberRole request: HTTP client returned nil response") - } - - result := &UpdateShowMemberRoleResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateShowMemberRole status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected UpdateShowMemberRole response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type RemoveShowMemberParams struct { - ShowSlug ShowSlug - UserId UserID -} - -type RemoveShowMemberResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewRemoveShowMemberRequest(ctx context.Context, params RemoveShowMemberParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build RemoveShowMember request: context must not be nil") - } - if params.ShowSlug == "" { - return nil, fmt.Errorf("build RemoveShowMember request: required parameter show_slug is empty") - } - if params.UserId == "" { - return nil, fmt.Errorf("build RemoveShowMember request: required parameter user_id is empty") - } - path := "/s/shows/{show_slug}/members/{user_id}" - - path = strings.ReplaceAll(path, "{show_slug}", url.PathEscape(fmt.Sprint(params.ShowSlug))) - path = strings.ReplaceAll(path, "{user_id}", url.PathEscape(fmt.Sprint(params.UserId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build RemoveShowMember URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "DELETE", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build RemoveShowMember request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) RemoveShowMember(ctx context.Context, params RemoveShowMemberParams) (*RemoveShowMemberResponse, error) { - - req, err := c.NewRemoveShowMemberRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute RemoveShowMember request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute RemoveShowMember request: HTTP client returned nil response") - } - - result := &RemoveShowMemberResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode RemoveShowMember status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected RemoveShowMember response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type CreateTeamInvitationParams struct { - Body CreateTeamInvitation -} - -type CreateTeamInvitationResponse struct { - StatusCode int - Raw *http.Response - Status201 *PendingTeamInvitation - Status400 *ValidationErr -} - -func (c *Client) NewCreateTeamInvitationRequest(ctx context.Context, params CreateTeamInvitationParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build CreateTeamInvitation request: context must not be nil") - } - path := "/s/team/invitations" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build CreateTeamInvitation URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode CreateTeamInvitation JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "POST", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build CreateTeamInvitation request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) CreateTeamInvitation(ctx context.Context, params CreateTeamInvitationParams) (*CreateTeamInvitationResponse, error) { - - req, err := c.NewCreateTeamInvitationRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute CreateTeamInvitation request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute CreateTeamInvitation request: HTTP client returned nil response") - } - - result := &CreateTeamInvitationResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 201: - var decoded PendingTeamInvitation - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateTeamInvitation status 201 response: %w", err) - } - _ = res.Body.Close() - result.Status201 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode CreateTeamInvitation status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 409: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected CreateTeamInvitation response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type UpdateTeamInvitationRoleParams struct { - InvitationId TeamInvitationID - Body UpdateTeamMemberRole -} - -type UpdateTeamInvitationRoleResponse struct { - StatusCode int - Raw *http.Response - Status200 *PendingTeamInvitation - Status400 *ValidationErr -} - -func (c *Client) NewUpdateTeamInvitationRoleRequest(ctx context.Context, params UpdateTeamInvitationRoleParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build UpdateTeamInvitationRole request: context must not be nil") - } - if params.InvitationId == "" { - return nil, fmt.Errorf("build UpdateTeamInvitationRole request: required parameter invitation_id is empty") - } - path := "/s/team/invitations/{invitation_id}" - - path = strings.ReplaceAll(path, "{invitation_id}", url.PathEscape(fmt.Sprint(params.InvitationId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build UpdateTeamInvitationRole URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode UpdateTeamInvitationRole JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "PUT", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build UpdateTeamInvitationRole request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) UpdateTeamInvitationRole(ctx context.Context, params UpdateTeamInvitationRoleParams) (*UpdateTeamInvitationRoleResponse, error) { - - req, err := c.NewUpdateTeamInvitationRoleRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute UpdateTeamInvitationRole request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute UpdateTeamInvitationRole request: HTTP client returned nil response") - } - - result := &UpdateTeamInvitationRoleResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded PendingTeamInvitation - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateTeamInvitationRole status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateTeamInvitationRole status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected UpdateTeamInvitationRole response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type RevokeTeamInvitationParams struct { - InvitationId TeamInvitationID -} - -type RevokeTeamInvitationResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewRevokeTeamInvitationRequest(ctx context.Context, params RevokeTeamInvitationParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build RevokeTeamInvitation request: context must not be nil") - } - if params.InvitationId == "" { - return nil, fmt.Errorf("build RevokeTeamInvitation request: required parameter invitation_id is empty") - } - path := "/s/team/invitations/{invitation_id}" - - path = strings.ReplaceAll(path, "{invitation_id}", url.PathEscape(fmt.Sprint(params.InvitationId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build RevokeTeamInvitation URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "DELETE", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build RevokeTeamInvitation request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) RevokeTeamInvitation(ctx context.Context, params RevokeTeamInvitationParams) (*RevokeTeamInvitationResponse, error) { - - req, err := c.NewRevokeTeamInvitationRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute RevokeTeamInvitation request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute RevokeTeamInvitation request: HTTP client returned nil response") - } - - result := &RevokeTeamInvitationResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode RevokeTeamInvitation status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected RevokeTeamInvitation response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type ListTeamMembersResponse struct { - StatusCode int - Raw *http.Response - Status200 *TeamMemberCollection - Status400 *ValidationErr -} - -func (c *Client) NewListTeamMembersRequest(ctx context.Context) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build ListTeamMembers request: context must not be nil") - } - path := "/s/team/members" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build ListTeamMembers URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build ListTeamMembers request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) ListTeamMembers(ctx context.Context) (*ListTeamMembersResponse, error) { - - req, err := c.NewListTeamMembersRequest(ctx) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute ListTeamMembers request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute ListTeamMembers request: HTTP client returned nil response") - } - - result := &ListTeamMembersResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded TeamMemberCollection - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListTeamMembers status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode ListTeamMembers status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401, 403: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected ListTeamMembers response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type UpdateTeamMemberRoleParams struct { - UserId UserID - Body UpdateTeamMemberRole -} - -type UpdateTeamMemberRoleResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewUpdateTeamMemberRoleRequest(ctx context.Context, params UpdateTeamMemberRoleParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build UpdateTeamMemberRole request: context must not be nil") - } - if params.UserId == "" { - return nil, fmt.Errorf("build UpdateTeamMemberRole request: required parameter user_id is empty") - } - path := "/s/team/members/{user_id}" - - path = strings.ReplaceAll(path, "{user_id}", url.PathEscape(fmt.Sprint(params.UserId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build UpdateTeamMemberRole URL: %w", err) - } - encodedBody, err := json.Marshal(params.Body) - if err != nil { - return nil, fmt.Errorf("encode UpdateTeamMemberRole JSON body: %w", err) - } - requestBody := bytes.NewReader(encodedBody) - req, err := http.NewRequestWithContext(ctx, "PUT", endpoint.String(), requestBody) - if err != nil { - return nil, fmt.Errorf("build UpdateTeamMemberRole request: %w", err) - } - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - return req, nil -} - -func (c *Client) UpdateTeamMemberRole(ctx context.Context, params UpdateTeamMemberRoleParams) (*UpdateTeamMemberRoleResponse, error) { - - req, err := c.NewUpdateTeamMemberRoleRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute UpdateTeamMemberRole request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute UpdateTeamMemberRole request: HTTP client returned nil response") - } - - result := &UpdateTeamMemberRoleResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode UpdateTeamMemberRole status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected UpdateTeamMemberRole response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type RemoveTeamMemberParams struct { - UserId UserID -} - -type RemoveTeamMemberResponse struct { - StatusCode int - Raw *http.Response - Status400 *ValidationErr -} - -func (c *Client) NewRemoveTeamMemberRequest(ctx context.Context, params RemoveTeamMemberParams) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build RemoveTeamMember request: context must not be nil") - } - if params.UserId == "" { - return nil, fmt.Errorf("build RemoveTeamMember request: required parameter user_id is empty") - } - path := "/s/team/members/{user_id}" - - path = strings.ReplaceAll(path, "{user_id}", url.PathEscape(fmt.Sprint(params.UserId))) - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build RemoveTeamMember URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "DELETE", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build RemoveTeamMember request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) RemoveTeamMember(ctx context.Context, params RemoveTeamMemberParams) (*RemoveTeamMemberResponse, error) { - - req, err := c.NewRemoveTeamMemberRequest(ctx, params) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute RemoveTeamMember request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute RemoveTeamMember request: HTTP client returned nil response") - } - - result := &RemoveTeamMemberResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode RemoveTeamMember status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 204, 401, 403, 404: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected RemoveTeamMember response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type WhoamiResponse struct { - StatusCode int - Raw *http.Response - Status200 *APIKeyWhoami - Status400 *ValidationErr -} - -func (c *Client) NewWhoamiRequest(ctx context.Context) (*http.Request, error) { - if ctx == nil { - return nil, fmt.Errorf("build Whoami request: context must not be nil") - } - path := "/s/whoami" - - endpoint, err := url.Parse(c.baseURL + path) - if err != nil { - return nil, fmt.Errorf("build Whoami URL: %w", err) - } - req, err := http.NewRequestWithContext(ctx, "GET", endpoint.String(), nil) - if err != nil { - return nil, fmt.Errorf("build Whoami request: %w", err) - } - req.Header.Set("Accept", "application/json") - return req, nil -} - -func (c *Client) Whoami(ctx context.Context) (*WhoamiResponse, error) { - - req, err := c.NewWhoamiRequest(ctx) - if err != nil { - return nil, err - } - responseCtx, lifecycle := c.responseContext(ctx) - req = req.Clone(responseCtx) - keepLifecycle := false - defer func() { - if !keepLifecycle { - lifecycle.close() - } - }() - res, err := c.do(responseCtx, req) - if err != nil { - return nil, fmt.Errorf("execute Whoami request: %w", err) - } - if res == nil { - return nil, fmt.Errorf("execute Whoami request: HTTP client returned nil response") - } - - result := &WhoamiResponse{StatusCode: res.StatusCode, Raw: res} - switch res.StatusCode { - case 200: - var decoded APIKeyWhoami - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode Whoami status 200 response: %w", err) - } - _ = res.Body.Close() - result.Status200 = &decoded - return result, nil - case 400: - var decoded ValidationErr - if err := json.NewDecoder(io.LimitReader(res.Body, maxDecodedBodyBytes)).Decode(&decoded); err != nil { - _ = res.Body.Close() - return nil, fmt.Errorf("decode Whoami status 400 response: %w", err) - } - _ = res.Body.Close() - result.Status400 = &decoded - return result, nil - case 401: - _ = res.Body.Close() - return result, nil - default: - rawBody, readErr := io.ReadAll(io.LimitReader(res.Body, maxDiagnosticBodyBytes)) - _ = res.Body.Close() - if readErr != nil { - return nil, fmt.Errorf("read unexpected Whoami response status %d: %w", res.StatusCode, readErr) - } - return nil, &UnexpectedStatusError{ - Method: req.Method, - URL: req.URL.String(), - StatusCode: res.StatusCode, - Body: strings.TrimSpace(string(rawBody)), - } - } -} - -type SSEIdleTimeoutError struct { - Duration time.Duration -} - -func (err *SSEIdleTimeoutError) Error() string { - return fmt.Sprintf("SSE stream was idle for %s", err.Duration) -} - -func (err *SSEIdleTimeoutError) Timeout() bool { return true } - -type idleTimeoutBody struct { - body io.ReadCloser - duration time.Duration - onTimeout func() - - mu sync.Mutex - timer *time.Timer - timedOut bool - closed bool - closeOnce sync.Once - closeErr error -} - -func newIdleTimeoutBody(body io.ReadCloser, duration time.Duration, onTimeout func()) io.ReadCloser { - if duration == 0 { - return body - } - wrapped := &idleTimeoutBody{ - body: body, - duration: duration, - onTimeout: onTimeout, - } - wrapped.timer = time.AfterFunc(duration, wrapped.expire) - return wrapped -} - -func (body *idleTimeoutBody) expire() { - body.mu.Lock() - if body.closed { - body.mu.Unlock() - return - } - body.timedOut = true - body.mu.Unlock() - if body.onTimeout != nil { - body.onTimeout() - } - _ = body.body.Close() -} - -func (body *idleTimeoutBody) Read(buffer []byte) (int, error) { - read, err := body.body.Read(buffer) - body.mu.Lock() - if read > 0 && !body.timedOut && !body.closed { - body.timer.Reset(body.duration) - } - timedOut := body.timedOut - body.mu.Unlock() - if timedOut && err != nil { - return read, &SSEIdleTimeoutError{Duration: body.duration} - } - return read, err -} - -func (body *idleTimeoutBody) Close() error { - body.closeOnce.Do(func() { - body.mu.Lock() - body.closed = true - body.timer.Stop() - body.mu.Unlock() - body.closeErr = body.body.Close() - }) - return body.closeErr -} - -type SSEStream[T any] struct { - body io.ReadCloser - scanner *bufio.Scanner - closeOnce sync.Once - nextMu sync.Mutex - data []string - currentEvent string - lastEvent string - finished bool - closeErr error - cleanup func() -} - -func NewSSEStream[T any](body io.ReadCloser) *SSEStream[T] { - return newSSEStream[T](body, defaultSSEIdleTimeout, nil) -} - -func newSSEStream[T any](body io.ReadCloser, idleTimeout time.Duration, cleanup func()) *SSEStream[T] { - body = newIdleTimeoutBody(body, idleTimeout, cleanup) - return newSSEStreamWithoutIdle[T](body, cleanup) -} - -func newReconnectingSSEStream[T any](body io.ReadCloser, cleanup func()) *SSEStream[T] { - return newSSEStreamWithoutIdle[T](body, cleanup) -} - -func newSSEStreamWithoutIdle[T any](body io.ReadCloser, cleanup func()) *SSEStream[T] { - scanner := bufio.NewScanner(body) - scanner.Buffer(make([]byte, 64<<10), maxSSEEventBytes) - return &SSEStream[T]{body: body, scanner: scanner, cleanup: cleanup} -} - -// EventName returns the event field associated with the item most recently -// returned by Next. An omitted event field produces an empty string. -func (stream *SSEStream[T]) EventName() string { - stream.nextMu.Lock() - defer stream.nextMu.Unlock() - return stream.lastEvent -} - -// Next returns one decoded SSE data payload without buffering the full stream. -// The boolean is false after clean EOF. Close ends consumption early. -func (stream *SSEStream[T]) Next(ctx context.Context) (T, bool, error) { - stream.nextMu.Lock() - defer stream.nextMu.Unlock() - - var zero T - if stream.finished { - return zero, false, nil - } - if err := ctx.Err(); err != nil { - _ = stream.Close() - return zero, false, err - } - wake := make(chan struct{}) - go func() { - select { - case <-ctx.Done(): - _ = stream.Close() - case <-wake: - } - }() - defer close(wake) - - for stream.scanner.Scan() { - line := stream.scanner.Text() - if line == "" { - decoded, ok, err := stream.decodeEvent() - if ok || err != nil { - return decoded, ok, err - } - continue - } - if strings.HasPrefix(line, ":") { - continue - } - field, value, found := strings.Cut(line, ":") - if !found { - value = "" - } - value = strings.TrimPrefix(value, " ") - switch field { - case "data": - stream.data = append(stream.data, value) - case "event": - stream.currentEvent = value - } - } - if err := ctx.Err(); err != nil { - stream.finished = true - _ = stream.Close() - return zero, false, err - } - if err := stream.scanner.Err(); err != nil { - stream.finished = true - _ = stream.Close() - return zero, false, fmt.Errorf("read SSE stream: %w", err) - } - stream.finished = true - decoded, ok, err := stream.decodeEvent() - _ = stream.Close() - return decoded, ok, err -} - -func (stream *SSEStream[T]) decodeEvent() (T, bool, error) { - var zero T - payload := strings.Join(stream.data, "\n") - stream.data = nil - stream.lastEvent = stream.currentEvent - stream.currentEvent = "" - if strings.TrimSpace(payload) == "" { - return zero, false, nil - } - var decoded T - if err := json.Unmarshal([]byte(payload), &decoded); err != nil { - stream.finished = true - _ = stream.Close() - return zero, false, fmt.Errorf("decode SSE JSON event: %w", err) - } - return decoded, true, nil -} - -func (stream *SSEStream[T]) Close() error { - stream.closeOnce.Do(func() { - stream.closeErr = stream.body.Close() - if stream.cleanup != nil { - stream.cleanup() - } - }) - return stream.closeErr -} diff --git a/publicapi/models.go b/publicapi/models.go deleted file mode 100644 index 050b2e1..0000000 --- a/publicapi/models.go +++ /dev/null @@ -1,1332 +0,0 @@ -package publicapi - -import ( - "encoding/json" - "fmt" -) - -type APIKeyWhoami struct { - Email string `json:"email"` - Id ApiKeyID `json:"id"` - Name *NonEmptyString `json:"name,omitempty"` - Scopes []ApiKeyScope `json:"scopes"` - TeamId TeamID `json:"team_id"` -} - -type ApiKeyID = string - -type ApiKeyScope string - -const ( - ApiKeyScopeTeamRead ApiKeyScope = "team:read" - ApiKeyScopeTeamManage ApiKeyScope = "team:manage" - ApiKeyScopeShowRead ApiKeyScope = "show:read" - ApiKeyScopeShowCreate ApiKeyScope = "show:create" - ApiKeyScopeShowUpdate ApiKeyScope = "show:update" - ApiKeyScopeEpisodeCreate ApiKeyScope = "episode:create" - ApiKeyScopeEpisodeDelete ApiKeyScope = "episode:delete" - ApiKeyScopeEpisodeUpdate ApiKeyScope = "episode:update" - ApiKeyScopeEpisodePublish ApiKeyScope = "episode:publish" -) - -type AssignableTeamRole string - -const ( - AssignableTeamRoleWrite AssignableTeamRole = "write" - AssignableTeamRoleRead AssignableTeamRole = "read" -) - -type CLIAuthorizationApprovedEvent struct { - TeamId TeamID `json:"team_id"` - Type string `json:"type"` -} - -type CLIAuthorizationCode = string - -type CLIAuthorizationDeniedEvent struct { - Type string `json:"type"` -} - -// CLIAuthorizationEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: CLIAuthorizationPendingEvent -// oneOf variant: CLIAuthorizationApprovedEvent -// oneOf variant: CLIAuthorizationDeniedEvent -// oneOf variant: CLIAuthorizationExpiredEvent -type CLIAuthorizationEvent struct { - CLIAuthorizationPendingEvent *CLIAuthorizationPendingEvent - CLIAuthorizationApprovedEvent *CLIAuthorizationApprovedEvent - CLIAuthorizationDeniedEvent *CLIAuthorizationDeniedEvent - CLIAuthorizationExpiredEvent *CLIAuthorizationExpiredEvent -} - -func (dst *CLIAuthorizationEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"type"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode CLIAuthorizationEvent discriminator: %w", err) - } - switch discriminator.Value { - case "cli.authorization.approved": - var decoded CLIAuthorizationApprovedEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode CLIAuthorizationEvent as CLIAuthorizationApprovedEvent: %w", err) - } - *dst = CLIAuthorizationEvent{CLIAuthorizationApprovedEvent: &decoded} - return nil - case "cli.authorization.denied": - var decoded CLIAuthorizationDeniedEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode CLIAuthorizationEvent as CLIAuthorizationDeniedEvent: %w", err) - } - *dst = CLIAuthorizationEvent{CLIAuthorizationDeniedEvent: &decoded} - return nil - case "cli.authorization.expired": - var decoded CLIAuthorizationExpiredEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode CLIAuthorizationEvent as CLIAuthorizationExpiredEvent: %w", err) - } - *dst = CLIAuthorizationEvent{CLIAuthorizationExpiredEvent: &decoded} - return nil - case "cli.authorization.pending": - var decoded CLIAuthorizationPendingEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode CLIAuthorizationEvent as CLIAuthorizationPendingEvent: %w", err) - } - *dst = CLIAuthorizationEvent{CLIAuthorizationPendingEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported CLIAuthorizationEvent discriminator %q", discriminator.Value) - } -} - -func (src CLIAuthorizationEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.CLIAuthorizationPendingEvent != nil { - matchCount++ - } - if src.CLIAuthorizationApprovedEvent != nil { - matchCount++ - } - if src.CLIAuthorizationDeniedEvent != nil { - matchCount++ - } - if src.CLIAuthorizationExpiredEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("CLIAuthorizationEvent must contain exactly one variant, got %d", matchCount) - } - if src.CLIAuthorizationPendingEvent != nil { - return json.Marshal(src.CLIAuthorizationPendingEvent) - } - if src.CLIAuthorizationApprovedEvent != nil { - return json.Marshal(src.CLIAuthorizationApprovedEvent) - } - if src.CLIAuthorizationDeniedEvent != nil { - return json.Marshal(src.CLIAuthorizationDeniedEvent) - } - if src.CLIAuthorizationExpiredEvent != nil { - return json.Marshal(src.CLIAuthorizationExpiredEvent) - } - return nil, fmt.Errorf("CLIAuthorizationEvent has no variant") -} - -func (src CLIAuthorizationEvent) GetActualInstance() any { - if src.CLIAuthorizationPendingEvent != nil { - return src.CLIAuthorizationPendingEvent - } - if src.CLIAuthorizationApprovedEvent != nil { - return src.CLIAuthorizationApprovedEvent - } - if src.CLIAuthorizationDeniedEvent != nil { - return src.CLIAuthorizationDeniedEvent - } - if src.CLIAuthorizationExpiredEvent != nil { - return src.CLIAuthorizationExpiredEvent - } - return nil -} - -func CLIAuthorizationPendingEventAsCLIAuthorizationEvent(v CLIAuthorizationPendingEvent) CLIAuthorizationEvent { - return CLIAuthorizationEvent{CLIAuthorizationPendingEvent: &v} -} - -func CLIAuthorizationApprovedEventAsCLIAuthorizationEvent(v CLIAuthorizationApprovedEvent) CLIAuthorizationEvent { - return CLIAuthorizationEvent{CLIAuthorizationApprovedEvent: &v} -} - -func CLIAuthorizationDeniedEventAsCLIAuthorizationEvent(v CLIAuthorizationDeniedEvent) CLIAuthorizationEvent { - return CLIAuthorizationEvent{CLIAuthorizationDeniedEvent: &v} -} - -func CLIAuthorizationExpiredEventAsCLIAuthorizationEvent(v CLIAuthorizationExpiredEvent) CLIAuthorizationEvent { - return CLIAuthorizationEvent{CLIAuthorizationExpiredEvent: &v} -} - -type CLIAuthorizationExpiredEvent struct { - Type string `json:"type"` -} - -type CLIAuthorizationPendingEvent struct { - Credential string `json:"credential"` - Type string `json:"type"` - VerificationUrl string `json:"verification_url"` -} - -type CompleteEpisodeUploadSession struct { - Publication EpisodePublication `json:"publication"` -} - -type CompleteImageUpload struct { - ObjectKey ImageUploadObjectKey `json:"object_key"` -} - -type CompletedEpisodeUploadPart struct { - Etag NonEmptyString `json:"etag"` - PartNumber MediaUploadPartNumber `json:"part_number"` - Size int64 `json:"size"` -} - -type CreateCLIAuthorization struct { - Scopes []ApiKeyScope `json:"scopes"` -} - -type CreateEpisodePackage struct { - Description *string `json:"description,omitempty"` - DurationSeconds int64 `json:"duration_seconds"` - Objects []PreparedMediaObject `json:"objects"` - ShowSlug ShowSlug `json:"show_slug"` - SourceUrl string `json:"source_url"` - Title string `json:"title"` -} - -type CreateEpisodeUploadSession struct { - ByteLength int64 `json:"byte_length"` - ContentType EpisodeUploadContentType `json:"content_type"` - Description *NonEmptyString `json:"description,omitempty"` - FileName NonEmptyString `json:"file_name"` - ShowSlug ShowSlug `json:"show_slug"` - SourceSha256 SHA256Hex `json:"source_sha256"` - ThumbnailUrl *string `json:"thumbnail_url,omitempty"` - Title NonEmptyString `json:"title"` -} - -type CreateImageUploadPresign struct { - ByteLength int64 `json:"byte_length"` - ContentType string `json:"content_type"` - FileName *NonEmptyString `json:"file_name,omitempty"` - ShowId ShowID `json:"show_id"` -} - -type CreateShow struct { - Id ShowID `json:"id"` - ImageAssetId *ImageAssetID `json:"image_asset_id,omitempty"` - Language ShowLanguage `json:"language"` - Slug ShowSlug `json:"slug"` - SourceKind ShowSourceKind `json:"source_kind"` - Title NonEmptyString `json:"title"` -} - -type CreateShowInvitation struct { - Email string `json:"email"` - Role AssignableTeamRole `json:"role"` -} - -type CreateTeamInvitation struct { - Email string `json:"email"` - Role AssignableTeamRole `json:"role"` -} - -type CreatedCLIAuthorization struct { - Code CLIAuthorizationCode `json:"code"` - Credential string `json:"credential"` - VerificationUrl string `json:"verification_url"` -} - -type CreatedImageUploadPresign struct { - ContentType string `json:"content_type"` - ExpiresAt int64 `json:"expires_at"` - ImageAssetId ImageAssetID `json:"image_asset_id"` - Method string `json:"method"` - ObjectKey ImageUploadObjectKey `json:"object_key"` - PublicUrl string `json:"public_url"` - UploadUrl string `json:"upload_url"` -} - -type CreatedPublicEpisodeDeletion struct { - EpisodeDeletionRunId EpisodeDeletionRunID `json:"episode_deletion_run_id"` -} - -type CreatedPublicRSSImport struct { - ImportRunId ImportRunID `json:"import_run_id"` -} - -type CreatedPublicShowDeletion struct { - ShowDeletionRunId ShowDeletionRunID `json:"show_deletion_run_id"` -} - -type Episode struct { - Description *string `json:"description,omitempty"` - Enclosures []EpisodeEnclosure `json:"enclosures"` - Id EpisodeID `json:"id"` - Link *string `json:"link,omitempty"` - Processing *EpisodeProcessingSnapshot `json:"processing,omitempty"` - RepublishedAt *UnixMillis `json:"republished_at,omitempty"` - ShowId ShowID `json:"show_id"` - Slug *NonEmptyString `json:"slug,omitempty"` - Status EpisodeStatus `json:"status"` - ThumbnailUrl *string `json:"thumbnail_url,omitempty"` - Title NonEmptyString `json:"title"` - VideoHlsUrl *string `json:"video_hls_url,omitempty"` -} - -type EpisodeDeletionCompletedEvent struct { - EpisodeDeletionRunId EpisodeDeletionRunID `json:"episode_deletion_run_id"` - EpisodeId EpisodeID `json:"episode_id"` - Status string `json:"status"` - Type string `json:"type"` -} - -// EpisodeDeletionEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: EpisodeDeletionProgressEvent -// oneOf variant: EpisodeDeletionTerminalEvent -type EpisodeDeletionEvent struct { - EpisodeDeletionProgressEvent *EpisodeDeletionProgressEvent - EpisodeDeletionTerminalEvent *EpisodeDeletionTerminalEvent -} - -func (dst *EpisodeDeletionEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"type"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode EpisodeDeletionEvent discriminator: %w", err) - } - switch discriminator.Value { - case "progress": - var decoded EpisodeDeletionProgressEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode EpisodeDeletionEvent as EpisodeDeletionProgressEvent: %w", err) - } - *dst = EpisodeDeletionEvent{EpisodeDeletionProgressEvent: &decoded} - return nil - case "terminal": - var decoded EpisodeDeletionTerminalEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode EpisodeDeletionEvent as EpisodeDeletionTerminalEvent: %w", err) - } - *dst = EpisodeDeletionEvent{EpisodeDeletionTerminalEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported EpisodeDeletionEvent discriminator %q", discriminator.Value) - } -} - -func (src EpisodeDeletionEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.EpisodeDeletionProgressEvent != nil { - matchCount++ - } - if src.EpisodeDeletionTerminalEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("EpisodeDeletionEvent must contain exactly one variant, got %d", matchCount) - } - if src.EpisodeDeletionProgressEvent != nil { - return json.Marshal(src.EpisodeDeletionProgressEvent) - } - if src.EpisodeDeletionTerminalEvent != nil { - return json.Marshal(src.EpisodeDeletionTerminalEvent) - } - return nil, fmt.Errorf("EpisodeDeletionEvent has no variant") -} - -func (src EpisodeDeletionEvent) GetActualInstance() any { - if src.EpisodeDeletionProgressEvent != nil { - return src.EpisodeDeletionProgressEvent - } - if src.EpisodeDeletionTerminalEvent != nil { - return src.EpisodeDeletionTerminalEvent - } - return nil -} - -func EpisodeDeletionProgressEventAsEpisodeDeletionEvent(v EpisodeDeletionProgressEvent) EpisodeDeletionEvent { - return EpisodeDeletionEvent{EpisodeDeletionProgressEvent: &v} -} - -func EpisodeDeletionTerminalEventAsEpisodeDeletionEvent(v EpisodeDeletionTerminalEvent) EpisodeDeletionEvent { - return EpisodeDeletionEvent{EpisodeDeletionTerminalEvent: &v} -} - -type EpisodeDeletionFailedEvent struct { - EpisodeDeletionRunId EpisodeDeletionRunID `json:"episode_deletion_run_id"` - ErrorCode NonEmptyString `json:"error_code"` - ErrorMessage NonEmptyString `json:"error_message"` - Status string `json:"status"` - Type string `json:"type"` -} - -type EpisodeDeletionProgressEvent struct { - CurrentStep NonEmptyString `json:"current_step"` - EpisodeDeletionRunId EpisodeDeletionRunID `json:"episode_deletion_run_id"` - Percent int32 `json:"percent"` - Status string `json:"status"` - Type string `json:"type"` -} - -type EpisodeDeletionRunID = string - -// EpisodeDeletionTerminalEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: EpisodeDeletionCompletedEvent -// oneOf variant: EpisodeDeletionFailedEvent -type EpisodeDeletionTerminalEvent struct { - EpisodeDeletionCompletedEvent *EpisodeDeletionCompletedEvent - EpisodeDeletionFailedEvent *EpisodeDeletionFailedEvent -} - -func (dst *EpisodeDeletionTerminalEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"status"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode EpisodeDeletionTerminalEvent discriminator: %w", err) - } - switch discriminator.Value { - case "completed": - var decoded EpisodeDeletionCompletedEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode EpisodeDeletionTerminalEvent as EpisodeDeletionCompletedEvent: %w", err) - } - *dst = EpisodeDeletionTerminalEvent{EpisodeDeletionCompletedEvent: &decoded} - return nil - case "failed": - var decoded EpisodeDeletionFailedEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode EpisodeDeletionTerminalEvent as EpisodeDeletionFailedEvent: %w", err) - } - *dst = EpisodeDeletionTerminalEvent{EpisodeDeletionFailedEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported EpisodeDeletionTerminalEvent discriminator %q", discriminator.Value) - } -} - -func (src EpisodeDeletionTerminalEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.EpisodeDeletionCompletedEvent != nil { - matchCount++ - } - if src.EpisodeDeletionFailedEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("EpisodeDeletionTerminalEvent must contain exactly one variant, got %d", matchCount) - } - if src.EpisodeDeletionCompletedEvent != nil { - return json.Marshal(src.EpisodeDeletionCompletedEvent) - } - if src.EpisodeDeletionFailedEvent != nil { - return json.Marshal(src.EpisodeDeletionFailedEvent) - } - return nil, fmt.Errorf("EpisodeDeletionTerminalEvent has no variant") -} - -func (src EpisodeDeletionTerminalEvent) GetActualInstance() any { - if src.EpisodeDeletionCompletedEvent != nil { - return src.EpisodeDeletionCompletedEvent - } - if src.EpisodeDeletionFailedEvent != nil { - return src.EpisodeDeletionFailedEvent - } - return nil -} - -func EpisodeDeletionCompletedEventAsEpisodeDeletionTerminalEvent(v EpisodeDeletionCompletedEvent) EpisodeDeletionTerminalEvent { - return EpisodeDeletionTerminalEvent{EpisodeDeletionCompletedEvent: &v} -} - -func EpisodeDeletionFailedEventAsEpisodeDeletionTerminalEvent(v EpisodeDeletionFailedEvent) EpisodeDeletionTerminalEvent { - return EpisodeDeletionTerminalEvent{EpisodeDeletionFailedEvent: &v} -} - -type EpisodeEnclosure struct { - ByteLength int64 `json:"byte_length"` - ContentType NonEmptyString `json:"content_type"` - DurationSeconds int64 `json:"duration_seconds"` - FeedId FeedID `json:"feed_id"` - Format EpisodeEnclosureFormat `json:"format"` - Url string `json:"url"` -} - -type EpisodeEnclosureFormat string - -const ( - EpisodeEnclosureFormatMp3 EpisodeEnclosureFormat = "mp3" - EpisodeEnclosureFormatM4a EpisodeEnclosureFormat = "m4a" - EpisodeEnclosureFormatWav EpisodeEnclosureFormat = "wav" - EpisodeEnclosureFormatFlac EpisodeEnclosureFormat = "flac" - EpisodeEnclosureFormatMp4 EpisodeEnclosureFormat = "mp4" -) - -type EpisodeID = string - -type EpisodeListItem struct { - AudioUrl *string `json:"audio_url,omitempty"` - Description *string `json:"description,omitempty"` - DurationSeconds *int64 `json:"duration_seconds,omitempty"` - Id EpisodeID `json:"id"` - Processing *EpisodeProcessingSnapshot `json:"processing,omitempty"` - RepublishedAt *UnixMillis `json:"republished_at,omitempty"` - ShowId ShowID `json:"show_id"` - Slug *NonEmptyString `json:"slug,omitempty"` - Status EpisodeStatus `json:"status"` - ThumbnailUrl *string `json:"thumbnail_url,omitempty"` - Title NonEmptyString `json:"title"` - VideoHlsUrl *string `json:"video_hls_url,omitempty"` -} - -type EpisodePackage struct { - EpisodeId EpisodeID `json:"episode_id"` - ObjectPrefix NonEmptyString `json:"object_prefix"` - PartSize int64 `json:"part_size"` - ShowId ShowID `json:"show_id"` - Status string `json:"status"` - TeamId TeamID `json:"team_id"` - UploadSessionId UploadSessionID `json:"upload_session_id"` -} - -type EpisodePage struct { - Episodes []EpisodeListItem `json:"episodes"` - NextCursor *EpisodePageCursor `json:"next_cursor,omitempty"` -} - -type EpisodePageCursor = string - -type EpisodePageLimit = int32 - -// EpisodeProcessingEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: EpisodeProcessingProgressEvent -// oneOf variant: EpisodeProcessingSafeHandoffEvent -// oneOf variant: EpisodeProcessingFailedEvent -type EpisodeProcessingEvent struct { - EpisodeProcessingProgressEvent *EpisodeProcessingProgressEvent - EpisodeProcessingSafeHandoffEvent *EpisodeProcessingSafeHandoffEvent - EpisodeProcessingFailedEvent *EpisodeProcessingFailedEvent -} - -func (dst *EpisodeProcessingEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"type"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode EpisodeProcessingEvent discriminator: %w", err) - } - switch discriminator.Value { - case "episode.processing.failed": - var decoded EpisodeProcessingFailedEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode EpisodeProcessingEvent as EpisodeProcessingFailedEvent: %w", err) - } - *dst = EpisodeProcessingEvent{EpisodeProcessingFailedEvent: &decoded} - return nil - case "episode.processing.progress": - var decoded EpisodeProcessingProgressEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode EpisodeProcessingEvent as EpisodeProcessingProgressEvent: %w", err) - } - *dst = EpisodeProcessingEvent{EpisodeProcessingProgressEvent: &decoded} - return nil - case "episode.processing.safe_handoff": - var decoded EpisodeProcessingSafeHandoffEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode EpisodeProcessingEvent as EpisodeProcessingSafeHandoffEvent: %w", err) - } - *dst = EpisodeProcessingEvent{EpisodeProcessingSafeHandoffEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported EpisodeProcessingEvent discriminator %q", discriminator.Value) - } -} - -func (src EpisodeProcessingEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.EpisodeProcessingProgressEvent != nil { - matchCount++ - } - if src.EpisodeProcessingSafeHandoffEvent != nil { - matchCount++ - } - if src.EpisodeProcessingFailedEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("EpisodeProcessingEvent must contain exactly one variant, got %d", matchCount) - } - if src.EpisodeProcessingProgressEvent != nil { - return json.Marshal(src.EpisodeProcessingProgressEvent) - } - if src.EpisodeProcessingSafeHandoffEvent != nil { - return json.Marshal(src.EpisodeProcessingSafeHandoffEvent) - } - if src.EpisodeProcessingFailedEvent != nil { - return json.Marshal(src.EpisodeProcessingFailedEvent) - } - return nil, fmt.Errorf("EpisodeProcessingEvent has no variant") -} - -func (src EpisodeProcessingEvent) GetActualInstance() any { - if src.EpisodeProcessingProgressEvent != nil { - return src.EpisodeProcessingProgressEvent - } - if src.EpisodeProcessingSafeHandoffEvent != nil { - return src.EpisodeProcessingSafeHandoffEvent - } - if src.EpisodeProcessingFailedEvent != nil { - return src.EpisodeProcessingFailedEvent - } - return nil -} - -func EpisodeProcessingProgressEventAsEpisodeProcessingEvent(v EpisodeProcessingProgressEvent) EpisodeProcessingEvent { - return EpisodeProcessingEvent{EpisodeProcessingProgressEvent: &v} -} - -func EpisodeProcessingSafeHandoffEventAsEpisodeProcessingEvent(v EpisodeProcessingSafeHandoffEvent) EpisodeProcessingEvent { - return EpisodeProcessingEvent{EpisodeProcessingSafeHandoffEvent: &v} -} - -func EpisodeProcessingFailedEventAsEpisodeProcessingEvent(v EpisodeProcessingFailedEvent) EpisodeProcessingEvent { - return EpisodeProcessingEvent{EpisodeProcessingFailedEvent: &v} -} - -type EpisodeProcessingFailedEvent struct { - EpisodeId EpisodeID `json:"episode_id"` - ErrorCode NonEmptyString `json:"error_code"` - ErrorMessage NonEmptyString `json:"error_message"` - RunId ProcessingRunID `json:"run_id"` - Status string `json:"status"` - Type string `json:"type"` -} - -type EpisodeProcessingProgressEvent struct { - CurrentStep NonEmptyString `json:"current_step"` - Percent int32 `json:"percent"` - RunId ProcessingRunID `json:"run_id"` - Status EpisodeProcessingStatus `json:"status"` - Type string `json:"type"` -} - -type EpisodeProcessingSafeHandoffEvent struct { - EpisodeId EpisodeID `json:"episode_id"` - MediaVersionId MediaVersionID `json:"media_version_id"` - RunId ProcessingRunID `json:"run_id"` - Status string `json:"status"` - Type string `json:"type"` - YoutubeVideoId *NonEmptyString `json:"youtube_video_id,omitempty"` -} - -type EpisodeProcessingSnapshot struct { - CurrentStep NonEmptyString `json:"current_step"` - ErrorCode *NonEmptyString `json:"error_code,omitempty"` - ErrorMessage *NonEmptyString `json:"error_message,omitempty"` - Percent int32 `json:"percent"` - RunId ProcessingRunID `json:"run_id"` - Status EpisodeProcessingStatus `json:"status"` -} - -type EpisodeProcessingStatus string - -const ( - EpisodeProcessingStatusQueued EpisodeProcessingStatus = "queued" - EpisodeProcessingStatusProcessing EpisodeProcessingStatus = "processing" - EpisodeProcessingStatusCompleted EpisodeProcessingStatus = "completed" - EpisodeProcessingStatusFailed EpisodeProcessingStatus = "failed" -) - -type EpisodePublication string - -const ( - EpisodePublicationDraft EpisodePublication = "draft" - EpisodePublicationPublish EpisodePublication = "publish" -) - -type EpisodeStatus string - -const ( - EpisodeStatusDraft EpisodeStatus = "draft" - EpisodeStatusPublished EpisodeStatus = "published" -) - -type EpisodeUploadContentType string - -const ( - EpisodeUploadContentTypeAudioMpeg EpisodeUploadContentType = "audio/mpeg" - EpisodeUploadContentTypeAudioMp4 EpisodeUploadContentType = "audio/mp4" - EpisodeUploadContentTypeAudioWav EpisodeUploadContentType = "audio/wav" - EpisodeUploadContentTypeAudioFlac EpisodeUploadContentType = "audio/flac" - EpisodeUploadContentTypeVideoMp4 EpisodeUploadContentType = "video/mp4" - EpisodeUploadContentTypeVideoQuicktime EpisodeUploadContentType = "video/quicktime" -) - -type EpisodeUploadSession struct { - ByteLength int64 `json:"byte_length"` - CompletedParts []CompletedEpisodeUploadPart `json:"completed_parts"` - ContentType EpisodeUploadContentType `json:"content_type"` - Description *string `json:"description,omitempty"` - EpisodeId *EpisodeID `json:"episode_id,omitempty"` - ExpiresAt UnixMillis `json:"expires_at"` - FileName NonEmptyString `json:"file_name"` - MaxParallelism int32 `json:"max_parallelism"` - ObjectKey R2ObjectKey `json:"object_key"` - PartSize int64 `json:"part_size"` - Phase EpisodeUploadSessionPhase `json:"phase"` - Processing *EpisodeProcessingSnapshot `json:"processing,omitempty"` - ShowId ShowID `json:"show_id"` - SourceDurationSeconds *int64 `json:"source_duration_seconds,omitempty"` - SourceSha256 SHA256Hex `json:"source_sha256"` - TeamId TeamID `json:"team_id"` - ThumbnailUrl *string `json:"thumbnail_url,omitempty"` - Title NonEmptyString `json:"title"` - UploadSessionId UploadSessionID `json:"upload_session_id"` -} - -type EpisodeUploadSessionPhase string - -const ( - EpisodeUploadSessionPhaseCreated EpisodeUploadSessionPhase = "created" - EpisodeUploadSessionPhaseUploading EpisodeUploadSessionPhase = "uploading" - EpisodeUploadSessionPhaseProcessing EpisodeUploadSessionPhase = "processing" - EpisodeUploadSessionPhaseCompleted EpisodeUploadSessionPhase = "completed" - EpisodeUploadSessionPhaseFailed EpisodeUploadSessionPhase = "failed" - EpisodeUploadSessionPhaseExpired EpisodeUploadSessionPhase = "expired" -) - -type FeedID = string - -type ImageAsset struct { - ByteLength int64 `json:"byte_length"` - ContentType string `json:"content_type"` - HasTransparency bool `json:"has_transparency"` - HeightPixels int64 `json:"height_pixels"` - Id ImageAssetID `json:"id"` - PublicUrl string `json:"public_url"` - WidthPixels int64 `json:"width_pixels"` -} - -type ImageAssetID = string - -type ImageUploadObjectKey = string - -type ImportRSSRequest struct { - Slug *ShowSlug `json:"slug,omitempty"` - SourceUrl string `json:"source_url"` -} - -type ImportRunID = string - -type MediaUploadPartNumber = int32 - -type MediaVersionID = string - -type NonEmptyString = string - -type OpenAPIJSONDocument struct { -} - -type OpenAPIYAMLDocument = string - -type PatchEpisodeUploadSession struct { - Description *string `json:"description,omitempty"` - ThumbnailUrl *string `json:"thumbnail_url,omitempty"` -} - -type PendingShowInvitation struct { - Email string `json:"email"` - ExpiresAt UnixMillis `json:"expires_at"` - Id ShowInvitationID `json:"id"` - InvitedAt UnixMillis `json:"invited_at"` - Role AssignableTeamRole `json:"role"` -} - -type PendingTeamInvitation struct { - Email string `json:"email"` - ExpiresAt UnixMillis `json:"expires_at"` - Id TeamInvitationID `json:"id"` - InvitedAt UnixMillis `json:"invited_at"` - Role AssignableTeamRole `json:"role"` -} - -type PreparedMediaObject struct { - ByteLength int64 `json:"byte_length"` - ContentType string `json:"content_type"` - Name string `json:"name"` - Sha256 SHA256Hex `json:"sha256"` -} - -type PresignEpisodeUploadSessionParts struct { - PartNumbers []MediaUploadPartNumber `json:"part_numbers"` -} - -type PresignedEpisodeUploadPart struct { - ExpiresAt UnixMillis `json:"expires_at"` - Method string `json:"method"` - PartNumber MediaUploadPartNumber `json:"part_number"` - UploadUrl string `json:"upload_url"` -} - -type PresignedEpisodeUploadParts struct { - Parts []PresignedEpisodeUploadPart `json:"parts"` - UploadSessionId UploadSessionID `json:"upload_session_id"` -} - -type ProcessingRunID = string - -type PublicRSSImportCompletedTerminalEvent struct { - FeedId FeedID `json:"feed_id"` - ShowId ShowID `json:"show_id"` - ShowSlug ShowSlug `json:"show_slug"` - Status string `json:"status"` - TeamId TeamID `json:"team_id"` - Type string `json:"type"` -} - -// PublicRSSImportEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: RSSImportProgressEvent -// oneOf variant: PublicRSSImportTerminalEvent -type PublicRSSImportEvent struct { - RSSImportProgressEvent *RSSImportProgressEvent - PublicRSSImportTerminalEvent *PublicRSSImportTerminalEvent -} - -func (dst *PublicRSSImportEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"type"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode PublicRSSImportEvent discriminator: %w", err) - } - switch discriminator.Value { - case "progress": - var decoded RSSImportProgressEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode PublicRSSImportEvent as RSSImportProgressEvent: %w", err) - } - *dst = PublicRSSImportEvent{RSSImportProgressEvent: &decoded} - return nil - case "terminal": - var decoded PublicRSSImportTerminalEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode PublicRSSImportEvent as PublicRSSImportTerminalEvent: %w", err) - } - *dst = PublicRSSImportEvent{PublicRSSImportTerminalEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported PublicRSSImportEvent discriminator %q", discriminator.Value) - } -} - -func (src PublicRSSImportEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.RSSImportProgressEvent != nil { - matchCount++ - } - if src.PublicRSSImportTerminalEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("PublicRSSImportEvent must contain exactly one variant, got %d", matchCount) - } - if src.RSSImportProgressEvent != nil { - return json.Marshal(src.RSSImportProgressEvent) - } - if src.PublicRSSImportTerminalEvent != nil { - return json.Marshal(src.PublicRSSImportTerminalEvent) - } - return nil, fmt.Errorf("PublicRSSImportEvent has no variant") -} - -func (src PublicRSSImportEvent) GetActualInstance() any { - if src.RSSImportProgressEvent != nil { - return src.RSSImportProgressEvent - } - if src.PublicRSSImportTerminalEvent != nil { - return src.PublicRSSImportTerminalEvent - } - return nil -} - -func RSSImportProgressEventAsPublicRSSImportEvent(v RSSImportProgressEvent) PublicRSSImportEvent { - return PublicRSSImportEvent{RSSImportProgressEvent: &v} -} - -func PublicRSSImportTerminalEventAsPublicRSSImportEvent(v PublicRSSImportTerminalEvent) PublicRSSImportEvent { - return PublicRSSImportEvent{PublicRSSImportTerminalEvent: &v} -} - -// PublicRSSImportTerminalEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: PublicRSSImportCompletedTerminalEvent -// oneOf variant: RSSImportFailedTerminalEvent -// oneOf variant: RSSImportCancelledTerminalEvent -type PublicRSSImportTerminalEvent struct { - PublicRSSImportCompletedTerminalEvent *PublicRSSImportCompletedTerminalEvent - RSSImportFailedTerminalEvent *RSSImportFailedTerminalEvent - RSSImportCancelledTerminalEvent *RSSImportCancelledTerminalEvent -} - -func (dst *PublicRSSImportTerminalEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"status"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode PublicRSSImportTerminalEvent discriminator: %w", err) - } - switch discriminator.Value { - case "cancelled": - var decoded RSSImportCancelledTerminalEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode PublicRSSImportTerminalEvent as RSSImportCancelledTerminalEvent: %w", err) - } - *dst = PublicRSSImportTerminalEvent{RSSImportCancelledTerminalEvent: &decoded} - return nil - case "completed": - var decoded PublicRSSImportCompletedTerminalEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode PublicRSSImportTerminalEvent as PublicRSSImportCompletedTerminalEvent: %w", err) - } - *dst = PublicRSSImportTerminalEvent{PublicRSSImportCompletedTerminalEvent: &decoded} - return nil - case "failed": - var decoded RSSImportFailedTerminalEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode PublicRSSImportTerminalEvent as RSSImportFailedTerminalEvent: %w", err) - } - *dst = PublicRSSImportTerminalEvent{RSSImportFailedTerminalEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported PublicRSSImportTerminalEvent discriminator %q", discriminator.Value) - } -} - -func (src PublicRSSImportTerminalEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.PublicRSSImportCompletedTerminalEvent != nil { - matchCount++ - } - if src.RSSImportFailedTerminalEvent != nil { - matchCount++ - } - if src.RSSImportCancelledTerminalEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("PublicRSSImportTerminalEvent must contain exactly one variant, got %d", matchCount) - } - if src.PublicRSSImportCompletedTerminalEvent != nil { - return json.Marshal(src.PublicRSSImportCompletedTerminalEvent) - } - if src.RSSImportFailedTerminalEvent != nil { - return json.Marshal(src.RSSImportFailedTerminalEvent) - } - if src.RSSImportCancelledTerminalEvent != nil { - return json.Marshal(src.RSSImportCancelledTerminalEvent) - } - return nil, fmt.Errorf("PublicRSSImportTerminalEvent has no variant") -} - -func (src PublicRSSImportTerminalEvent) GetActualInstance() any { - if src.PublicRSSImportCompletedTerminalEvent != nil { - return src.PublicRSSImportCompletedTerminalEvent - } - if src.RSSImportFailedTerminalEvent != nil { - return src.RSSImportFailedTerminalEvent - } - if src.RSSImportCancelledTerminalEvent != nil { - return src.RSSImportCancelledTerminalEvent - } - return nil -} - -func PublicRSSImportCompletedTerminalEventAsPublicRSSImportTerminalEvent(v PublicRSSImportCompletedTerminalEvent) PublicRSSImportTerminalEvent { - return PublicRSSImportTerminalEvent{PublicRSSImportCompletedTerminalEvent: &v} -} - -func RSSImportFailedTerminalEventAsPublicRSSImportTerminalEvent(v RSSImportFailedTerminalEvent) PublicRSSImportTerminalEvent { - return PublicRSSImportTerminalEvent{RSSImportFailedTerminalEvent: &v} -} - -func RSSImportCancelledTerminalEventAsPublicRSSImportTerminalEvent(v RSSImportCancelledTerminalEvent) PublicRSSImportTerminalEvent { - return PublicRSSImportTerminalEvent{RSSImportCancelledTerminalEvent: &v} -} - -type R2ObjectKey = string - -type RSSImportCancelledTerminalEvent struct { - Status string `json:"status"` - Type string `json:"type"` -} - -type RSSImportEntitlementError struct { - Code string `json:"code"` - CurrentEntitlement VideoDeliveryEntitlement `json:"current_entitlement"` - MediaKind RSSImportMediaKind `json:"media_kind"` - PricingUrl string `json:"pricing_url"` - RequiredEntitlement VideoDeliveryEntitlement `json:"required_entitlement"` -} - -type RSSImportFailedTerminalEvent struct { - ErrorCode NonEmptyString `json:"error_code"` - ErrorMessage NonEmptyString `json:"error_message"` - HttpStatus *int64 `json:"http_status,omitempty"` - LimitSeconds *int64 `json:"limit_seconds,omitempty"` - PricingUrl *string `json:"pricing_url,omitempty"` - RemainingSeconds *int64 `json:"remaining_seconds,omitempty"` - RequestedSeconds *int64 `json:"requested_seconds,omitempty"` - RequiredNextPlan *NonEmptyString `json:"required_next_plan,omitempty"` - ReservedSeconds *int64 `json:"reserved_seconds,omitempty"` - RetainedSeconds *int64 `json:"retained_seconds,omitempty"` - SalesContactRequired *bool `json:"sales_contact_required,omitempty"` - Status string `json:"status"` - Type string `json:"type"` -} - -type RSSImportMediaKind string - -const ( - RSSImportMediaKindAudio RSSImportMediaKind = "audio" - RSSImportMediaKindVideo RSSImportMediaKind = "video" -) - -type RSSImportProgressEvent struct { - CurrentStep NonEmptyString `json:"current_step"` - Percent int32 `json:"percent"` - Type string `json:"type"` -} - -type SHA256Hex = string - -type Show struct { - Id ShowID `json:"id"` - Language ShowLanguage `json:"language"` - Slug ShowSlug `json:"slug"` - SourceKind ShowSourceKind `json:"source_kind"` - TeamId TeamID `json:"team_id"` - Title NonEmptyString `json:"title"` -} - -type ShowAccessSource string - -const ( - ShowAccessSourceTeam ShowAccessSource = "team" - ShowAccessSourceShow ShowAccessSource = "show" -) - -type ShowDeletionCompletedEvent struct { - ShowDeletionRunId ShowDeletionRunID `json:"show_deletion_run_id"` - ShowSlug ShowSlug `json:"show_slug"` - Status string `json:"status"` - Type string `json:"type"` -} - -// ShowDeletionEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: ShowDeletionProgressEvent -// oneOf variant: ShowDeletionTerminalEvent -type ShowDeletionEvent struct { - ShowDeletionProgressEvent *ShowDeletionProgressEvent - ShowDeletionTerminalEvent *ShowDeletionTerminalEvent -} - -func (dst *ShowDeletionEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"type"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode ShowDeletionEvent discriminator: %w", err) - } - switch discriminator.Value { - case "progress": - var decoded ShowDeletionProgressEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode ShowDeletionEvent as ShowDeletionProgressEvent: %w", err) - } - *dst = ShowDeletionEvent{ShowDeletionProgressEvent: &decoded} - return nil - case "terminal": - var decoded ShowDeletionTerminalEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode ShowDeletionEvent as ShowDeletionTerminalEvent: %w", err) - } - *dst = ShowDeletionEvent{ShowDeletionTerminalEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported ShowDeletionEvent discriminator %q", discriminator.Value) - } -} - -func (src ShowDeletionEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.ShowDeletionProgressEvent != nil { - matchCount++ - } - if src.ShowDeletionTerminalEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("ShowDeletionEvent must contain exactly one variant, got %d", matchCount) - } - if src.ShowDeletionProgressEvent != nil { - return json.Marshal(src.ShowDeletionProgressEvent) - } - if src.ShowDeletionTerminalEvent != nil { - return json.Marshal(src.ShowDeletionTerminalEvent) - } - return nil, fmt.Errorf("ShowDeletionEvent has no variant") -} - -func (src ShowDeletionEvent) GetActualInstance() any { - if src.ShowDeletionProgressEvent != nil { - return src.ShowDeletionProgressEvent - } - if src.ShowDeletionTerminalEvent != nil { - return src.ShowDeletionTerminalEvent - } - return nil -} - -func ShowDeletionProgressEventAsShowDeletionEvent(v ShowDeletionProgressEvent) ShowDeletionEvent { - return ShowDeletionEvent{ShowDeletionProgressEvent: &v} -} - -func ShowDeletionTerminalEventAsShowDeletionEvent(v ShowDeletionTerminalEvent) ShowDeletionEvent { - return ShowDeletionEvent{ShowDeletionTerminalEvent: &v} -} - -type ShowDeletionFailedEvent struct { - ErrorCode NonEmptyString `json:"error_code"` - ErrorMessage NonEmptyString `json:"error_message"` - ShowDeletionRunId ShowDeletionRunID `json:"show_deletion_run_id"` - Status string `json:"status"` - Type string `json:"type"` -} - -type ShowDeletionProgressEvent struct { - CurrentStep NonEmptyString `json:"current_step"` - Percent int32 `json:"percent"` - ShowDeletionRunId ShowDeletionRunID `json:"show_deletion_run_id"` - Status string `json:"status"` - Type string `json:"type"` -} - -type ShowDeletionRunID = string - -// ShowDeletionTerminalEvent is generated from an OpenAPI oneOf schema. -// oneOf variant: ShowDeletionCompletedEvent -// oneOf variant: ShowDeletionFailedEvent -type ShowDeletionTerminalEvent struct { - ShowDeletionCompletedEvent *ShowDeletionCompletedEvent - ShowDeletionFailedEvent *ShowDeletionFailedEvent -} - -func (dst *ShowDeletionTerminalEvent) UnmarshalJSON(data []byte) error { - var discriminator struct { - Value string `json:"status"` - } - if err := json.Unmarshal(data, &discriminator); err != nil { - return fmt.Errorf("decode ShowDeletionTerminalEvent discriminator: %w", err) - } - switch discriminator.Value { - case "completed": - var decoded ShowDeletionCompletedEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode ShowDeletionTerminalEvent as ShowDeletionCompletedEvent: %w", err) - } - *dst = ShowDeletionTerminalEvent{ShowDeletionCompletedEvent: &decoded} - return nil - case "failed": - var decoded ShowDeletionFailedEvent - if err := json.Unmarshal(data, &decoded); err != nil { - return fmt.Errorf("decode ShowDeletionTerminalEvent as ShowDeletionFailedEvent: %w", err) - } - *dst = ShowDeletionTerminalEvent{ShowDeletionFailedEvent: &decoded} - return nil - default: - return fmt.Errorf("unsupported ShowDeletionTerminalEvent discriminator %q", discriminator.Value) - } -} - -func (src ShowDeletionTerminalEvent) MarshalJSON() ([]byte, error) { - matchCount := 0 - if src.ShowDeletionCompletedEvent != nil { - matchCount++ - } - if src.ShowDeletionFailedEvent != nil { - matchCount++ - } - if matchCount != 1 { - return nil, fmt.Errorf("ShowDeletionTerminalEvent must contain exactly one variant, got %d", matchCount) - } - if src.ShowDeletionCompletedEvent != nil { - return json.Marshal(src.ShowDeletionCompletedEvent) - } - if src.ShowDeletionFailedEvent != nil { - return json.Marshal(src.ShowDeletionFailedEvent) - } - return nil, fmt.Errorf("ShowDeletionTerminalEvent has no variant") -} - -func (src ShowDeletionTerminalEvent) GetActualInstance() any { - if src.ShowDeletionCompletedEvent != nil { - return src.ShowDeletionCompletedEvent - } - if src.ShowDeletionFailedEvent != nil { - return src.ShowDeletionFailedEvent - } - return nil -} - -func ShowDeletionCompletedEventAsShowDeletionTerminalEvent(v ShowDeletionCompletedEvent) ShowDeletionTerminalEvent { - return ShowDeletionTerminalEvent{ShowDeletionCompletedEvent: &v} -} - -func ShowDeletionFailedEventAsShowDeletionTerminalEvent(v ShowDeletionFailedEvent) ShowDeletionTerminalEvent { - return ShowDeletionTerminalEvent{ShowDeletionFailedEvent: &v} -} - -type ShowID = string - -type ShowInvitationID = string - -type ShowLanguage = string - -type ShowMember struct { - AccessSource ShowAccessSource `json:"access_source"` - JoinedAt UnixMillis `json:"joined_at"` - Role TeamRole `json:"role"` - User User `json:"user"` -} - -type ShowMemberCollection struct { - Invitations []PendingShowInvitation `json:"invitations"` - Members []ShowMember `json:"members"` -} - -type ShowSlug = string - -type ShowSourceKind string - -const ( - ShowSourceKindAudio ShowSourceKind = "audio" - ShowSourceKindVideo ShowSourceKind = "video" -) - -type TeamID = string - -type TeamInvitationID = string - -type TeamMember struct { - JoinedAt UnixMillis `json:"joined_at"` - Role TeamRole `json:"role"` - User User `json:"user"` -} - -type TeamMemberCollection struct { - Invitations []PendingTeamInvitation `json:"invitations"` - Members []TeamMember `json:"members"` -} - -type TeamRole string - -const ( - TeamRoleOwner TeamRole = "owner" - TeamRoleWrite TeamRole = "write" - TeamRoleRead TeamRole = "read" -) - -type UnixMillis = int64 - -type UpdateShowMemberRole struct { - Role AssignableTeamRole `json:"role"` -} - -type UpdateTeamMemberRole struct { - Role AssignableTeamRole `json:"role"` -} - -type UploadSessionID = string - -type User struct { - Email string `json:"email"` - Id UserID `json:"id"` - Name *NonEmptyString `json:"name,omitempty"` - PictureUrl *string `json:"picture_url,omitempty"` -} - -type UserID = string - -type ValidationErr struct { - Errors []ValidationIssue `json:"errors"` - Message NonEmptyString `json:"message"` -} - -type ValidationIssue struct { - Code ValidationIssueCode `json:"code"` - Field *string `json:"field,omitempty"` - In ValidationLocation `json:"in"` - Message NonEmptyString `json:"message"` - Path *[]String `json:"path,omitempty"` -} - -type ValidationIssueCode = string - -type ValidationLocation string - -const ( - ValidationLocationBody ValidationLocation = "body" - ValidationLocationQuery ValidationLocation = "query" - ValidationLocationPath ValidationLocation = "path" - ValidationLocationHeader ValidationLocation = "header" - ValidationLocationCookie ValidationLocation = "cookie" -) - -type VideoAdmissionError struct { - Code VideoAdmissionErrorCode `json:"code"` - CurrentPlan VideoPlan `json:"current_plan"` - DeliveryEntitlement VideoDeliveryEntitlement `json:"delivery_entitlement"` - LimitSeconds int64 `json:"limit_seconds"` - PricingUrl string `json:"pricing_url"` - RemainingSeconds int64 `json:"remaining_seconds"` - RequestedSeconds int64 `json:"requested_seconds"` - RequiredNextPlan VideoPlan `json:"required_next_plan"` - ReservedSeconds int64 `json:"reserved_seconds"` - RetainedSeconds int64 `json:"retained_seconds"` - SalesContactRequired bool `json:"sales_contact_required"` -} - -type VideoAdmissionErrorCode string - -const ( - VideoAdmissionErrorCodeVideoPlanRequired VideoAdmissionErrorCode = "video_plan_required" - VideoAdmissionErrorCodeVideoHoursExhausted VideoAdmissionErrorCode = "video_hours_exhausted" - VideoAdmissionErrorCodeVideoHoursExceeded VideoAdmissionErrorCode = "video_hours_exceeded" -) - -type VideoDeliveryEntitlement string - -const ( - VideoDeliveryEntitlementFree VideoDeliveryEntitlement = "free" - VideoDeliveryEntitlementAudio VideoDeliveryEntitlement = "audio" - VideoDeliveryEntitlementVideoHd VideoDeliveryEntitlement = "video_hd" - VideoDeliveryEntitlementVideo4k VideoDeliveryEntitlement = "video_4k" -) - -type VideoPlan string - -const ( - VideoPlanStarter VideoPlan = "starter" - VideoPlanProfessional VideoPlan = "professional" - VideoPlanStudio VideoPlan = "studio" -) - -type String = string diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..e630049 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.98.1" +profile = "minimal" +components = ["clippy", "rustfmt"] diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 0000000..54162e4 --- /dev/null +++ b/src/api.rs @@ -0,0 +1,153 @@ +use crate::{auth::StoredAuth, config::Config}; +use anyhow::{Context, Result, bail, ensure}; +use reqwest::{Client, RequestBuilder, Response}; +use serde::de::DeserializeOwned; +use serde_json::Value; +use std::{future::Future, time::Duration}; +use tokio_util::sync::CancellationToken; + +#[derive(Clone)] +pub struct Api { + pub config: Config, + pub credential: Option, + pub http: Client, + pub cancel: CancellationToken, +} + +impl Api { + pub fn new(config: Config, cancel: CancellationToken) -> Result { + let credential = std::fs::read(config.directory.join("auth.json")) + .ok() + .filter(|raw| raw.len() <= 64 << 10) + .and_then(|raw| serde_json::from_slice::(&raw).ok()) + .filter(|auth| crate::config::credential_valid(&auth.api_key)) + .map(|auth| auth.api_key); + let mut builder = Client::builder() + .connect_timeout(Duration::from_secs(30)) + .timeout(Duration::from_secs(30 * 60)); + if let Some(path) = std::env::var_os("SSL_CERT_FILE") { + for certificate in reqwest::Certificate::from_pem_bundle(&std::fs::read(path)?)? { + builder = builder.add_root_certificate(certificate); + } + } + Ok(Self { + config, + credential, + http: builder.build()?, + cancel, + }) + } + + pub fn client(&self) -> crate::publicapi::Client { + crate::publicapi::Client::new( + self.http.clone(), + self.config.api_origin.clone(), + self.credential.clone(), + ) + } + + pub async fn wait(&self, work: impl Future>) -> Result { + tokio::select! { + biased; + _ = self.cancel.cancelled() => bail!("operation interrupted; resumable source upload state preserved"), + result = work => result, + } + } + + pub async fn send(&self, request: RequestBuilder) -> Result { + self.wait(async { + request + .send() + .await + .map_err(|error| error.without_url()) + .context("send HTTP request") + }) + .await + } + + pub async fn bytes(&self, response: Response, limit: usize) -> Result> { + self.wait(read_bounded(response, limit)).await + } + + pub async fn decode(&self, response: Response) -> Result { + serde_json::from_slice(&self.bytes(response, 4 << 20).await?).context("decode API response") + } + + pub async fn json(&self, request: RequestBuilder, statuses: &[u16]) -> Result { + let response = self.send(request).await?; + self.accept(response, statuses).await + } + + pub async fn accept(&self, response: Response, statuses: &[u16]) -> Result { + if !statuses.contains(&response.status().as_u16()) { + return Err(self.response_error(response).await); + } + if response.status() == reqwest::StatusCode::NO_CONTENT { + return Ok(Value::Null); + } + self.decode(response).await + } + + pub async fn response_error(&self, response: Response) -> anyhow::Error { + let status = response.status().as_u16(); + let raw = self.bytes(response, 64 << 10).await.unwrap_or_default(); + let message = serde_json::from_slice::(&raw) + .ok() + .and_then(|body| { + body.get("message") + .and_then(Value::as_str) + .map(str::to_owned) + }) + .unwrap_or_else(|| String::from_utf8_lossy(&raw).trim().to_owned()); + let hint = match status { + 401 => "authentication failed; run listenbox login", + 403 => "permission denied", + _ => &message, + }; + anyhow::anyhow!("HTTP {status}: {hint}") + } + + pub fn trace(&self, response: &Response, stdout: bool) -> Result<()> { + if self.config.print_trace_ids { + let trace = response + .headers() + .get("X-Trace-Id") + .context("response missing X-Trace-Id")? + .to_str()?; + crate::config::check_trace(trace)?; + if stdout { + println!("Trace ID: {trace}"); + } else { + eprintln!("Trace ID: {trace}"); + } + } + Ok(()) + } +} + +pub async fn read_bounded(mut response: Response, limit: usize) -> Result> { + let mut body = Vec::new(); + while let Some(chunk) = response.chunk().await? { + ensure!( + chunk.len() <= limit.saturating_sub(body.len()), + "HTTP response exceeds {limit} bytes" + ); + body.extend_from_slice(&chunk); + } + Ok(body) +} + +pub fn string<'a>(value: &'a Value, key: &str) -> Result<&'a str> { + value + .get(key) + .and_then(Value::as_str) + .filter(|value| !value.is_empty()) + .with_context(|| format!("response missing {key}")) +} + +pub fn number(value: &Value, key: &str) -> Result { + value + .get(key) + .and_then(Value::as_i64) + .with_context(|| format!("response missing numeric {key}")) +} diff --git a/src/audio.rs b/src/audio.rs new file mode 100644 index 0000000..b1b2f20 --- /dev/null +++ b/src/audio.rs @@ -0,0 +1,287 @@ +//! FFmpeg objects stay on one worker thread and are owned by ffmpeg-the-third's Rust wrappers. +use anyhow::{Context, Result, ensure}; +use ffmpeg::{ + ChannelLayout, ChannelLayoutMask, Dictionary, Packet, codec, format, frame, media, + software::resampling, +}; +use std::{ + collections::VecDeque, + fs::File, + io::{Read, Seek, SeekFrom}, + path::Path, +}; +use tokio_util::sync::CancellationToken; + +pub fn prepare_m4a(input: &Path, output: &Path, cancelled: &CancellationToken) -> Result { + ensure!(!cancelled.is_cancelled(), "Audio conversion cancelled"); + ensure!( + input != output && !output.exists(), + "Audio output must be a new file" + ); + ffmpeg::init()?; + let result = convert(input, output, cancelled).and_then(|()| { + ensure!(fast_start(output)?, "M4A fast-start validation failed"); + Ok(output.metadata()?.len()) + }); + if result.is_err() { + let _ = std::fs::remove_file(output); + } + result +} +fn convert(input: &Path, output: &Path, cancelled: &CancellationToken) -> Result<()> { + let mut input = format::input(input)?; + let source = input + .streams() + .best(media::Type::Audio) + .context("No audio stream")?; + let index = source.index(); + let time_base = source.time_base(); + let parameters = source.parameters(); + let mut output = format::output_as(output, "ipod")?; + if parameters.id() != codec::Id::AAC { + let decoder = codec::Context::from_parameters(parameters)? + .decoder() + .audio()?; + return transcode(input, output, index, decoder, cancelled); + } + + { + let mut stream = output.add_stream(ffmpeg::encoder::find(codec::Id::None))?; + stream.set_parameters(parameters); + stream.set_time_base(time_base); + // Reset the source-container codec tag when writing an M4A container. + // SAFETY: the uniquely borrowed output stream owns these codec parameters. + unsafe { + (*stream.parameters_mut().as_mut_ptr()).codec_tag = 0; + } + } + header(&mut output)?; + let destination_time = output.stream(0).context("No output stream")?.time_base(); + let mut count = 0; + loop { + ensure!(!cancelled.is_cancelled(), "Audio conversion cancelled"); + let mut packet = Packet::empty(); + match packet.read(&mut input) { + Ok(()) => (), + Err(ffmpeg::Error::Eof) => break, + Err(error) => return Err(error.into()), + } + if packet.stream() == index { + packet.rescale_ts(time_base, destination_time); + packet.set_stream(0); + packet.set_position(-1); + packet.write_interleaved(&mut output)?; + count += 1; + } + } + ensure!(count > 0, "Downloaded audio was empty"); + output.write_trailer()?; + Ok(()) +} +fn header(output: &mut format::context::Output) -> Result<()> { + let mut options = Dictionary::new(); + options.set("movflags", "+faststart"); + output.write_header_with(options)?; + Ok(()) +} +fn waiting(error: ffmpeg::Error) -> bool { + matches!(error, ffmpeg::Error::Eof) + || matches!(error, ffmpeg::Error::Other { errno } if std::io::Error::from_raw_os_error(errno).kind() == std::io::ErrorKind::WouldBlock) +} + +struct Transcoder { + decoder: codec::decoder::Audio, + encoder: codec::encoder::Audio, + resampler: resampling::Context, + fifo: Vec>, + output: format::context::Output, + layout: ChannelLayoutMask, + timestamp: i64, + output_time: ffmpeg::Rational, +} +impl Transcoder { + fn drain_encoder(&mut self) -> Result<()> { + loop { + let mut packet = Packet::empty(); + match self.encoder.receive_packet(&mut packet) { + Ok(()) => (), + Err(error) if waiting(error) => break, + Err(error) => return Err(error.into()), + } + packet.set_stream(0); + packet.rescale_ts((1, 48000), self.output_time); + packet.write_interleaved(&mut self.output)?; + } + Ok(()) + } + fn encode_ready(&mut self, finish: bool) -> Result<()> { + let frame_size = self.encoder.frame_size() as usize; + while self.fifo[0].len() >= frame_size || (finish && !self.fifo[0].is_empty()) { + let count = frame_size.min(self.fifo[0].len()); + let mut frame = frame::Audio::new(self.encoder.format(), count, self.layout); + frame.set_rate(48000); + for (channel, samples) in self.fifo.iter_mut().enumerate() { + for (target, value) in frame + .plane_mut::(channel) + .iter_mut() + .zip(samples.drain(..count)) + { + *target = value; + } + } + frame.set_pts(Some(self.timestamp)); + self.timestamp += count as i64; + self.encoder.send_frame(&frame)?; + self.drain_encoder()?; + } + Ok(()) + } + fn collect(&mut self, converted: &frame::Audio) -> Result<()> { + for (channel, fifo) in self.fifo.iter_mut().enumerate() { + fifo.extend(converted.plane::(channel)); + } + self.encode_ready(false) + } + fn drain_decoder(&mut self) -> Result<()> { + loop { + let mut decoded = frame::Audio::empty(); + match self.decoder.receive_frame(&mut decoded) { + Ok(()) => (), + Err(error) if waiting(error) => break, + Err(error) => return Err(error.into()), + } + let delay = self + .resampler + .delay() + .map_or(0, |delay| delay.output.max(0) as usize); + let capacity = (decoded.samples() as u64 * 48000) + .div_ceil(u64::from(self.decoder.rate())) as usize + + delay + + 32; + let mut converted = frame::Audio::new(self.encoder.format(), capacity, self.layout); + self.resampler.run(&decoded, &mut converted)?; + self.collect(&converted)?; + } + Ok(()) + } +} +fn transcode( + mut input: format::context::Input, + mut output: format::context::Output, + index: usize, + decoder: codec::decoder::Audio, + cancelled: &CancellationToken, +) -> Result<()> { + ensure!( + decoder.rate() > 0 && decoder.ch_layout().channels() > 0, + "Invalid input audio format" + ); + let layout = if decoder.ch_layout().channels() == 1 { + ChannelLayoutMask::MONO + } else { + ChannelLayoutMask::STEREO + }; + let codec = ffmpeg::encoder::find(codec::Id::AAC).context("Bundled AAC encoder unavailable")?; + let mut encoder = codec::Context::new_with_codec(codec).encoder().audio()?; + encoder.set_rate(48000); + encoder.set_format(format::Sample::F32(format::sample::Type::Planar)); + encoder.set_ch_layout(ChannelLayout::from_mask(layout).context("AAC channel layout")?); + encoder.set_bit_rate(if layout == ChannelLayoutMask::MONO { + 64000 + } else { + 128000 + }); + encoder.set_time_base((1, 48000)); + encoder.set_flags(codec::Flags::GLOBAL_HEADER); + let encoder = encoder.open_as(codec)?; + { + let mut stream = output.add_stream(codec)?; + stream.set_time_base((1, 48000)); + stream.set_parameters(codec::Parameters::from(&encoder)); + } + header(&mut output)?; + let output_time = output + .stream(0) + .context("No AAC output stream")? + .time_base(); + let resampler = resampling::Context::get2( + decoder.format(), + decoder.ch_layout(), + decoder.rate(), + encoder.format(), + ChannelLayout::from_mask(layout).context("AAC channel layout")?, + 48000, + )?; + let mut state = Transcoder { + decoder, + encoder, + resampler, + output, + layout, + output_time, + timestamp: 0, + fifo: vec![VecDeque::new(); layout.bits().count_ones() as usize], + }; + loop { + ensure!(!cancelled.is_cancelled(), "Audio conversion cancelled"); + let mut packet = Packet::empty(); + match packet.read(&mut input) { + Ok(()) => (), + Err(ffmpeg::Error::Eof) => break, + Err(error) => return Err(error.into()), + } + if packet.stream() == index { + state.decoder.send_packet(&packet)?; + state.drain_decoder()?; + } + } + state.decoder.send_eof()?; + state.drain_decoder()?; + loop { + ensure!(!cancelled.is_cancelled(), "Audio conversion cancelled"); + let capacity = state + .resampler + .delay() + .map_or(32, |delay| delay.output.max(0) as usize + 32); + let mut converted = frame::Audio::new(state.encoder.format(), capacity, layout); + let remaining = state.resampler.flush(&mut converted)?; + state.collect(&converted)?; + if remaining.is_none() || converted.samples() == 0 { + break; + } + } + state.encode_ready(true)?; + ensure!(state.timestamp > 0, "Downloaded audio was empty"); + state.encoder.send_eof()?; + state.drain_encoder()?; + state.output.write_trailer()?; + Ok(()) +} + +pub fn fast_start(path: &Path) -> Result { + let mut file = File::open(path)?; + let total = file.metadata()?.len(); + let mut offset = 0; + while offset + 8 <= total { + file.seek(SeekFrom::Start(offset))?; + let mut bytes = [0; 8]; + file.read_exact(&mut bytes)?; + let mut size = u64::from(u32::from_be_bytes(bytes[..4].try_into()?)); + let kind = bytes[4..8].to_vec(); + if size == 1 { + file.read_exact(&mut bytes)?; + size = u64::from_be_bytes(bytes); + } + if size < 8 || size > total - offset { + return Ok(false); + } + if kind == b"moov" { + return Ok(true); + } + if kind == b"mdat" { + return Ok(false); + } + offset += size; + } + Ok(false) +} diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..62aea50 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,126 @@ +use crate::publicapi as p; +use crate::{ + api::{Api, string}, + config::{credential_valid, write_private_json}, + events::Events, +}; +use anyhow::{Context, Result, bail, ensure}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use url::Url; + +#[derive(Serialize, Deserialize)] +pub struct StoredAuth { + pub api_key: String, +} + +const SCOPES: &[&str] = &[ + "team:read", + "team:manage", + "show:read", + "show:create", + "show:update", + "episode:create", + "episode:delete", + "episode:update", + "episode:publish", +]; + +pub async fn login(api: &mut Api) -> Result<()> { + let mut response = api + .send( + api.client() + .create_cli_authorization(p::CreateCLIAuthorizationParams { + body: serde_json::from_value(json!({"scopes": SCOPES}))?, + }), + ) + .await?; + if response.status() == reqwest::StatusCode::UNAUTHORIZED && api.credential.is_some() { + api.credential = None; + response = api + .send( + api.client() + .create_cli_authorization(p::CreateCLIAuthorizationParams { + body: serde_json::from_value(json!({"scopes": SCOPES}))?, + }), + ) + .await?; + } + let created = api.accept(response, &[201]).await?; + let credential = string(&created, "credential")?; + ensure!(credential_valid(credential), "invalid pending credential"); + let mut verification = Url::parse(string(&created, "verification_url")?)?; + ensure!( + matches!(verification.scheme(), "http" | "https") + && verification.host_str().is_some() + && verification.username().is_empty() + && verification.password().is_none() + && verification.fragment().is_none(), + "invalid verification URL" + ); + let dashboard = Url::parse(&api.config.dashboard_origin)?; + verification + .set_scheme(dashboard.scheme()) + .map_err(|()| anyhow::anyhow!("invalid dashboard scheme"))?; + verification.set_host(dashboard.host_str())?; + verification + .set_port(dashboard.port()) + .map_err(|()| anyhow::anyhow!("invalid dashboard port"))?; + println!("Verification URL: {verification}"); + let mut events = Events::open( + api, + api.client() + .cli_authorization_events(p::CliAuthorizationEventsParams { + code: string(&created, "code")?.into(), + }), + false, + ) + .await?; + let event = events.next(api).await?; + match string(&event, "type")? { + "cli.authorization.approved" => { + api.credential = Some(credential.to_owned()); + let identity = api.json(api.client().whoami(), &[200]).await?; + ensure!( + string(&identity, "team_id")? == string(&event, "team_id")?, + "approved credential team does not match approved team" + ); + let scopes = identity["scopes"] + .as_array() + .context("identity missing scopes")?; + for scope in SCOPES { + ensure!( + scopes.iter().any(|value| value.as_str() == Some(scope)), + "approved credential missing scope {scope}" + ); + } + write_private_json( + &api.config.directory.join("auth.json"), + &StoredAuth { + api_key: credential.into(), + }, + ) + } + "cli.authorization.denied" => bail!("CLI authorization was denied"), + "cli.authorization.expired" => bail!("CLI authorization expired"), + kind => bail!("unexpected authorization event {kind:?}"), + } +} + +pub async fn status(api: &Api) -> Result<()> { + let identity = api.json(api.client().whoami(), &[200]).await?; + let email = string(&identity, "email")?.trim(); + ensure!(!email.is_empty(), "identity email is empty"); + let name = identity["name"] + .as_str() + .unwrap_or("") + .split_whitespace() + .collect::>() + .join(" "); + if name.is_empty() { + println!("Logged in as {email}"); + } else { + println!("Logged in as {name} <{email}>"); + } + Ok(()) +} diff --git a/src/cliconfig/mod.rs b/src/cliconfig/mod.rs new file mode 100644 index 0000000..6f086b7 --- /dev/null +++ b/src/cliconfig/mod.rs @@ -0,0 +1,15 @@ +// Code generated by oasmith. DO NOT EDIT. +#![allow(dead_code, clippy::large_enum_variant, clippy::enum_variant_names)] +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIConfig { + #[serde(rename = "api_origin")] + pub api_origin: std::string::String, + #[serde(rename = "dashboard_origin")] + pub dashboard_origin: std::string::String, + #[serde(rename = "print_trace_ids")] + pub print_trace_ids: bool, +} + diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..c3159b1 --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,331 @@ +use crate::{ + MemberCommand, ShowCommand, + api::{Api, number, string}, + events::{Events, Progress}, + publicapi as p, +}; +use anyhow::{Context, Result, bail, ensure}; +use serde_json::json; + +pub async fn shows(api: &Api, command: ShowCommand) -> Result<()> { + match command { + ShowCommand::List => { + let result = api.json(api.client().list_shows(), &[200]).await?; + let shows: Vec = serde_json::from_value(result)?; + for show in shows { + println!("{}", show.slug); + } + } + ShowCommand::Create { + title, + slug, + source_kind, + language, + artwork, + } => { + let id = format!("shw_{}", &uuid::Uuid::new_v4().simple().to_string()[..16]); + let image_asset_id = match artwork { + Some(path) => Some(upload_artwork(api, &id, &path).await?), + None => None, + }; + let request = api.client().create_show(p::CreateShowParams { + body: p::CreateShow { + id, + title, + slug: slug.clone(), + language, + image_asset_id, + source_kind: serde_json::from_value(serde_json::to_value(source_kind)?)?, + }, + }); + let response = api.send(request).await?; + if response.status() == reqwest::StatusCode::CONFLICT { + bail!("create show: slug {slug:?} already exists"); + } + let show: p::Show = serde_json::from_value(api.accept(response, &[201]).await?)?; + println!( + "Created show {:?}\nOpen in Listenbox: {}", + show.slug, + api.config.show_url(&show.team_id, &show.id)? + ); + } + ShowCommand::Delete { show, yes } => { + ensure!(yes, "shows delete: --yes is required"); + let result = api + .json( + api.client() + .create_show_deletion(p::CreateShowDeletionParams { + show_slug: show.clone(), + }), + &[202], + ) + .await?; + delete_events( + api, + api.client() + .show_deletion_events(p::ShowDeletionEventsParams { + show_deletion_run_id: string(&result, "show_deletion_run_id")?.into(), + }), + "show", + "show_slug", + &show, + ) + .await?; + } + } + Ok(()) +} + +async fn upload_artwork(api: &Api, show: &str, path: &std::path::Path) -> Result { + let raw = std::fs::read(path).with_context(|| format!("read artwork {}", path.display()))?; + let reader = image::ImageReader::new(std::io::Cursor::new(&raw)).with_guessed_format()?; + let content_type = match reader.format() { + Some(image::ImageFormat::Jpeg) => "image/jpeg", + Some(image::ImageFormat::Png) => "image/png", + _ => bail!("artwork must be a JPEG or PNG image"), + }; + let (width, height) = reader + .into_dimensions() + .context("read artwork dimensions")?; + ensure!( + width == height && (1400..=3000).contains(&width), + "upload a square image between 1400 and 3000 pixels. Attempted resolution: {width} × {height} pixels" + ); + let result = api.json(api.client().create_image_upload_presign(p::CreateImageUploadPresignParams { body: serde_json::from_value(json!({ + "show_id": show, "byte_length": raw.len(), "content_type": content_type, + "file_name": path.file_name().context("artwork filename")?.to_string_lossy(), + }))? }), &[201]).await?; + let response = api + .send( + api.http + .request( + string(&result, "method")?.parse()?, + string(&result, "upload_url")?, + ) + .header("Content-Type", content_type) + .body(raw), + ) + .await?; + ensure!( + response.status().is_success(), + "upload artwork: HTTP {}", + response.status() + ); + let completed = api + .json( + api.client() + .complete_image_upload(p::CompleteImageUploadParams { + image_asset_id: string(&result, "image_asset_id")?.into(), + body: p::CompleteImageUpload { + object_key: string(&result, "object_key")?.into(), + }, + }), + &[201], + ) + .await?; + Ok(string(&completed, "id")?.into()) +} + +pub async fn import_rss(api: &Api, source: &str, slug: Option<&str>) -> Result<()> { + let response = api + .send(api.client().import_rss(p::ImportRSSParams { + body: p::ImportRSSRequest { + source_url: source.into(), + slug: slug.map(str::to_owned), + }, + })) + .await?; + if response.status() == reqwest::StatusCode::CONFLICT { + if let Some(slug) = slug { + bail!("requested slug {slug:?} conflicts"); + } + bail!("import choice conflicts"); + } + if response.status() != reqwest::StatusCode::ACCEPTED { + return Err(api.response_error(response).await); + } + api.trace(&response, true)?; + let created: p::CreatedPublicRSSImport = api.decode(response).await?; + let mut stream = Events::open( + api, + api.client() + .import_rss_run_events(p::ImportRSSRunEventsParams { + import_run_id: created.import_run_id, + }), + false, + ) + .await?; + let mut progress = Progress::default(); + loop { + let event = stream.next(api).await?; + match string(&event, "type")? { + "progress" => progress.update(number(&event, "percent")?)?, + "terminal" => match string(&event, "status")? { + "completed" => { + let slug = + crate::slug(string(&event, "show_slug")?).map_err(anyhow::Error::msg)?; + ensure!( + crate::valid_id(string(&event, "feed_id")?, "lb_"), + "invalid import feed ID" + ); + let location = api + .config + .show_url(string(&event, "team_id")?, string(&event, "show_id")?)?; + progress.update(100)?; + println!("{slug}"); + eprintln!("Open in Listenbox: {location}"); + return Ok(()); + } + "cancelled" => bail!("RSS import was cancelled"), + "failed" => bail!( + "RSS import failed ({}): {}", + string(&event, "error_code")?, + string(&event, "error_message")? + ), + status => bail!("malformed import terminal status {status:?}"), + }, + kind => bail!("malformed import event {kind:?}"), + } + } +} + +pub async fn delete_events( + api: &Api, + request: reqwest::RequestBuilder, + kind: &str, + identity_key: &str, + identity: &str, +) -> Result<()> { + let mut stream = Events::open(api, request, false).await?; + let mut progress = Progress::default(); + loop { + let event = stream.next(api).await?; + match string(&event, "type")? { + "progress" => { + string(&event, "current_step")?; + progress.update(number(&event, "percent")?)?; + } + "terminal" => match string(&event, "status")? { + "completed" => { + ensure!( + string(&event, identity_key)? == identity, + "deletion completed for another {kind}" + ); + progress.update(100)?; + println!("Deleted {kind} {identity:?}"); + return Ok(()); + } + "failed" => bail!( + "delete {kind} failed ({}): {}", + string(&event, "error_code")?, + string(&event, "error_message")? + ), + status => bail!("malformed deletion terminal status {status:?}"), + }, + kind => bail!("malformed deletion event {kind:?}"), + } + } +} + +pub async fn members(api: &Api, command: MemberCommand) -> Result<()> { + let client = api.client(); + match command { + MemberCommand::List { show } => { + let request = match &show { + Some(show) => client.list_show_members(p::ListShowMembersParams { + show_slug: show.clone(), + }), + None => client.list_team_members(), + }; + let result = api.json(request, &[200]).await?; + for member in result["members"].as_array().context("missing members")? { + if show.is_some() { + print!("{}\t", string(member, "access_source")?); + } + println!( + "{}\t{}\t{}", + string(member, "role")?, + string(&member["user"], "email")?, + string(&member["user"], "id")? + ); + } + for invite in result["invitations"] + .as_array() + .context("missing invitations")? + { + let expiry = chrono::DateTime::from_timestamp_millis(number(invite, "expires_at")?) + .context("invalid invitation expiry")?; + println!( + "pending\t{}\t{}\t{}\t{}", + string(invite, "role")?, + string(invite, "email")?, + string(invite, "id")?, + expiry.to_rfc3339_opts(chrono::SecondsFormat::Secs, true) + ); + } + } + MemberCommand::Invite { email, role, show } => { + let body = json!({"email": email.trim().to_lowercase(), "role": role}); + let request = match &show { + Some(show) => client.create_show_invitation(p::CreateShowInvitationParams { + show_slug: show.clone(), + body: serde_json::from_value(body)?, + }), + None => client.create_team_invitation(p::CreateTeamInvitationParams { + body: serde_json::from_value(body)?, + }), + }; + let result = api.json(request, &[201]).await?; + let target = show.map_or(String::new(), |show| format!(" to {show}")); + println!( + "Invited {}{target} as {}.", + string(&result, "email")?, + string(&result, "role")? + ); + } + MemberCommand::Role { member, role, show } => { + let label = if show.is_some() { + "show member" + } else { + "member" + }; + let body = json!({"role": role}); + let request = match show { + Some(show) => client.update_show_member_role(p::UpdateShowMemberRoleParams { + show_slug: show, + user_id: member.clone(), + body: serde_json::from_value(body.clone())?, + }), + None => client.update_team_member_role(p::UpdateTeamMemberRoleParams { + user_id: member.clone(), + body: serde_json::from_value(body.clone())?, + }), + }; + api.json(request, &[204]).await?; + println!( + "Changed {label} {member} to {}.", + body["role"].as_str().unwrap_or("") + ); + } + MemberCommand::Remove { member, yes, show } => { + let label = if show.is_some() { + "show member" + } else { + "member" + }; + ensure!(yes, "members remove: --yes is required"); + let request = match show { + Some(show) => client.remove_show_member(p::RemoveShowMemberParams { + show_slug: show, + user_id: member.clone(), + }), + None => client.remove_team_member(p::RemoveTeamMemberParams { + user_id: member.clone(), + }), + }; + api.json(request, &[204]).await?; + println!("Removed {label} {member}."); + } + } + Ok(()) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b882e3b --- /dev/null +++ b/src/config.rs @@ -0,0 +1,156 @@ +use crate::cliconfig::CLIConfig; +use anyhow::{Context, Result, bail, ensure}; +use serde::Serialize; +use std::{ + fs, + io::Write, + path::{Path, PathBuf}, +}; +use url::Url; + +#[derive(Clone)] +pub struct Config { + values: CLIConfig, + pub directory: PathBuf, +} + +impl std::ops::Deref for Config { + type Target = CLIConfig; + fn deref(&self) -> &Self::Target { + &self.values + } +} + +impl Config { + pub fn load(explicit: Option<&Path>) -> Result { + let home = std::env::var_os("HOME").context("resolve user home directory")?; + Self::load_in(explicit, PathBuf::from(home).join(".config/listenbox")) + } + + fn load_in(explicit: Option<&Path>, directory: PathBuf) -> Result { + let default_path = directory.join("config.yaml"); + let path = explicit.unwrap_or(&default_path); + ensure!( + !path.as_os_str().is_empty(), + "explicit config path is empty" + ); + let mut values = match fs::read(path) { + Ok(raw) => serde_yaml::from_slice::(&raw) + .with_context(|| format!("decode CLI config {}", path.display()))?, + Err(error) if explicit.is_none() && error.kind() == std::io::ErrorKind::NotFound => { + CLIConfig { + api_origin: "https://v1.listenbox.app".into(), + dashboard_origin: "https://web.listenbox.app".into(), + print_trace_ids: false, + } + } + Err(error) => { + return Err(error).with_context(|| format!("load CLI config {}", path.display())); + } + }; + values.api_origin = origin(&values.api_origin).context("validate api_origin")?; + values.dashboard_origin = + origin(&values.dashboard_origin).context("validate dashboard_origin")?; + Ok(Self { values, directory }) + } + + pub fn show_url(&self, team: &str, show: &str) -> Result { + ensure!( + crate::valid_id(team, "team_") && crate::valid_id(show, "shw_"), + "invalid show management identifiers" + ); + Ok(format!("{}/{team}/shows/{show}", self.dashboard_origin)) + } +} + +fn origin(value: &str) -> Result { + let value = value.trim(); + let (_, authority) = value + .split_once("://") + .context("origin must have an explicit HTTP(S) authority")?; + ensure!( + !authority.is_empty() + && !authority.starts_with('/') + && !authority.contains(['@', '\\', '?', '#']) + && authority + .split_once('/') + .is_none_or(|(_, path)| path.is_empty()), + "origin must contain only a host and optional port" + ); + let parsed = Url::parse(value).context("invalid origin URL")?; + ensure!( + matches!(parsed.scheme(), "http" | "https") && parsed.host_str().is_some(), + "origin must be an absolute HTTP(S) URL" + ); + ensure!( + parsed.username().is_empty() && parsed.password().is_none(), + "origin must not include user information" + ); + ensure!( + matches!(parsed.path(), "" | "/") + && parsed.query().is_none() + && parsed.fragment().is_none(), + "origin must not include a path, query or fragment" + ); + Ok(parsed.origin().ascii_serialization()) +} + +pub fn write_private_json(path: &Path, value: &impl Serialize) -> Result<()> { + let directory = path + .parent() + .context("private file requires parent directory")?; + fs::create_dir_all(directory)?; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(directory, fs::Permissions::from_mode(0o700))?; + } + let mut temporary = tempfile::NamedTempFile::new_in(directory)?; + serde_json::to_writer_pretty(&mut temporary, value)?; + temporary.write_all(b"\n")?; + temporary.as_file().sync_all()?; + temporary + .persist(path) + .with_context(|| format!("replace {}", path.display()))?; + #[cfg(unix)] + fs::File::open(directory)?.sync_all()?; + Ok(()) +} + +pub fn credential_valid(value: &str) -> bool { + !value.is_empty() && value.bytes().all(|c| (0x21..=0x7e).contains(&c)) +} + +pub fn check_trace(value: &str) -> Result<()> { + if value.len() != 32 + || !value + .bytes() + .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()) + || value.bytes().all(|c| c == b'0') + { + bail!("invalid X-Trace-Id {value:?}"); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn missing_user_config_uses_production_defaults() { + let home = tempfile::tempdir().unwrap(); + let config = Config::load_in(None, home.path().into()).unwrap(); + assert_eq!(config.api_origin, "https://v1.listenbox.app"); + assert_eq!(config.dashboard_origin, "https://web.listenbox.app"); + assert!(!config.print_trace_ids); + } + #[test] + fn generated_config_decodes_yaml_and_normalizes_origins() { + let home = tempfile::tempdir().unwrap(); + std::fs::write(home.path().join("config.yaml"), "api_origin: http://EXAMPLE.com:80/\ndashboard_origin: https://WEB.example.com:443/\nprint_trace_ids: true\n").unwrap(); + let config = Config::load_in(None, home.path().into()).unwrap(); + assert_eq!(config.api_origin, "http://example.com"); + assert_eq!(config.dashboard_origin, "https://web.example.com"); + assert!(config.print_trace_ids); + } +} diff --git a/src/episodes.rs b/src/episodes.rs new file mode 100644 index 0000000..4a20bf7 --- /dev/null +++ b/src/episodes.rs @@ -0,0 +1,369 @@ +use crate::{ + EpisodeCommand, EpisodeCreate, Publication, + api::{Api, number, string}, + config::write_private_json, + events::Events, + publicapi as p, +}; +use anyhow::{Context, Result, bail, ensure}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use sha2::{Digest, Sha256}; +use std::{collections::HashSet, path::Path}; +use tokio::io::{AsyncReadExt, AsyncSeekExt}; + +#[derive(Serialize, Deserialize)] +struct Resume { + version: u8, + episode_id: String, + upload_session_id: String, + team_id: String, + show_id: String, + management_url: String, + file_path: String, + file_sha256: String, + file_byte_length: u64, + completed_parts: Vec, + expires_at: i64, + phase: String, +} + +pub async fn run(api: &Api, command: EpisodeCommand) -> Result<()> { + match command { + EpisodeCommand::List { show, limit } => { + let mut cursor = None; + let mut seen = HashSet::new(); + loop { + let page = api + .json( + api.client().list_episodes(p::ListEpisodesParams { + show_slug: show.clone(), + limit: Some(i64::from(limit)), + cursor, + }), + &[200], + ) + .await?; + for episode in page["episodes"] + .as_array() + .context("episode page missing episodes")? + { + let id = string(episode, "id")?; + ensure!(crate::valid_id(id, "ep_"), "invalid episode ID in list"); + println!("{id}"); + } + cursor = page["next_cursor"].as_str().map(str::to_owned); + let Some(next) = &cursor else { + return Ok(()); + }; + ensure!( + !next.is_empty() && seen.insert(next.clone()), + "empty or repeated episode list cursor" + ); + } + } + EpisodeCommand::Create(args) => create(api, args).await, + EpisodeCommand::Delete { episode, yes } => { + ensure!(yes, "episodes delete: --yes is required"); + let result = api + .json( + api.client() + .create_episode_deletion(p::CreateEpisodeDeletionParams { + episode_id: episode.clone(), + }), + &[202], + ) + .await?; + crate::commands::delete_events( + api, + api.client() + .episode_deletion_events(p::EpisodeDeletionEventsParams { + episode_deletion_run_id: string(&result, "episode_deletion_run_id")?.into(), + }), + "episode", + "episode_id", + &episode, + ) + .await + } + } +} + +pub async fn file_hash(path: &Path) -> Result<(u64, String)> { + let mut file = tokio::fs::File::open(path) + .await + .with_context(|| format!("open --file {:?}", path))?; + let metadata = file.metadata().await?; + ensure!( + metadata.is_file() && metadata.len() > 0, + "--file must be a non-empty regular file" + ); + let mut buffer = [0; 64 * 1024]; + let mut hash = Sha256::new(); + loop { + let count = file.read(&mut buffer).await?; + if count == 0 { + break; + } + hash.update(&buffer[..count]); + } + Ok((metadata.len(), hex::encode(hash.finalize()))) +} + +async fn create(api: &Api, args: EpisodeCreate) -> Result<()> { + let path = std::path::absolute(&args.file)?; + let (length, hash) = file_hash(&path).await?; + let content_type = match path + .extension() + .and_then(|v| v.to_str()) + .unwrap_or("") + .to_ascii_lowercase() + .as_str() + { + "mp3" => "audio/mpeg", + "m4a" => "audio/mp4", + "wav" => "audio/wav", + "flac" => "audio/flac", + "mp4" | "m4v" => "video/mp4", + "mov" => "video/quicktime", + _ => bail!( + "unsupported media upload file; accepted extensions: .mp3, .m4a, .wav, .flac, .mp4, .m4v, .mov" + ), + }; + let key = hex::encode(Sha256::digest(format!("{}\0{hash}", args.show))); + let resume_path = api + .config + .directory + .join("episode-resume") + .join(format!("{key}.json")); + let mut record = match std::fs::read(&resume_path) { + Ok(raw) => { + let record: Resume = + serde_json::from_slice(&raw).context("decode episode resume record")?; + ensure!( + record.version == 1 + && record.file_sha256 == hash + && record.file_byte_length == length, + "invalid episode resume record" + ); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_millis() as i64; + ensure!( + record.expires_at == 0 + || record.expires_at > now + || ["finalizing", "processing", "completed"].contains(&record.phase.as_str()), + "resumable upload expired; remove {} to restart explicitly", + resume_path.display() + ); + record + } + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Resume { + version: 1, + episode_id: String::new(), + upload_session_id: String::new(), + team_id: String::new(), + show_id: String::new(), + management_url: String::new(), + file_path: path.to_string_lossy().into(), + file_sha256: hash.clone(), + file_byte_length: length, + completed_parts: Vec::new(), + expires_at: 0, + phase: "prepared".into(), + }, + Err(error) => return Err(error.into()), + }; + if !["finalizing", "processing", "completed"].contains(&record.phase.as_str()) { + let request = api.client().create_episode_upload_session(p::CreateEpisodeUploadSessionParams { body: serde_json::from_value(json!({ + "show_slug": args.show, "title": args.title, "file_name": path.file_name().context("source filename")?.to_string_lossy(), + "content_type": content_type, "byte_length": length, "source_sha256": hash, + "description": args.description, + }))? }); + let response = api.send(request).await?; + api.trace(&response, false)?; + let session = api.accept(response, &[200, 201]).await?; + record.upload_session_id = string(&session, "upload_session_id")?.into(); + record.team_id = string(&session, "team_id")?.into(); + record.show_id = string(&session, "show_id")?.into(); + record.episode_id = session["episode_id"].as_str().unwrap_or("").into(); + record.expires_at = number(&session, "expires_at")?; + record.phase = string(&session, "phase")?.into(); + record.completed_parts = serde_json::from_value(session["completed_parts"].clone())?; + write_private_json(&resume_path, &record)?; + if record.phase != "finalizing" { + let part_size = u64::try_from(number(&session, "part_size")?)?; + ensure!(part_size >= 5 << 20, "invalid upload part size {part_size}"); + let count = length.div_ceil(part_size); + ensure!(count <= 10000, "episode source requires too many parts"); + let accepted = record + .completed_parts + .iter() + .map(|p| p.part_number) + .collect::>(); + let missing: Vec<_> = (1..=count as i64) + .filter(|n| !accepted.contains(n)) + .collect(); + saved_progress(&record); + if !missing.is_empty() { + let response = api + .send(api.client().presign_episode_upload_session_parts( + p::PresignEpisodeUploadSessionPartsParams { + upload_session_id: record.upload_session_id.clone(), + body: p::PresignEpisodeUploadSessionParts { + part_numbers: missing.clone(), + }, + }, + )) + .await?; + api.trace(&response, false)?; + let signed = api.accept(response, &[201]).await?; + let parts = signed["parts"].as_array().context("missing signed parts")?; + let mut pending = missing.into_iter().collect::>(); + for part in parts { + let number = number(part, "part_number")?; + ensure!( + pending.remove(&number), + "unexpected or duplicate signed part {number}" + ); + let offset = (number as u64 - 1) * part_size; + let size = part_size.min(length - offset); + let etag = upload_part( + api, + &path, + offset, + size, + string(part, "method")?, + string(part, "upload_url")?, + ) + .await?; + record.completed_parts.push(p::CompletedEpisodeUploadPart { + part_number: number, + etag, + size: size as i64, + }); + record.completed_parts.sort_by_key(|part| part.part_number); + record.phase = "uploading".into(); + write_private_json(&resume_path, &record)?; + saved_progress(&record); + } + ensure!(pending.is_empty(), "signed response omitted upload parts"); + } + } + record.phase = "finalizing".into(); + write_private_json(&resume_path, &record)?; + } + let response = + api.send(api.client().complete_episode_upload_session( + p::CompleteEpisodeUploadSessionParams { + upload_session_id: record.upload_session_id.clone(), + body: serde_json::from_value(json!({"publication": args.publication}))?, + }, + )) + .await?; + api.trace(&response, false)?; + let episode = api.accept(response, &[200, 201]).await?; + record.episode_id = string(&episode, "id")?.into(); + record.show_id = string(&episode, "show_id")?.into(); + ensure!( + crate::valid_id(&record.episode_id, "ep_"), + "invalid episode management identifier" + ); + record.management_url = format!( + "{}/episodes/{}/edit", + api.config.show_url(&record.team_id, &record.show_id)?, + record.episode_id + ); + record.phase = "processing".into(); + write_private_json(&resume_path, &record)?; + let mut events = Events::open( + api, + api.client() + .episode_upload_session_events(p::EpisodeUploadSessionEventsParams { + upload_session_id: record.upload_session_id.clone(), + }), + true, + ) + .await?; + loop { + let event = events.next(api).await?; + match string(&event, "type")? { + "episode.processing.progress" => { + let label = match string(&event, "current_step")? { + "queued" | "processing_audio" | "listenbox_processed" => "Listenbox processing", + "video_preparation" | "video_prepared" => "Video preparation", + "youtube_upload" => "YouTube upload", + _ => "Episode processing", + }; + let percent = number(&event, "percent")?; + ensure!((0..=100).contains(&percent), "invalid processing progress"); + eprintln!("{label}: {percent}%"); + } + "episode.processing.safe_handoff" => { + record.phase = "completed".into(); + write_private_json(&resume_path, &record)?; + let verb = if args.publication == Publication::Publish { + "Published episode" + } else { + "Created draft episode" + }; + println!( + "{verb} {:?}\nOpen in Listenbox: {}", + args.title, record.management_url + ); + if let Some(id) = event["youtube_video_id"].as_str().filter(|s| !s.is_empty()) { + println!("YouTube (processing): https://www.youtube.com/watch?v={id}"); + } + return Ok(()); + } + "episode.processing.failed" => bail!( + "episode processing failed ({}): {}", + string(&event, "error_code")?, + string(&event, "error_message")? + ), + kind => bail!("unknown episode processing event {kind:?}"), + } + } +} + +fn saved_progress(record: &Resume) { + let saved: i64 = record.completed_parts.iter().map(|part| part.size).sum(); + eprintln!( + "Source upload: {}% saved", + (saved as u64 * 100 / record.file_byte_length).min(100) + ); +} + +pub async fn upload_part( + api: &Api, + path: &Path, + offset: u64, + length: u64, + method: &str, + url: &str, +) -> Result { + let mut file = tokio::fs::File::open(path).await?; + file.seek(std::io::SeekFrom::Start(offset)).await?; + let stream = tokio_util::io::ReaderStream::new(file.take(length)); + let response = api + .send( + api.http + .request(method.parse()?, url) + .header("Content-Length", length) + .body(reqwest::Body::wrap_stream(stream)), + ) + .await?; + ensure!( + response.status().is_success(), + "upload source part returned HTTP {}", + response.status() + ); + let etag = response + .headers() + .get("ETag") + .context("upload part returned no ETag")? + .to_str()? + .to_owned(); + api.bytes(response, 64 << 10).await?; + Ok(etag) +} diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..cfb9ac9 --- /dev/null +++ b/src/events.rs @@ -0,0 +1,83 @@ +use crate::api::Api; +use anyhow::{Result, bail, ensure}; +use reqwest::{RequestBuilder, Response}; +use serde_json::Value; + +pub struct Events { + response: Response, + buffer: Vec, + data: String, +} + +impl Events { + pub async fn open(api: &Api, request: RequestBuilder, trace: bool) -> Result { + let response = api.send(request).await?; + if !response.status().is_success() { + return Err(api.response_error(response).await); + } + ensure!( + response + .headers() + .get("Content-Type") + .and_then(|v| v.to_str().ok()) + .is_some_and(|v| v.starts_with("text/event-stream")), + "expected an event stream" + ); + if trace { + api.trace(&response, false)?; + } + Ok(Self { + response, + buffer: Vec::new(), + data: String::new(), + }) + } + + pub async fn next(&mut self, api: &Api) -> Result { + loop { + while let Some(end) = self.buffer.iter().position(|c| *c == b'\n') { + let line = self.buffer.drain(..=end).collect::>(); + let line = std::str::from_utf8(&line)?.trim_end_matches(['\r', '\n']); + if line.is_empty() && !self.data.is_empty() { + let data = std::mem::take(&mut self.data); + return Ok(serde_json::from_str(&data)?); + } + if let Some(value) = line.strip_prefix("data:") { + if !self.data.is_empty() { + self.data.push('\n'); + } + self.data.push_str(value.strip_prefix(' ').unwrap_or(value)); + } + ensure!(self.data.len() <= 1 << 20, "event exceeds size limit"); + } + let chunk = api.wait(async { Ok(self.response.chunk().await?) }).await?; + match chunk { + Some(chunk) => { + ensure!( + self.buffer.len() + chunk.len() <= 1 << 20, + "event line exceeds size limit" + ); + self.buffer.extend_from_slice(&chunk); + } + None => bail!("event stream ended before a terminal event"), + } + } + } +} + +#[derive(Default)] +pub struct Progress(Option); + +impl Progress { + pub fn update(&mut self, percent: i64) -> Result<()> { + ensure!( + (0..=100).contains(&percent), + "malformed progress percent {percent}" + ); + if self.0.is_none_or(|previous| percent > previous) { + eprintln!("Import progress: {percent}%"); + self.0 = Some(percent); + } + Ok(()) + } +} diff --git a/src/innertube.rs b/src/innertube.rs new file mode 100644 index 0000000..505d694 --- /dev/null +++ b/src/innertube.rs @@ -0,0 +1,144 @@ +//! YouTube.js runs inside QuickJS; Rust owns cancellation and all HTTP transfers. +use crate::api::{Api, read_bounded}; +use anyhow::{Context as _, Result, anyhow, bail, ensure}; +use base64::{Engine, prelude::BASE64_STANDARD}; +use llrt_modules::module_builder::ModuleBuilder; +use rquickjs::{ + AsyncContext, AsyncRuntime, Function, Module, Promise, async_with, + function::{Async, Func}, +}; +use serde::Deserialize; +use serde_json::{Value, json}; + +pub struct YouTube { + context: AsyncContext, + _runtime: AsyncRuntime, +} + +impl YouTube { + pub async fn new(api: &Api) -> Result { + let runtime = AsyncRuntime::new()?; + runtime.set_memory_limit(256 * 1024 * 1024).await; + runtime.set_max_stack_size(4 * 1024 * 1024).await; + let cancel = api.cancel.clone(); + runtime + .set_interrupt_handler(Some(Box::new(move || cancel.is_cancelled()))) + .await; + let (resolver, loader, globals) = ModuleBuilder::default().build(); + runtime.set_loader(resolver, loader).await; + let context = AsyncContext::full(&runtime).await?; + let api = api.clone(); + context + .with(|ctx| { + use llrt_utils::primordials::Primordial; + llrt_utils::primordials::BasePrimordials::init(&ctx)?; + globals.attach(&ctx)?; + ctx.globals() + .set("structuredClone", Func::from(clone_value))?; + ctx.globals().set( + "hostFetch", + Func::from(Async(move |request: String| { + let api = api.clone(); + async move { + match fetch(&api, &request).await { + Ok(value) => value.to_string(), + Err(error) => json!({"error": error.to_string()}).to_string(), + } + } + })), + )?; + ctx.eval::<(), _>( + "var console = {log(){},info(){},warn(){},error(){},debug(){}};", + )?; + let module = + Module::declare(ctx.clone(), "youtube", include_str!("../.cache/youtube.js"))?; + let (module, evaluated) = module.eval()?; + evaluated.finish::<()>()?; + ctx.globals().set("YouTubeBridge", module.namespace()?) + }) + .await + .context("initialize YouTube.js")?; + Ok(Self { + context, + _runtime: runtime, + }) + } + + pub async fn call(&self, api: &Api, method: &str, args: Value) -> Result { + let result = api + .wait(async { + let text = async_with!(self.context => |ctx| { + let bridge: rquickjs::Object = ctx.globals().get("YouTubeBridge")?; + let function: Function = bridge.get("call")?; + let promise: Promise = function.call((method, args.to_string()))?; + promise.into_future::().await + }) + .await + .map_err(|error| anyhow!("YouTube.js: {error}"))?; + Ok(serde_json::from_str::(&text)?) + }) + .await?; + if let Some(error) = result["bridge_error"].as_str() { + bail!("YouTube: {error}"); + } + Ok(result) + } +} + +fn clone_value<'js>( + ctx: rquickjs::Ctx<'js>, + value: rquickjs::Value<'js>, + options: rquickjs::function::Opt>, +) -> rquickjs::Result> { + llrt_utils::clone::structured_clone(&ctx, value, options) +} + +#[derive(Deserialize)] +struct Fetch { + url: String, + method: String, + headers: Vec<(String, String)>, + body: Option, +} + +async fn fetch(api: &Api, request: &str) -> Result { + let input: Fetch = serde_json::from_str(request)?; + let url = url::Url::parse(&input.url)?; + let host = url.host_str().unwrap_or(""); + ensure!( + url.scheme() == "https" + && [ + "youtube.com", + "google.com", + "googleapis.com", + "googlevideo.com", + "ytimg.com" + ] + .iter() + .any(|base| host == *base || host.ends_with(&format!(".{base}"))), + "unexpected YouTube API host" + ); + let mut request = api.http.request(input.method.parse()?, url); + for (name, value) in input.headers { + if !["host", "content-length", "cookie"].contains(&name.to_ascii_lowercase().as_str()) { + request = request.header(name, value); + } + } + if let Some(body) = input.body { + request = request.body(BASE64_STANDARD.decode(body)?); + } + let response = api.send(request).await?; + let status = response.status().as_u16(); + let headers = response + .headers() + .iter() + .filter_map(|(key, value)| { + value + .to_str() + .ok() + .map(|value| (key.to_string(), value.to_owned())) + }) + .collect::>(); + let body = api.wait(read_bounded(response, 32 << 20)).await?; + Ok(json!({"status": status, "headers": headers, "body": BASE64_STANDARD.encode(body)})) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7a10c3a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,282 @@ +mod api; +mod audio; +mod auth; +#[rustfmt::skip] +mod cliconfig; +mod commands; +mod config; +mod episodes; +mod events; +mod innertube; +mod media; +#[rustfmt::skip] +mod publicapi; +mod youtube; + +use anyhow::{Result, bail}; +use clap::{Args, Parser, Subcommand, ValueEnum}; +use tokio_util::sync::CancellationToken; + +#[derive(Parser)] +#[command( + name = "listenbox", + version, + about = "Publish and manage podcasts on Listenbox" +)] +struct Cli { + #[arg(long, global = true, help = "Path to CLI config file")] + config: Option, + #[command(subcommand)] + command: Command, +} + +#[derive(Subcommand)] +enum Command { + /// Authorize this CLI with Listenbox + Login, + /// Show current CLI authorization + Auth { + #[command(subcommand)] + command: AuthCommand, + }, + /// Import an RSS feed or public YouTube video/playlist + Import { + #[arg(long)] + slug: Option, + source_url: String, + }, + /// Manage shows by slug + Shows { + #[command(subcommand)] + command: ShowCommand, + }, + /// Manage episodes + Episodes { + #[command(subcommand)] + command: EpisodeCommand, + }, + /// Manage team or show members + Members { + #[command(subcommand)] + command: MemberCommand, + }, +} + +#[derive(Subcommand)] +enum AuthCommand { + Status, +} + +#[derive(Subcommand)] +enum ShowCommand { + List, + Create { + #[arg(long, value_parser = nonblank)] + title: String, + #[arg(long, value_parser = slug)] + slug: String, + #[arg(long = "type", value_enum)] + source_kind: ShowKind, + #[arg(long, value_parser = language)] + language: String, + #[arg(long)] + artwork: Option, + }, + Delete { + #[arg(long, value_parser = slug)] + show: String, + #[arg(long, required = true)] + yes: bool, + }, +} + +#[derive(Clone, Copy, ValueEnum, serde::Serialize)] +#[serde(rename_all = "lowercase")] +enum ShowKind { + Audio, + Video, +} + +#[derive(Subcommand)] +enum EpisodeCommand { + List { + #[arg(long, value_parser = slug)] + show: String, + #[arg(long, default_value_t = 100, value_parser = clap::value_parser!(u16).range(1..=500))] + limit: u16, + }, + Create(EpisodeCreate), + Delete { + #[arg(long, value_parser = episode_id)] + episode: String, + #[arg(long, required = true)] + yes: bool, + }, +} + +#[derive(Args)] +struct EpisodeCreate { + #[arg(long, value_parser = slug)] + show: String, + #[arg(long, value_parser = nonblank)] + title: String, + #[arg(long, value_parser = nonblank)] + description: Option, + #[arg(long)] + file: std::path::PathBuf, + #[arg(long, value_enum, default_value = "draft")] + publication: Publication, +} + +#[derive(Clone, Copy, PartialEq, ValueEnum, serde::Serialize)] +#[serde(rename_all = "lowercase")] +enum Publication { + Draft, + Publish, +} + +#[derive(Subcommand)] +enum MemberCommand { + List { + #[arg(long, value_parser = slug)] + show: Option, + }, + Invite { + #[arg(long, value_parser = nonblank)] + email: String, + #[arg(long, value_enum)] + role: Role, + #[arg(long, value_parser = slug)] + show: Option, + }, + Role { + #[arg(long, value_parser = nonblank)] + member: String, + #[arg(long, value_enum)] + role: Role, + #[arg(long, value_parser = slug)] + show: Option, + }, + Remove { + #[arg(long, value_parser = nonblank)] + member: String, + #[arg(long, required = true)] + yes: bool, + #[arg(long, value_parser = slug)] + show: Option, + }, +} + +#[derive(Clone, Copy, ValueEnum, serde::Serialize)] +#[serde(rename_all = "lowercase")] +enum Role { + Read, + Write, +} + +fn nonblank(value: &str) -> Result { + let value = value.trim(); + if value.is_empty() { + return Err("must not be blank".into()); + } + Ok(value.into()) +} + +fn slug(value: &str) -> Result { + let value = nonblank(value)?; + if value.split('-').any(|part| { + part.is_empty() + || !part + .bytes() + .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()) + }) { + return Err("must use lowercase letters, numbers, and single hyphens".into()); + } + Ok(value) +} + +fn language(value: &str) -> Result { + let value = nonblank(value)?; + let bytes = value.as_bytes(); + if !((bytes.len() == 2 || bytes.len() == 5) + && bytes[..2].iter().all(u8::is_ascii_lowercase) + && (bytes.len() == 2 + || (bytes[2] == b'-' && bytes[3..].iter().all(u8::is_ascii_uppercase)))) + { + return Err("must use a language code such as en or en-US".into()); + } + Ok(value) +} + +fn valid_id(value: &str, prefix: &str) -> bool { + value.strip_prefix(prefix).is_some_and(|suffix| { + suffix.len() == 16 + && suffix + .bytes() + .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()) + }) +} + +fn episode_id(value: &str) -> Result { + if !valid_id(value, "ep_") { + return Err(format!("invalid episode ID {value:?}")); + } + Ok(value.into()) +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> std::process::ExitCode { + let cli = Cli::parse(); + let cancel = CancellationToken::new(); + let signal_cancel = cancel.clone(); + tokio::spawn(async move { + #[cfg(unix)] + { + match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) { + Ok(mut terminate) => { + tokio::select! { _ = tokio::signal::ctrl_c() => {}, _ = terminate.recv() => {} } + } + Err(_) => { + let _ = tokio::signal::ctrl_c().await; + } + } + } + #[cfg(not(unix))] + let _ = tokio::signal::ctrl_c().await; + signal_cancel.cancel(); + }); + match run(cli, cancel.clone()).await { + Ok(()) => std::process::ExitCode::SUCCESS, + Err(error) => { + eprintln!("{error:#}"); + std::process::ExitCode::from(if cancel.is_cancelled() { 130 } else { 1 }) + } + } +} + +async fn run(cli: Cli, cancel: CancellationToken) -> Result<()> { + let config = config::Config::load(cli.config.as_deref())?; + let mut api = api::Api::new(config, cancel)?; + match cli.command { + Command::Login => auth::login(&mut api).await, + command => { + if api.credential.is_none() { + bail!("not logged in; run listenbox login"); + } + match command { + Command::Auth { .. } => auth::status(&api).await, + Command::Import { slug, source_url } => { + if youtube::is_source(&source_url) { + youtube::import(&api, &source_url, slug.as_deref()).await + } else { + commands::import_rss(&api, &source_url, slug.as_deref()).await + } + } + Command::Shows { command } => commands::shows(&api, command).await, + Command::Episodes { command } => episodes::run(&api, command).await, + Command::Members { command } => commands::members(&api, command).await, + Command::Login => unreachable!(), + } + } + } +} diff --git a/src/media.rs b/src/media.rs new file mode 100644 index 0000000..e8bd886 --- /dev/null +++ b/src/media.rs @@ -0,0 +1,218 @@ +use anyhow::{Context, Result, ensure}; +use ffmpeg::{Dictionary, Packet, codec, encoder, format, media}; +use std::path::Path; +use tokio_util::sync::CancellationToken; + +pub fn prepare(directory: &Path, separate_audio: bool, cancel: &CancellationToken) -> Result { + ffmpeg::init()?; + ffmpeg::log::set_level(ffmpeg::log::Level::Error); + let video = directory.join("source-video"); + let audio = directory.join(if separate_audio { + "source-audio" + } else { + "source-video" + }); + let prepared_audio = directory.join("audio.m4a"); + crate::audio::prepare_m4a(&audio, &prepared_audio, cancel)?; + let mp4 = directory.join("video.mp4"); + mux(&video, &prepared_audio, &mp4, cancel)?; + let mut input = format::input(&mp4)?; + let stream = input + .streams() + .best(media::Type::Video) + .context("prepared video has no video stream")?; + let decoder = codec::Context::from_parameters(stream.parameters())? + .decoder() + .video()?; + let (width, height) = (decoder.width(), decoder.height()); + ensure!(width > 0 && height > 0, "invalid video dimensions"); + for (name, kind) in [("video", media::Type::Video), ("audio", media::Type::Audio)] { + let root = directory.join("hls").join(name); + std::fs::create_dir_all(&root)?; + let output = root.join("index.m3u8"); + let mut options = Dictionary::new(); + options.set("hls_time", "6"); + options.set("hls_playlist_type", "vod"); + options.set("hls_segment_type", "fmp4"); + options.set("hls_fmp4_init_filename", "init.mp4"); + options.set( + "hls_segment_filename", + root.join("segment-%05d.m4s") + .to_str() + .context("non-UTF-8 media path")?, + ); + remux(&mut input, &mp4, &output, kind, options, cancel)?; + } + std::fs::write( + directory.join("hls/master.m3u8"), + format!( + "#EXTM3U\n#EXT-X-VERSION:7\n#EXT-X-INDEPENDENT-SEGMENTS\n#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",NAME=\"Audio\",DEFAULT=YES,AUTOSELECT=YES,URI=\"audio/index.m3u8\"\n#EXT-X-STREAM-INF:BANDWIDTH=12000000,RESOLUTION={width}x{height},AUDIO=\"audio\"\nvideo/index.m3u8\n" + ), + )?; + let duration = playlist_duration(&directory.join("hls/video/index.m3u8"))? + .min(playlist_duration(&directory.join("hls/audio/index.m3u8"))?); + Ok(duration.ceil() as i64) +} + +fn add_stream( + input: &format::context::Input, + output: &mut format::context::Output, + kind: media::Type, +) -> Result<(usize, usize, ffmpeg::Rational)> { + let source = input + .streams() + .best(kind) + .context("source missing required media stream")?; + let mut stream = output.add_stream(encoder::find(codec::Id::None))?; + stream.set_parameters(source.parameters()); + stream.set_time_base(source.time_base()); + // SAFETY: the uniquely borrowed output stream owns these codec parameters. + unsafe { + (*stream.parameters_mut().as_mut_ptr()).codec_tag = 0; + } + Ok((source.index(), stream.index(), source.time_base())) +} + +fn packets( + input: &mut format::context::Input, + output: &mut format::context::Output, + mapping: (usize, usize, ffmpeg::Rational), + cancel: &CancellationToken, +) -> Result<()> { + let (source, target, time_base) = mapping; + let output_time = output + .stream(target) + .context("missing output stream")? + .time_base(); + let mut count = 0; + loop { + ensure!(!cancel.is_cancelled(), "media preparation interrupted"); + let mut packet = Packet::empty(); + match packet.read(input) { + Ok(()) => {} + Err(ffmpeg::Error::Eof) => break, + Err(error) => return Err(error.into()), + } + if packet.stream() == source { + packet.rescale_ts(time_base, output_time); + packet.set_stream(target); + packet.set_position(-1); + packet.write_interleaved(output)?; + count += 1; + } + } + ensure!(count > 0, "downloaded media stream was empty"); + Ok(()) +} + +fn mux(video: &Path, audio: &Path, destination: &Path, cancel: &CancellationToken) -> Result<()> { + let mut video = format::input(video)?; + let mut audio = format::input(audio)?; + let mut output = format::output_as(destination, "mp4")?; + let video_mapping = add_stream(&video, &mut output, media::Type::Video)?; + let audio_mapping = add_stream(&audio, &mut output, media::Type::Audio)?; + let mut options = Dictionary::new(); + options.set("movflags", "+faststart"); + output.write_header_with(options)?; + // Keep only one packet per input while muxing in decoding timestamp order. + // Feeding a whole input first can make the interleaver buffer long recordings. + let mut video_packet = next_packet(&mut video, video_mapping.0, cancel)?; + let mut audio_packet = next_packet(&mut audio, audio_mapping.0, cancel)?; + ensure!( + video_packet.is_some() && audio_packet.is_some(), + "downloaded media stream was empty" + ); + while video_packet.is_some() || audio_packet.is_some() { + let take_video = match (&video_packet, &audio_packet) { + (Some(video), Some(audio)) => { + timestamp(video, video_mapping.2) <= timestamp(audio, audio_mapping.2) + } + (Some(_), None) => true, + _ => false, + }; + let (slot, input, mapping) = if take_video { + (&mut video_packet, &mut video, video_mapping) + } else { + (&mut audio_packet, &mut audio, audio_mapping) + }; + let mut packet = slot.take().context("missing mux packet")?; + let output_time = output + .stream(mapping.1) + .context("missing output stream")? + .time_base(); + packet.rescale_ts(mapping.2, output_time); + packet.set_stream(mapping.1); + packet.set_position(-1); + packet.write_interleaved(&mut output)?; + *slot = next_packet(input, mapping.0, cancel)?; + } + output.write_trailer()?; + Ok(()) +} + +fn timestamp(packet: &Packet, time: ffmpeg::Rational) -> i64 { + use ffmpeg::Rescale; + packet + .dts() + .or_else(|| packet.pts()) + .unwrap_or(0) + .rescale(time, (1, 1_000_000)) +} + +fn next_packet( + input: &mut format::context::Input, + stream: usize, + cancel: &CancellationToken, +) -> Result> { + loop { + ensure!(!cancel.is_cancelled(), "media preparation interrupted"); + let mut packet = Packet::empty(); + match packet.read(input) { + Ok(()) if packet.stream() == stream => return Ok(Some(packet)), + Ok(()) => {} + Err(ffmpeg::Error::Eof) => return Ok(None), + Err(error) => return Err(error.into()), + } + } +} + +fn remux( + input: &mut format::context::Input, + source: &Path, + destination: &Path, + kind: media::Type, + options: Dictionary, + cancel: &CancellationToken, +) -> Result<()> { + *input = format::input(source)?; + let mut output = format::output_as(destination, "hls")?; + let mapping = add_stream(input, &mut output, kind)?; + output.write_header_with(options)?; + packets(input, &mut output, mapping, cancel)?; + output.write_trailer()?; + Ok(()) +} + +fn playlist_duration(path: &Path) -> Result { + let text = std::fs::read_to_string(path)?; + let mut total = 0.0; + for line in text.lines() { + if let Some(value) = line.strip_prefix("#EXTINF:") { + let seconds: f64 = value + .split(',') + .next() + .context("missing HLS duration")? + .parse()?; + ensure!( + seconds.is_finite() && seconds > 0.0, + "invalid HLS segment duration" + ); + total += seconds; + } + } + ensure!( + total.is_finite() && total > 0.0, + "playlist has no valid segments" + ); + Ok(total) +} diff --git a/src/publicapi/mod.rs b/src/publicapi/mod.rs new file mode 100644 index 0000000..49e3376 --- /dev/null +++ b/src/publicapi/mod.rs @@ -0,0 +1,2940 @@ +// Code generated by oasmith. DO NOT EDIT. +#![allow(dead_code, clippy::large_enum_variant, clippy::enum_variant_names)] +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct APIKeyWhoami { + #[serde(rename = "email")] + pub email: std::string::String, + #[serde(rename = "id")] + pub id: ApiKeyID, + #[serde(rename = "name", default, skip_serializing_if = "Option::is_none")] + pub name: std::option::Option, + #[serde(rename = "scopes")] + pub scopes: std::vec::Vec, + #[serde(rename = "team_id")] + pub team_id: TeamID, +} + +pub type ApiKeyID = std::string::String; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ApiKeyScope { + #[serde(rename = "team:read")] + TeamRead, + #[serde(rename = "team:manage")] + TeamManage, + #[serde(rename = "show:read")] + ShowRead, + #[serde(rename = "show:create")] + ShowCreate, + #[serde(rename = "show:update")] + ShowUpdate, + #[serde(rename = "episode:create")] + EpisodeCreate, + #[serde(rename = "episode:delete")] + EpisodeDelete, + #[serde(rename = "episode:update")] + EpisodeUpdate, + #[serde(rename = "episode:publish")] + EpisodePublish, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum AssignableTeamRole { + #[serde(rename = "write")] + Write, + #[serde(rename = "read")] + Read, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationApprovedEvent { + #[serde(rename = "team_id")] + pub team_id: TeamID, + #[serde(rename = "type")] + pub r#type: CLIAuthorizationApprovedEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationApprovedEventType { + #[serde(rename = "cli.authorization.approved")] + CliAuthorizationApproved, +} + +pub type CLIAuthorizationCode = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationDeniedEvent { + #[serde(rename = "type")] + pub r#type: CLIAuthorizationDeniedEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationDeniedEventType { + #[serde(rename = "cli.authorization.denied")] + CliAuthorizationDenied, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CLIAuthorizationEvent { + Variant0(CLIAuthorizationEventVariant0), + Variant1(CLIAuthorizationEventVariant1), + Variant2(CLIAuthorizationEventVariant2), + Variant3(CLIAuthorizationEventVariant3), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationEventVariant0 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: CLIAuthorizationEventVariant0Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationEventVariant0Event { + #[serde(rename = "cli.authorization.pending")] + CliAuthorizationPending, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationEventVariant1 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: CLIAuthorizationEventVariant1Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationEventVariant1Event { + #[serde(rename = "cli.authorization.approved")] + CliAuthorizationApproved, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationEventVariant2 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: CLIAuthorizationEventVariant2Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationEventVariant2Event { + #[serde(rename = "cli.authorization.denied")] + CliAuthorizationDenied, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationEventVariant3 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: CLIAuthorizationEventVariant3Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationEventVariant3Event { + #[serde(rename = "cli.authorization.expired")] + CliAuthorizationExpired, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationExpiredEvent { + #[serde(rename = "type")] + pub r#type: CLIAuthorizationExpiredEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationExpiredEventType { + #[serde(rename = "cli.authorization.expired")] + CliAuthorizationExpired, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CLIAuthorizationPendingEvent { + #[serde(rename = "credential")] + pub credential: std::string::String, + #[serde(rename = "type")] + pub r#type: CLIAuthorizationPendingEventType, + #[serde(rename = "verification_url")] + pub verification_url: std::string::String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CLIAuthorizationPendingEventType { + #[serde(rename = "cli.authorization.pending")] + CliAuthorizationPending, +} + +#[derive(Clone, Debug)] +pub struct CancelEpisodePackageParams { + pub upload_session_id: UploadSessionID, +} + +#[derive(Debug)] +pub enum CancelEpisodePackageResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CancelEpisodePackageResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct CancelEpisodeUploadSessionParams { + pub upload_session_id: UploadSessionID, +} + +#[derive(Debug)] +pub enum CancelEpisodeUploadSessionResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status404(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CancelEpisodeUploadSessionResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct CliAuthorizationEventsParams { + pub code: CLIAuthorizationCode, +} + +#[derive(Debug)] +pub enum CliAuthorizationEventsResponse { + Status200(reqwest::Response), + Status400(ValidationErr), + Status404(()), + Unexpected(reqwest::Response), +} +impl CliAuthorizationEventsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response), + 400 => Self::Status400(response.json().await?), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct CompleteEpisodePackageParams { + pub upload_session_id: UploadSessionID, +} + +#[derive(Debug)] +pub enum CompleteEpisodePackageResponse { + Status200(EpisodePackage), + Status400(ValidationErr), + Status401(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CompleteEpisodePackageResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CompleteEpisodeUploadSession { + #[serde(rename = "publication")] + pub publication: EpisodePublication, +} + +#[derive(Clone, Debug)] +pub struct CompleteEpisodeUploadSessionParams { + pub upload_session_id: UploadSessionID, + pub body: CompleteEpisodeUploadSession, +} + +#[derive(Debug)] +pub enum CompleteEpisodeUploadSessionResponse { + Status200(Episode), + Status201(Episode), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Status409(()), + Status410(()), + Unexpected(reqwest::Response), +} +impl CompleteEpisodeUploadSessionResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + 410 => Self::Status410(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CompleteImageUpload { + #[serde(rename = "object_key")] + pub object_key: ImageUploadObjectKey, +} + +#[derive(Clone, Debug)] +pub struct CompleteImageUploadParams { + pub image_asset_id: ImageAssetID, + pub body: CompleteImageUpload, +} + +#[derive(Debug)] +pub enum CompleteImageUploadResponse { + Status201(ImageAsset), + Status400(()), + Status401(()), + Status403(()), + Status404(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CompleteImageUploadResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(()), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CompletedEpisodeUploadPart { + #[serde(rename = "etag")] + pub etag: NonEmptyString, + #[serde(rename = "part_number")] + pub part_number: MediaUploadPartNumber, + #[serde(rename = "size")] + pub size: i64, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateCLIAuthorization { + #[serde(rename = "scopes")] + pub scopes: std::vec::Vec, +} + +#[derive(Clone, Debug)] +pub struct CreateCLIAuthorizationParams { + pub body: CreateCLIAuthorization, +} + +#[derive(Debug)] +pub enum CreateCLIAuthorizationResponse { + Status201(CreatedCLIAuthorization), + Status400(ValidationErr), + Status401(()), + Unexpected(reqwest::Response), +} +impl CreateCLIAuthorizationResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct CreateEpisodeDeletionParams { + pub episode_id: EpisodeID, +} + +#[derive(Debug)] +pub enum CreateEpisodeDeletionResponse { + Status202(CreatedPublicEpisodeDeletion), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl CreateEpisodeDeletionResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 202 => Self::Status202(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateEpisodePackage { + #[serde(rename = "description", default, skip_serializing_if = "Option::is_none")] + pub description: std::option::Option, + #[serde(rename = "duration_seconds")] + pub duration_seconds: i64, + #[serde(rename = "objects")] + pub objects: std::vec::Vec, + #[serde(rename = "show_slug")] + pub show_slug: ShowSlug, + #[serde(rename = "source_url")] + pub source_url: std::string::String, + #[serde(rename = "title")] + pub title: std::string::String, +} + +#[derive(Clone, Debug)] +pub struct CreateEpisodePackageParams { + pub body: CreateEpisodePackage, +} + +#[derive(Debug)] +pub enum CreateEpisodePackageResponse { + Status201(EpisodePackage), + Status400(ValidationErr), + Status401(()), + Status402(VideoAdmissionError), + Status404(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CreateEpisodePackageResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 402 => Self::Status402(response.json().await?), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateEpisodeUploadSession { + #[serde(rename = "byte_length")] + pub byte_length: i64, + #[serde(rename = "content_type")] + pub content_type: EpisodeUploadContentType, + #[serde(rename = "description", default, skip_serializing_if = "Option::is_none")] + pub description: std::option::Option, + #[serde(rename = "file_name")] + pub file_name: NonEmptyString, + #[serde(rename = "show_slug")] + pub show_slug: ShowSlug, + #[serde(rename = "source_sha256")] + pub source_sha256: SHA256Hex, + #[serde(rename = "thumbnail_url", default, skip_serializing_if = "Option::is_none")] + pub thumbnail_url: std::option::Option, + #[serde(rename = "title")] + pub title: NonEmptyString, +} + +#[derive(Clone, Debug)] +pub struct CreateEpisodeUploadSessionParams { + pub body: CreateEpisodeUploadSession, +} + +#[derive(Debug)] +pub enum CreateEpisodeUploadSessionResponse { + Status200(EpisodeUploadSession), + Status201(EpisodeUploadSession), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Status409(()), + Status410(()), + Unexpected(reqwest::Response), +} +impl CreateEpisodeUploadSessionResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + 410 => Self::Status410(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateImageUploadPresign { + #[serde(rename = "byte_length")] + pub byte_length: i64, + #[serde(rename = "content_type")] + pub content_type: CreateImageUploadPresignContentType, + #[serde(rename = "file_name", default, skip_serializing_if = "Option::is_none")] + pub file_name: std::option::Option, + #[serde(rename = "show_id")] + pub show_id: ShowID, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CreateImageUploadPresignContentType { + #[serde(rename = "image/jpeg")] + ImageJpeg, + #[serde(rename = "image/png")] + ImagePng, +} + +#[derive(Clone, Debug)] +pub struct CreateImageUploadPresignParams { + pub body: CreateImageUploadPresign, +} + +#[derive(Debug)] +pub enum CreateImageUploadPresignResponse { + Status201(CreatedImageUploadPresign), + Status400(()), + Status401(()), + Status402(()), + Status403(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CreateImageUploadPresignResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(()), + 401 => Self::Status401(()), + 402 => Self::Status402(()), + 403 => Self::Status403(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateShow { + #[serde(rename = "id")] + pub id: ShowID, + #[serde(rename = "image_asset_id", default, skip_serializing_if = "Option::is_none")] + pub image_asset_id: std::option::Option, + #[serde(rename = "language")] + pub language: ShowLanguage, + #[serde(rename = "slug")] + pub slug: ShowSlug, + #[serde(rename = "source_kind")] + pub source_kind: ShowSourceKind, + #[serde(rename = "title")] + pub title: NonEmptyString, +} + +#[derive(Clone, Debug)] +pub struct CreateShowDeletionParams { + pub show_slug: ShowSlug, +} + +#[derive(Debug)] +pub enum CreateShowDeletionResponse { + Status202(CreatedPublicShowDeletion), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl CreateShowDeletionResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 202 => Self::Status202(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateShowInvitation { + #[serde(rename = "email")] + pub email: std::string::String, + #[serde(rename = "role")] + pub role: AssignableTeamRole, +} + +#[derive(Clone, Debug)] +pub struct CreateShowInvitationParams { + pub show_slug: ShowSlug, + pub body: CreateShowInvitation, +} + +#[derive(Debug)] +pub enum CreateShowInvitationResponse { + Status201(PendingShowInvitation), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CreateShowInvitationResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct CreateShowParams { + pub body: CreateShow, +} + +#[derive(Debug)] +pub enum CreateShowResponse { + Status201(Show), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CreateShowResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreateTeamInvitation { + #[serde(rename = "email")] + pub email: std::string::String, + #[serde(rename = "role")] + pub role: AssignableTeamRole, +} + +#[derive(Clone, Debug)] +pub struct CreateTeamInvitationParams { + pub body: CreateTeamInvitation, +} + +#[derive(Debug)] +pub enum CreateTeamInvitationResponse { + Status201(PendingTeamInvitation), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl CreateTeamInvitationResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreatedCLIAuthorization { + #[serde(rename = "code")] + pub code: CLIAuthorizationCode, + #[serde(rename = "credential")] + pub credential: std::string::String, + #[serde(rename = "verification_url")] + pub verification_url: std::string::String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreatedImageUploadPresign { + #[serde(rename = "content_type")] + pub content_type: CreatedImageUploadPresignContentType, + #[serde(rename = "expires_at")] + pub expires_at: i64, + #[serde(rename = "image_asset_id")] + pub image_asset_id: ImageAssetID, + #[serde(rename = "method")] + pub method: CreatedImageUploadPresignMethod, + #[serde(rename = "object_key")] + pub object_key: ImageUploadObjectKey, + #[serde(rename = "public_url")] + pub public_url: std::string::String, + #[serde(rename = "upload_url")] + pub upload_url: std::string::String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CreatedImageUploadPresignContentType { + #[serde(rename = "image/jpeg")] + ImageJpeg, + #[serde(rename = "image/png")] + ImagePng, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum CreatedImageUploadPresignMethod { + #[serde(rename = "PUT")] + Put, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreatedPublicEpisodeDeletion { + #[serde(rename = "episode_deletion_run_id")] + pub episode_deletion_run_id: EpisodeDeletionRunID, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreatedPublicRSSImport { + #[serde(rename = "import_run_id")] + pub import_run_id: ImportRunID, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct CreatedPublicShowDeletion { + #[serde(rename = "show_deletion_run_id")] + pub show_deletion_run_id: ShowDeletionRunID, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Episode { + #[serde(rename = "description", default, skip_serializing_if = "Option::is_none")] + pub description: std::option::Option, + #[serde(rename = "enclosures")] + pub enclosures: std::vec::Vec, + #[serde(rename = "id")] + pub id: EpisodeID, + #[serde(rename = "link", default, skip_serializing_if = "Option::is_none")] + pub link: std::option::Option, + #[serde(rename = "processing", default, skip_serializing_if = "Option::is_none")] + pub processing: std::option::Option, + #[serde(rename = "republished_at", default, skip_serializing_if = "Option::is_none")] + pub republished_at: std::option::Option, + #[serde(rename = "show_id")] + pub show_id: ShowID, + #[serde(rename = "slug", default, skip_serializing_if = "Option::is_none")] + pub slug: std::option::Option, + #[serde(rename = "status")] + pub status: EpisodeStatus, + #[serde(rename = "thumbnail_url", default, skip_serializing_if = "Option::is_none")] + pub thumbnail_url: std::option::Option, + #[serde(rename = "title")] + pub title: NonEmptyString, + #[serde(rename = "video_hls_url", default, skip_serializing_if = "Option::is_none")] + pub video_hls_url: std::option::Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeDeletionCompletedEvent { + #[serde(rename = "episode_deletion_run_id")] + pub episode_deletion_run_id: EpisodeDeletionRunID, + #[serde(rename = "episode_id")] + pub episode_id: EpisodeID, + #[serde(rename = "status")] + pub status: EpisodeDeletionCompletedEventStatus, + #[serde(rename = "type")] + pub r#type: EpisodeDeletionCompletedEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionCompletedEventStatus { + #[serde(rename = "completed")] + Completed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionCompletedEventType { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum EpisodeDeletionEvent { + Variant0(EpisodeDeletionEventVariant0), + Variant1(EpisodeDeletionEventVariant1), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeDeletionEventVariant0 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: EpisodeDeletionEventVariant0Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionEventVariant0Event { + #[serde(rename = "progress")] + Progress, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeDeletionEventVariant1 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: EpisodeDeletionEventVariant1Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionEventVariant1Event { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug)] +pub struct EpisodeDeletionEventsParams { + pub episode_deletion_run_id: EpisodeDeletionRunID, +} + +#[derive(Debug)] +pub enum EpisodeDeletionEventsResponse { + Status200(reqwest::Response), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl EpisodeDeletionEventsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeDeletionFailedEvent { + #[serde(rename = "episode_deletion_run_id")] + pub episode_deletion_run_id: EpisodeDeletionRunID, + #[serde(rename = "error_code")] + pub error_code: NonEmptyString, + #[serde(rename = "error_message")] + pub error_message: NonEmptyString, + #[serde(rename = "status")] + pub status: EpisodeDeletionFailedEventStatus, + #[serde(rename = "type")] + pub r#type: EpisodeDeletionFailedEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionFailedEventStatus { + #[serde(rename = "failed")] + Failed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionFailedEventType { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeDeletionProgressEvent { + #[serde(rename = "current_step")] + pub current_step: NonEmptyString, + #[serde(rename = "episode_deletion_run_id")] + pub episode_deletion_run_id: EpisodeDeletionRunID, + #[serde(rename = "percent")] + pub percent: i64, + #[serde(rename = "status")] + pub status: EpisodeDeletionProgressEventStatus, + #[serde(rename = "type")] + pub r#type: EpisodeDeletionProgressEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionProgressEventStatus { + #[serde(rename = "queued")] + Queued, + #[serde(rename = "processing")] + Processing, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeDeletionProgressEventType { + #[serde(rename = "progress")] + Progress, +} + +pub type EpisodeDeletionRunID = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum EpisodeDeletionTerminalEvent { + Variant0(EpisodeDeletionCompletedEvent), + Variant1(EpisodeDeletionFailedEvent), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeEnclosure { + #[serde(rename = "byte_length")] + pub byte_length: i64, + #[serde(rename = "content_type")] + pub content_type: NonEmptyString, + #[serde(rename = "duration_seconds")] + pub duration_seconds: i64, + #[serde(rename = "feed_id")] + pub feed_id: FeedID, + #[serde(rename = "format")] + pub format: EpisodeEnclosureFormat, + #[serde(rename = "url")] + pub url: std::string::String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeEnclosureFormat { + #[serde(rename = "mp3")] + Mp3, + #[serde(rename = "m4a")] + M4a, + #[serde(rename = "wav")] + Wav, + #[serde(rename = "flac")] + Flac, + #[serde(rename = "mp4")] + Mp4, +} + +pub type EpisodeID = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeListItem { + #[serde(rename = "audio_url", default, skip_serializing_if = "Option::is_none")] + pub audio_url: std::option::Option, + #[serde(rename = "description", default, skip_serializing_if = "Option::is_none")] + pub description: std::option::Option, + #[serde(rename = "duration_seconds", default, skip_serializing_if = "Option::is_none")] + pub duration_seconds: std::option::Option, + #[serde(rename = "id")] + pub id: EpisodeID, + #[serde(rename = "processing", default, skip_serializing_if = "Option::is_none")] + pub processing: std::option::Option, + #[serde(rename = "republished_at", default, skip_serializing_if = "Option::is_none")] + pub republished_at: std::option::Option, + #[serde(rename = "show_id")] + pub show_id: ShowID, + #[serde(rename = "slug", default, skip_serializing_if = "Option::is_none")] + pub slug: std::option::Option, + #[serde(rename = "status")] + pub status: EpisodeStatus, + #[serde(rename = "thumbnail_url", default, skip_serializing_if = "Option::is_none")] + pub thumbnail_url: std::option::Option, + #[serde(rename = "title")] + pub title: NonEmptyString, + #[serde(rename = "video_hls_url", default, skip_serializing_if = "Option::is_none")] + pub video_hls_url: std::option::Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodePackage { + #[serde(rename = "episode_id")] + pub episode_id: EpisodeID, + #[serde(rename = "object_prefix")] + pub object_prefix: NonEmptyString, + #[serde(rename = "part_size")] + pub part_size: i64, + #[serde(rename = "show_id")] + pub show_id: ShowID, + #[serde(rename = "status")] + pub status: EpisodePackageStatus, + #[serde(rename = "team_id")] + pub team_id: TeamID, + #[serde(rename = "upload_session_id")] + pub upload_session_id: UploadSessionID, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodePackageStatus { + #[serde(rename = "uploading")] + Uploading, + #[serde(rename = "completed")] + Completed, + #[serde(rename = "failed")] + Failed, + #[serde(rename = "expired")] + Expired, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodePage { + #[serde(rename = "episodes")] + pub episodes: std::vec::Vec, + #[serde(rename = "next_cursor", default, skip_serializing_if = "Option::is_none")] + pub next_cursor: std::option::Option, +} + +pub type EpisodePageCursor = std::string::String; + +pub type EpisodePageLimit = i64; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum EpisodeProcessingEvent { + Variant0(EpisodeProcessingEventVariant0), + Variant1(EpisodeProcessingEventVariant1), + Variant2(EpisodeProcessingEventVariant2), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeProcessingEventVariant0 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: EpisodeProcessingEventVariant0Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingEventVariant0Event { + #[serde(rename = "episode.processing.progress")] + EpisodeProcessingProgress, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeProcessingEventVariant1 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: EpisodeProcessingEventVariant1Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingEventVariant1Event { + #[serde(rename = "episode.processing.safe_handoff")] + EpisodeProcessingSafeHandoff, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeProcessingEventVariant2 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: EpisodeProcessingEventVariant2Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingEventVariant2Event { + #[serde(rename = "episode.processing.failed")] + EpisodeProcessingFailed, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeProcessingFailedEvent { + #[serde(rename = "episode_id")] + pub episode_id: EpisodeID, + #[serde(rename = "error_code")] + pub error_code: NonEmptyString, + #[serde(rename = "error_message")] + pub error_message: NonEmptyString, + #[serde(rename = "run_id")] + pub run_id: ProcessingRunID, + #[serde(rename = "status")] + pub status: EpisodeProcessingFailedEventStatus, + #[serde(rename = "type")] + pub r#type: EpisodeProcessingFailedEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingFailedEventStatus { + #[serde(rename = "failed")] + Failed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingFailedEventType { + #[serde(rename = "episode.processing.failed")] + EpisodeProcessingFailed, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeProcessingProgressEvent { + #[serde(rename = "current_step")] + pub current_step: NonEmptyString, + #[serde(rename = "percent")] + pub percent: i64, + #[serde(rename = "run_id")] + pub run_id: ProcessingRunID, + #[serde(rename = "status")] + pub status: EpisodeProcessingStatus, + #[serde(rename = "type")] + pub r#type: EpisodeProcessingProgressEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingProgressEventType { + #[serde(rename = "episode.processing.progress")] + EpisodeProcessingProgress, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeProcessingSafeHandoffEvent { + #[serde(rename = "episode_id")] + pub episode_id: EpisodeID, + #[serde(rename = "media_version_id")] + pub media_version_id: MediaVersionID, + #[serde(rename = "run_id")] + pub run_id: ProcessingRunID, + #[serde(rename = "status")] + pub status: EpisodeProcessingSafeHandoffEventStatus, + #[serde(rename = "type")] + pub r#type: EpisodeProcessingSafeHandoffEventType, + #[serde(rename = "youtube_video_id", default, skip_serializing_if = "Option::is_none")] + pub youtube_video_id: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingSafeHandoffEventStatus { + #[serde(rename = "ready")] + Ready, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingSafeHandoffEventType { + #[serde(rename = "episode.processing.safe_handoff")] + EpisodeProcessingSafeHandoff, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeProcessingSnapshot { + #[serde(rename = "current_step")] + pub current_step: NonEmptyString, + #[serde(rename = "error_code", default, skip_serializing_if = "Option::is_none")] + pub error_code: std::option::Option, + #[serde(rename = "error_message", default, skip_serializing_if = "Option::is_none")] + pub error_message: std::option::Option, + #[serde(rename = "percent")] + pub percent: i64, + #[serde(rename = "run_id")] + pub run_id: ProcessingRunID, + #[serde(rename = "status")] + pub status: EpisodeProcessingStatus, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeProcessingStatus { + #[serde(rename = "queued")] + Queued, + #[serde(rename = "processing")] + Processing, + #[serde(rename = "completed")] + Completed, + #[serde(rename = "failed")] + Failed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodePublication { + #[serde(rename = "draft")] + Draft, + #[serde(rename = "publish")] + Publish, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeStatus { + #[serde(rename = "draft")] + Draft, + #[serde(rename = "published")] + Published, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeUploadContentType { + #[serde(rename = "audio/mpeg")] + AudioMpeg, + #[serde(rename = "audio/mp4")] + AudioMp4, + #[serde(rename = "audio/wav")] + AudioWav, + #[serde(rename = "audio/flac")] + AudioFlac, + #[serde(rename = "video/mp4")] + VideoMp4, + #[serde(rename = "video/quicktime")] + VideoQuicktime, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpisodeUploadSession { + #[serde(rename = "byte_length")] + pub byte_length: i64, + #[serde(rename = "completed_parts")] + pub completed_parts: std::vec::Vec, + #[serde(rename = "content_type")] + pub content_type: EpisodeUploadContentType, + #[serde(rename = "description", default, skip_serializing_if = "Option::is_none")] + pub description: std::option::Option, + #[serde(rename = "episode_id", default, skip_serializing_if = "Option::is_none")] + pub episode_id: std::option::Option, + #[serde(rename = "expires_at")] + pub expires_at: UnixMillis, + #[serde(rename = "file_name")] + pub file_name: NonEmptyString, + #[serde(rename = "max_parallelism")] + pub max_parallelism: i64, + #[serde(rename = "object_key")] + pub object_key: R2ObjectKey, + #[serde(rename = "part_size")] + pub part_size: i64, + #[serde(rename = "phase")] + pub phase: EpisodeUploadSessionPhase, + #[serde(rename = "processing", default, skip_serializing_if = "Option::is_none")] + pub processing: std::option::Option, + #[serde(rename = "show_id")] + pub show_id: ShowID, + #[serde(rename = "source_duration_seconds", default, skip_serializing_if = "Option::is_none")] + pub source_duration_seconds: std::option::Option, + #[serde(rename = "source_sha256")] + pub source_sha256: SHA256Hex, + #[serde(rename = "team_id")] + pub team_id: TeamID, + #[serde(rename = "thumbnail_url", default, skip_serializing_if = "Option::is_none")] + pub thumbnail_url: std::option::Option, + #[serde(rename = "title")] + pub title: NonEmptyString, + #[serde(rename = "upload_session_id")] + pub upload_session_id: UploadSessionID, +} + +#[derive(Clone, Debug)] +pub struct EpisodeUploadSessionEventsParams { + pub upload_session_id: UploadSessionID, +} + +#[derive(Debug)] +pub enum EpisodeUploadSessionEventsResponse { + Status200(reqwest::Response), + Status400(ValidationErr), + Status401(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl EpisodeUploadSessionEventsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum EpisodeUploadSessionPhase { + #[serde(rename = "created")] + Created, + #[serde(rename = "uploading")] + Uploading, + #[serde(rename = "processing")] + Processing, + #[serde(rename = "completed")] + Completed, + #[serde(rename = "failed")] + Failed, + #[serde(rename = "expired")] + Expired, +} + +pub type FeedID = std::string::String; + +#[derive(Clone, Debug)] +pub struct GetEpisodeUploadSessionParams { + pub upload_session_id: UploadSessionID, +} + +#[derive(Debug)] +pub enum GetEpisodeUploadSessionResponse { + Status200(EpisodeUploadSession), + Status400(ValidationErr), + Status401(()), + Status404(()), + Status410(()), + Unexpected(reqwest::Response), +} +impl GetEpisodeUploadSessionResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 404 => Self::Status404(()), + 410 => Self::Status410(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Debug)] +pub enum HeadOpenapiResponse { + Status200(()), + Status304(()), + Status400(()), + Unexpected(reqwest::Response), +} +impl HeadOpenapiResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(()), + 304 => Self::Status304(()), + 400 => Self::Status400(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ImageAsset { + #[serde(rename = "byte_length")] + pub byte_length: i64, + #[serde(rename = "content_type")] + pub content_type: ImageAssetContentType, + #[serde(rename = "has_transparency")] + pub has_transparency: bool, + #[serde(rename = "height_pixels")] + pub height_pixels: i64, + #[serde(rename = "id")] + pub id: ImageAssetID, + #[serde(rename = "public_url")] + pub public_url: std::string::String, + #[serde(rename = "width_pixels")] + pub width_pixels: i64, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ImageAssetContentType { + #[serde(rename = "image/jpeg")] + ImageJpeg, + #[serde(rename = "image/png")] + ImagePng, +} + +pub type ImageAssetID = std::string::String; + +pub type ImageUploadObjectKey = std::string::String; + +#[derive(Clone, Debug)] +pub struct ImportRSSParams { + pub body: ImportRSSRequest, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ImportRSSRequest { + #[serde(rename = "slug", default, skip_serializing_if = "Option::is_none")] + pub slug: std::option::Option, + #[serde(rename = "source_url")] + pub source_url: std::string::String, +} + +#[derive(Debug)] +pub enum ImportRSSResponse { + Status202(CreatedPublicRSSImport), + Status400(ValidationErr), + Status401(()), + Status402(RSSImportEntitlementError), + Status403(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl ImportRSSResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 202 => Self::Status202(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 402 => Self::Status402(response.json().await?), + 403 => Self::Status403(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct ImportRSSRunEventsParams { + pub import_run_id: ImportRunID, +} + +#[derive(Debug)] +pub enum ImportRSSRunEventsResponse { + Status200(reqwest::Response), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl ImportRSSRunEventsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +pub type ImportRunID = std::string::String; + +#[derive(Clone, Debug)] +pub struct ListEpisodesParams { + pub show_slug: ShowSlug, + pub limit: std::option::Option, + pub cursor: std::option::Option, +} + +#[derive(Debug)] +pub enum ListEpisodesResponse { + Status200(EpisodePage), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl ListEpisodesResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct ListShowMembersParams { + pub show_slug: ShowSlug, +} + +#[derive(Debug)] +pub enum ListShowMembersResponse { + Status200(ShowMemberCollection), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl ListShowMembersResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Debug)] +pub enum ListShowsResponse { + Status200(std::vec::Vec), + Status400(ValidationErr), + Status401(()), + Unexpected(reqwest::Response), +} +impl ListShowsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Debug)] +pub enum ListTeamMembersResponse { + Status200(TeamMemberCollection), + Status400(ValidationErr), + Status401(()), + Status403(()), + Unexpected(reqwest::Response), +} +impl ListTeamMembersResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + _ => Self::Unexpected(response), + }) + } +} + +pub type MediaUploadPartNumber = i64; + +pub type MediaVersionID = std::string::String; + +pub type NonEmptyString = std::string::String; + +pub type OpenAPIJSONDocument = serde_json::Value; + +pub type OpenAPIYAMLDocument = std::string::String; + +#[derive(Debug)] +pub enum OpenapiResponse { + Status200(OpenAPIJSONDocument), + Status304(()), + Status400(ValidationErr), + Unexpected(reqwest::Response), +} +impl OpenapiResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 304 => Self::Status304(()), + 400 => Self::Status400(response.json().await?), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PatchEpisodeUploadSession { + #[serde(rename = "description", default, skip_serializing_if = "Option::is_none")] + pub description: std::option::Option, + #[serde(rename = "thumbnail_url", default, skip_serializing_if = "Option::is_none")] + pub thumbnail_url: std::option::Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PendingShowInvitation { + #[serde(rename = "email")] + pub email: std::string::String, + #[serde(rename = "expires_at")] + pub expires_at: UnixMillis, + #[serde(rename = "id")] + pub id: ShowInvitationID, + #[serde(rename = "invited_at")] + pub invited_at: UnixMillis, + #[serde(rename = "role")] + pub role: AssignableTeamRole, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PendingTeamInvitation { + #[serde(rename = "email")] + pub email: std::string::String, + #[serde(rename = "expires_at")] + pub expires_at: UnixMillis, + #[serde(rename = "id")] + pub id: TeamInvitationID, + #[serde(rename = "invited_at")] + pub invited_at: UnixMillis, + #[serde(rename = "role")] + pub role: AssignableTeamRole, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PreparedMediaObject { + #[serde(rename = "byte_length")] + pub byte_length: i64, + #[serde(rename = "content_type")] + pub content_type: PreparedMediaObjectContentType, + #[serde(rename = "name")] + pub name: std::string::String, + #[serde(rename = "sha256")] + pub sha256: SHA256Hex, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PreparedMediaObjectContentType { + #[serde(rename = "video/mp4")] + VideoMp4, + #[serde(rename = "audio/mp4")] + AudioMp4, + #[serde(rename = "application/vnd.apple.mpegurl")] + ApplicationVndAppleMpegurl, +} + +#[derive(Clone, Debug)] +pub struct PresignEpisodePackagePartsParams { + pub upload_session_id: UploadSessionID, + pub object_index: i64, + pub body: PresignEpisodeUploadSessionParts, +} + +#[derive(Debug)] +pub enum PresignEpisodePackagePartsResponse { + Status200(PresignedEpisodeUploadParts), + Status400(ValidationErr), + Status401(()), + Status409(()), + Unexpected(reqwest::Response), +} +impl PresignEpisodePackagePartsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 409 => Self::Status409(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PresignEpisodeUploadSessionParts { + #[serde(rename = "part_numbers")] + pub part_numbers: std::vec::Vec, +} + +#[derive(Clone, Debug)] +pub struct PresignEpisodeUploadSessionPartsParams { + pub upload_session_id: UploadSessionID, + pub body: PresignEpisodeUploadSessionParts, +} + +#[derive(Debug)] +pub enum PresignEpisodeUploadSessionPartsResponse { + Status201(PresignedEpisodeUploadParts), + Status400(ValidationErr), + Status401(()), + Status402(()), + Status404(()), + Status409(()), + Status410(()), + Unexpected(reqwest::Response), +} +impl PresignEpisodeUploadSessionPartsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 201 => Self::Status201(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 402 => Self::Status402(()), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + 410 => Self::Status410(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PresignedEpisodeUploadPart { + #[serde(rename = "expires_at")] + pub expires_at: UnixMillis, + #[serde(rename = "method")] + pub method: PresignedEpisodeUploadPartMethod, + #[serde(rename = "part_number")] + pub part_number: MediaUploadPartNumber, + #[serde(rename = "upload_url")] + pub upload_url: std::string::String, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PresignedEpisodeUploadPartMethod { + #[serde(rename = "PUT")] + Put, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PresignedEpisodeUploadParts { + #[serde(rename = "parts")] + pub parts: std::vec::Vec, + #[serde(rename = "upload_session_id")] + pub upload_session_id: UploadSessionID, +} + +pub type ProcessingRunID = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PublicRSSImportCompletedTerminalEvent { + #[serde(rename = "feed_id")] + pub feed_id: FeedID, + #[serde(rename = "show_id")] + pub show_id: ShowID, + #[serde(rename = "show_slug")] + pub show_slug: ShowSlug, + #[serde(rename = "status")] + pub status: PublicRSSImportCompletedTerminalEventStatus, + #[serde(rename = "team_id")] + pub team_id: TeamID, + #[serde(rename = "type")] + pub r#type: PublicRSSImportCompletedTerminalEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PublicRSSImportCompletedTerminalEventStatus { + #[serde(rename = "completed")] + Completed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PublicRSSImportCompletedTerminalEventType { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum PublicRSSImportEvent { + Variant0(PublicRSSImportEventVariant0), + Variant1(PublicRSSImportEventVariant1), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PublicRSSImportEventVariant0 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: PublicRSSImportEventVariant0Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PublicRSSImportEventVariant0Event { + #[serde(rename = "progress")] + Progress, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PublicRSSImportEventVariant1 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: PublicRSSImportEventVariant1Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PublicRSSImportEventVariant1Event { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum PublicRSSImportTerminalEvent { + Variant0(PublicRSSImportCompletedTerminalEvent), + Variant1(RSSImportFailedTerminalEvent), + Variant2(RSSImportCancelledTerminalEvent), +} + +pub type R2ObjectKey = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RSSImportCancelledTerminalEvent { + #[serde(rename = "status")] + pub status: RSSImportCancelledTerminalEventStatus, + #[serde(rename = "type")] + pub r#type: RSSImportCancelledTerminalEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RSSImportCancelledTerminalEventStatus { + #[serde(rename = "cancelled")] + Cancelled, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RSSImportCancelledTerminalEventType { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RSSImportEntitlementError { + #[serde(rename = "code")] + pub code: RSSImportEntitlementErrorCode, + #[serde(rename = "current_entitlement")] + pub current_entitlement: VideoDeliveryEntitlement, + #[serde(rename = "media_kind")] + pub media_kind: RSSImportMediaKind, + #[serde(rename = "pricing_url")] + pub pricing_url: std::string::String, + #[serde(rename = "required_entitlement")] + pub required_entitlement: VideoDeliveryEntitlement, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RSSImportEntitlementErrorCode { + #[serde(rename = "rss_import_entitlement_required")] + RssImportEntitlementRequired, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RSSImportFailedTerminalEvent { + #[serde(rename = "error_code")] + pub error_code: NonEmptyString, + #[serde(rename = "error_message")] + pub error_message: NonEmptyString, + #[serde(rename = "http_status", default, skip_serializing_if = "Option::is_none")] + pub http_status: std::option::Option, + #[serde(rename = "limit_seconds", default, skip_serializing_if = "Option::is_none")] + pub limit_seconds: std::option::Option, + #[serde(rename = "pricing_url", default, skip_serializing_if = "Option::is_none")] + pub pricing_url: std::option::Option, + #[serde(rename = "remaining_seconds", default, skip_serializing_if = "Option::is_none")] + pub remaining_seconds: std::option::Option, + #[serde(rename = "requested_seconds", default, skip_serializing_if = "Option::is_none")] + pub requested_seconds: std::option::Option, + #[serde(rename = "required_next_plan", default, skip_serializing_if = "Option::is_none")] + pub required_next_plan: std::option::Option, + #[serde(rename = "reserved_seconds", default, skip_serializing_if = "Option::is_none")] + pub reserved_seconds: std::option::Option, + #[serde(rename = "retained_seconds", default, skip_serializing_if = "Option::is_none")] + pub retained_seconds: std::option::Option, + #[serde(rename = "sales_contact_required", default, skip_serializing_if = "Option::is_none")] + pub sales_contact_required: std::option::Option, + #[serde(rename = "status")] + pub status: RSSImportFailedTerminalEventStatus, + #[serde(rename = "type")] + pub r#type: RSSImportFailedTerminalEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RSSImportFailedTerminalEventStatus { + #[serde(rename = "failed")] + Failed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RSSImportFailedTerminalEventType { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RSSImportMediaKind { + #[serde(rename = "audio")] + Audio, + #[serde(rename = "video")] + Video, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RSSImportProgressEvent { + #[serde(rename = "current_step")] + pub current_step: NonEmptyString, + #[serde(rename = "percent")] + pub percent: i64, + #[serde(rename = "type")] + pub r#type: RSSImportProgressEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum RSSImportProgressEventType { + #[serde(rename = "progress")] + Progress, +} + +#[derive(Clone, Debug)] +pub struct RemoveShowMemberParams { + pub show_slug: ShowSlug, + pub user_id: UserID, +} + +#[derive(Debug)] +pub enum RemoveShowMemberResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl RemoveShowMemberResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct RemoveTeamMemberParams { + pub user_id: UserID, +} + +#[derive(Debug)] +pub enum RemoveTeamMemberResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl RemoveTeamMemberResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct RevokeShowInvitationParams { + pub show_slug: ShowSlug, + pub invitation_id: ShowInvitationID, +} + +#[derive(Debug)] +pub enum RevokeShowInvitationResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl RevokeShowInvitationResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct RevokeTeamInvitationParams { + pub invitation_id: TeamInvitationID, +} + +#[derive(Debug)] +pub enum RevokeTeamInvitationResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl RevokeTeamInvitationResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +pub type SHA256Hex = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Show { + #[serde(rename = "id")] + pub id: ShowID, + #[serde(rename = "language")] + pub language: ShowLanguage, + #[serde(rename = "slug")] + pub slug: ShowSlug, + #[serde(rename = "source_kind")] + pub source_kind: ShowSourceKind, + #[serde(rename = "team_id")] + pub team_id: TeamID, + #[serde(rename = "title")] + pub title: NonEmptyString, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowAccessSource { + #[serde(rename = "team")] + Team, + #[serde(rename = "show")] + Show, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ShowDeletionCompletedEvent { + #[serde(rename = "show_deletion_run_id")] + pub show_deletion_run_id: ShowDeletionRunID, + #[serde(rename = "show_slug")] + pub show_slug: ShowSlug, + #[serde(rename = "status")] + pub status: ShowDeletionCompletedEventStatus, + #[serde(rename = "type")] + pub r#type: ShowDeletionCompletedEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionCompletedEventStatus { + #[serde(rename = "completed")] + Completed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionCompletedEventType { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ShowDeletionEvent { + Variant0(ShowDeletionEventVariant0), + Variant1(ShowDeletionEventVariant1), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ShowDeletionEventVariant0 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: ShowDeletionEventVariant0Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionEventVariant0Event { + #[serde(rename = "progress")] + Progress, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ShowDeletionEventVariant1 { + #[serde(rename = "data")] + pub data: std::string::String, + #[serde(rename = "event")] + pub event: ShowDeletionEventVariant1Event, + #[serde(rename = "id", default, skip_serializing_if = "Option::is_none")] + pub id: std::option::Option, + #[serde(rename = "retry", default, skip_serializing_if = "Option::is_none")] + pub retry: std::option::Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionEventVariant1Event { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug)] +pub struct ShowDeletionEventsParams { + pub show_deletion_run_id: ShowDeletionRunID, +} + +#[derive(Debug)] +pub enum ShowDeletionEventsResponse { + Status200(reqwest::Response), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl ShowDeletionEventsResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ShowDeletionFailedEvent { + #[serde(rename = "error_code")] + pub error_code: NonEmptyString, + #[serde(rename = "error_message")] + pub error_message: NonEmptyString, + #[serde(rename = "show_deletion_run_id")] + pub show_deletion_run_id: ShowDeletionRunID, + #[serde(rename = "status")] + pub status: ShowDeletionFailedEventStatus, + #[serde(rename = "type")] + pub r#type: ShowDeletionFailedEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionFailedEventStatus { + #[serde(rename = "failed")] + Failed, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionFailedEventType { + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ShowDeletionProgressEvent { + #[serde(rename = "current_step")] + pub current_step: NonEmptyString, + #[serde(rename = "percent")] + pub percent: i64, + #[serde(rename = "show_deletion_run_id")] + pub show_deletion_run_id: ShowDeletionRunID, + #[serde(rename = "status")] + pub status: ShowDeletionProgressEventStatus, + #[serde(rename = "type")] + pub r#type: ShowDeletionProgressEventType, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionProgressEventStatus { + #[serde(rename = "queued")] + Queued, + #[serde(rename = "processing")] + Processing, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowDeletionProgressEventType { + #[serde(rename = "progress")] + Progress, +} + +pub type ShowDeletionRunID = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ShowDeletionTerminalEvent { + Variant0(ShowDeletionCompletedEvent), + Variant1(ShowDeletionFailedEvent), +} + +pub type ShowID = std::string::String; + +pub type ShowInvitationID = std::string::String; + +pub type ShowLanguage = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ShowMember { + #[serde(rename = "access_source")] + pub access_source: ShowAccessSource, + #[serde(rename = "joined_at")] + pub joined_at: UnixMillis, + #[serde(rename = "role")] + pub role: TeamRole, + #[serde(rename = "user")] + pub user: User, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ShowMemberCollection { + #[serde(rename = "invitations")] + pub invitations: std::vec::Vec, + #[serde(rename = "members")] + pub members: std::vec::Vec, +} + +pub type ShowSlug = std::string::String; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ShowSourceKind { + #[serde(rename = "audio")] + Audio, + #[serde(rename = "video")] + Video, +} + +pub type String = std::string::String; + +pub type TeamID = std::string::String; + +pub type TeamInvitationID = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TeamMember { + #[serde(rename = "joined_at")] + pub joined_at: UnixMillis, + #[serde(rename = "role")] + pub role: TeamRole, + #[serde(rename = "user")] + pub user: User, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TeamMemberCollection { + #[serde(rename = "invitations")] + pub invitations: std::vec::Vec, + #[serde(rename = "members")] + pub members: std::vec::Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum TeamRole { + #[serde(rename = "owner")] + Owner, + #[serde(rename = "write")] + Write, + #[serde(rename = "read")] + Read, +} + +pub type UnixMillis = i64; + +#[derive(Clone, Debug)] +pub struct UpdateEpisodeUploadSessionParams { + pub upload_session_id: UploadSessionID, + pub body: PatchEpisodeUploadSession, +} + +#[derive(Debug)] +pub enum UpdateEpisodeUploadSessionResponse { + Status200(EpisodeUploadSession), + Status400(ValidationErr), + Status401(()), + Status404(()), + Status409(()), + Status410(()), + Unexpected(reqwest::Response), +} +impl UpdateEpisodeUploadSessionResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 404 => Self::Status404(()), + 409 => Self::Status409(()), + 410 => Self::Status410(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct UpdateShowInvitationRoleParams { + pub show_slug: ShowSlug, + pub invitation_id: ShowInvitationID, + pub body: UpdateShowMemberRole, +} + +#[derive(Debug)] +pub enum UpdateShowInvitationRoleResponse { + Status200(PendingShowInvitation), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl UpdateShowInvitationRoleResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct UpdateShowMemberRole { + #[serde(rename = "role")] + pub role: AssignableTeamRole, +} + +#[derive(Clone, Debug)] +pub struct UpdateShowMemberRoleParams { + pub show_slug: ShowSlug, + pub user_id: UserID, + pub body: UpdateShowMemberRole, +} + +#[derive(Debug)] +pub enum UpdateShowMemberRoleResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl UpdateShowMemberRoleResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug)] +pub struct UpdateTeamInvitationRoleParams { + pub invitation_id: TeamInvitationID, + pub body: UpdateTeamMemberRole, +} + +#[derive(Debug)] +pub enum UpdateTeamInvitationRoleResponse { + Status200(PendingTeamInvitation), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl UpdateTeamInvitationRoleResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct UpdateTeamMemberRole { + #[serde(rename = "role")] + pub role: AssignableTeamRole, +} + +#[derive(Clone, Debug)] +pub struct UpdateTeamMemberRoleParams { + pub user_id: UserID, + pub body: UpdateTeamMemberRole, +} + +#[derive(Debug)] +pub enum UpdateTeamMemberRoleResponse { + Status204(()), + Status400(ValidationErr), + Status401(()), + Status403(()), + Status404(()), + Unexpected(reqwest::Response), +} +impl UpdateTeamMemberRoleResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 204 => Self::Status204(()), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + 403 => Self::Status403(()), + 404 => Self::Status404(()), + _ => Self::Unexpected(response), + }) + } +} + +pub type UploadSessionID = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct User { + #[serde(rename = "email")] + pub email: std::string::String, + #[serde(rename = "id")] + pub id: UserID, + #[serde(rename = "name", default, skip_serializing_if = "Option::is_none")] + pub name: std::option::Option, + #[serde(rename = "picture_url", default, skip_serializing_if = "Option::is_none")] + pub picture_url: std::option::Option, +} + +pub type UserID = std::string::String; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ValidationErr { + #[serde(rename = "errors")] + pub errors: std::vec::Vec, + #[serde(rename = "message")] + pub message: NonEmptyString, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ValidationIssue { + #[serde(rename = "code")] + pub code: ValidationIssueCode, + #[serde(rename = "field", default, skip_serializing_if = "Option::is_none")] + pub field: std::option::Option, + #[serde(rename = "in")] + pub r#in: ValidationLocation, + #[serde(rename = "message")] + pub message: NonEmptyString, + #[serde(rename = "path", default, skip_serializing_if = "Option::is_none")] + pub path: std::option::Option>, +} + +pub type ValidationIssueCode = std::string::String; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum ValidationLocation { + #[serde(rename = "body")] + Body, + #[serde(rename = "query")] + Query, + #[serde(rename = "path")] + Path, + #[serde(rename = "header")] + Header, + #[serde(rename = "cookie")] + Cookie, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct VideoAdmissionError { + #[serde(rename = "code")] + pub code: VideoAdmissionErrorCode, + #[serde(rename = "current_plan")] + pub current_plan: VideoPlan, + #[serde(rename = "delivery_entitlement")] + pub delivery_entitlement: VideoDeliveryEntitlement, + #[serde(rename = "limit_seconds")] + pub limit_seconds: i64, + #[serde(rename = "pricing_url")] + pub pricing_url: std::string::String, + #[serde(rename = "remaining_seconds")] + pub remaining_seconds: i64, + #[serde(rename = "requested_seconds")] + pub requested_seconds: i64, + #[serde(rename = "required_next_plan")] + pub required_next_plan: VideoPlan, + #[serde(rename = "reserved_seconds")] + pub reserved_seconds: i64, + #[serde(rename = "retained_seconds")] + pub retained_seconds: i64, + #[serde(rename = "sales_contact_required")] + pub sales_contact_required: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum VideoAdmissionErrorCode { + #[serde(rename = "video_plan_required")] + VideoPlanRequired, + #[serde(rename = "video_hours_exhausted")] + VideoHoursExhausted, + #[serde(rename = "video_hours_exceeded")] + VideoHoursExceeded, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum VideoDeliveryEntitlement { + #[serde(rename = "free")] + Free, + #[serde(rename = "audio")] + Audio, + #[serde(rename = "video_hd")] + VideoHd, + #[serde(rename = "video_4k")] + Video4k, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum VideoPlan { + #[serde(rename = "starter")] + Starter, + #[serde(rename = "professional")] + Professional, + #[serde(rename = "studio")] + Studio, +} + +#[derive(Debug)] +pub enum WhoamiResponse { + Status200(APIKeyWhoami), + Status400(ValidationErr), + Status401(()), + Unexpected(reqwest::Response), +} +impl WhoamiResponse { + pub async fn decode(response: reqwest::Response) -> Result { + Ok(match response.status().as_u16() { + 200 => Self::Status200(response.json().await?), + 400 => Self::Status400(response.json().await?), + 401 => Self::Status401(()), + _ => Self::Unexpected(response), + }) + } +} + + +#[derive(Clone, Debug)] +pub struct Client { + http: reqwest::Client, + base_url: String, + bearer_token: Option, +} +fn encode_path(value: &str) -> String { + value.bytes().map(|byte| { + if byte.is_ascii_alphanumeric() || b"-._~".contains(&byte) { + char::from(byte).to_string() + } else { format!("%{byte:02X}") } + }).collect() +} +impl Client { + pub fn new(http: reqwest::Client, base_url: String, bearer_token: Option) -> Self { + Self { http, base_url: base_url.trim_end_matches('/').to_owned(), bearer_token } + } + pub fn openapi(&self) -> reqwest::RequestBuilder { + let path = "/".to_owned(); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn head_openapi(&self) -> reqwest::RequestBuilder { + let path = "/".to_owned(); + let request = self.http.request(reqwest::Method::HEAD, format!("{}{}", self.base_url, path)).header("Accept", "*/*"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn create_cli_authorization(&self, params: CreateCLIAuthorizationParams) -> reqwest::RequestBuilder { + let path = "/cli/authorizations".to_owned(); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn cli_authorization_events(&self, params: CliAuthorizationEventsParams) -> reqwest::RequestBuilder { + let path = "/cli/authorizations/{code}/events".to_owned(); + let path = path.replace("{code}", &encode_path(¶ms.code.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "text/event-stream"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn episode_deletion_events(&self, params: EpisodeDeletionEventsParams) -> reqwest::RequestBuilder { + let path = "/s/episode-deletions/{episode_deletion_run_id}/events".to_owned(); + let path = path.replace("{episode_deletion_run_id}", &encode_path(¶ms.episode_deletion_run_id.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "text/event-stream"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn create_episode_package(&self, params: CreateEpisodePackageParams) -> reqwest::RequestBuilder { + let path = "/s/episode-packages".to_owned(); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn cancel_episode_package(&self, params: CancelEpisodePackageParams) -> reqwest::RequestBuilder { + let path = "/s/episode-packages/{upload_session_id}".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::DELETE, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn complete_episode_package(&self, params: CompleteEpisodePackageParams) -> reqwest::RequestBuilder { + let path = "/s/episode-packages/{upload_session_id}/complete".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn presign_episode_package_parts(&self, params: PresignEpisodePackagePartsParams) -> reqwest::RequestBuilder { + let path = "/s/episode-packages/{upload_session_id}/objects/{object_index}/parts".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let path = path.replace("{object_index}", &encode_path(¶ms.object_index.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn create_episode_upload_session(&self, params: CreateEpisodeUploadSessionParams) -> reqwest::RequestBuilder { + let path = "/s/episode-upload-sessions".to_owned(); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn get_episode_upload_session(&self, params: GetEpisodeUploadSessionParams) -> reqwest::RequestBuilder { + let path = "/s/episode-upload-sessions/{upload_session_id}".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn update_episode_upload_session(&self, params: UpdateEpisodeUploadSessionParams) -> reqwest::RequestBuilder { + let path = "/s/episode-upload-sessions/{upload_session_id}".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::PUT, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn cancel_episode_upload_session(&self, params: CancelEpisodeUploadSessionParams) -> reqwest::RequestBuilder { + let path = "/s/episode-upload-sessions/{upload_session_id}".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::DELETE, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn complete_episode_upload_session(&self, params: CompleteEpisodeUploadSessionParams) -> reqwest::RequestBuilder { + let path = "/s/episode-upload-sessions/{upload_session_id}/complete".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn episode_upload_session_events(&self, params: EpisodeUploadSessionEventsParams) -> reqwest::RequestBuilder { + let path = "/s/episode-upload-sessions/{upload_session_id}/events".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "text/event-stream"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn presign_episode_upload_session_parts(&self, params: PresignEpisodeUploadSessionPartsParams) -> reqwest::RequestBuilder { + let path = "/s/episode-upload-sessions/{upload_session_id}/parts/presign".to_owned(); + let path = path.replace("{upload_session_id}", &encode_path(¶ms.upload_session_id.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn create_episode_deletion(&self, params: CreateEpisodeDeletionParams) -> reqwest::RequestBuilder { + let path = "/s/episodes/{episode_id}/deletions".to_owned(); + let path = path.replace("{episode_id}", &encode_path(¶ms.episode_id.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn create_image_upload_presign(&self, params: CreateImageUploadPresignParams) -> reqwest::RequestBuilder { + let path = "/s/image-uploads/presign".to_owned(); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn complete_image_upload(&self, params: CompleteImageUploadParams) -> reqwest::RequestBuilder { + let path = "/s/image-uploads/{image_asset_id}/complete".to_owned(); + let path = path.replace("{image_asset_id}", &encode_path(¶ms.image_asset_id.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn import_rss(&self, params: ImportRSSParams) -> reqwest::RequestBuilder { + let path = "/s/rss-imports".to_owned(); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn import_rss_run_events(&self, params: ImportRSSRunEventsParams) -> reqwest::RequestBuilder { + let path = "/s/rss-imports/{import_run_id}/events".to_owned(); + let path = path.replace("{import_run_id}", &encode_path(¶ms.import_run_id.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "text/event-stream"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn show_deletion_events(&self, params: ShowDeletionEventsParams) -> reqwest::RequestBuilder { + let path = "/s/show-deletions/{show_deletion_run_id}/events".to_owned(); + let path = path.replace("{show_deletion_run_id}", &encode_path(¶ms.show_deletion_run_id.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "text/event-stream"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn list_shows(&self) -> reqwest::RequestBuilder { + let path = "/s/shows".to_owned(); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn create_show(&self, params: CreateShowParams) -> reqwest::RequestBuilder { + let path = "/s/shows".to_owned(); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn create_show_deletion(&self, params: CreateShowDeletionParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/deletions".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn list_episodes(&self, params: ListEpisodesParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/episodes".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + let request = match params.limit { Some(value) => request.query(&[("limit", value)]), None => request }; + match params.cursor { Some(value) => request.query(&[("cursor", value)]), None => request } + } + pub fn create_show_invitation(&self, params: CreateShowInvitationParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/invitations".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn update_show_invitation_role(&self, params: UpdateShowInvitationRoleParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/invitations/{invitation_id}".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let path = path.replace("{invitation_id}", &encode_path(¶ms.invitation_id.to_string())); + let request = self.http.request(reqwest::Method::PUT, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn revoke_show_invitation(&self, params: RevokeShowInvitationParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/invitations/{invitation_id}".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let path = path.replace("{invitation_id}", &encode_path(¶ms.invitation_id.to_string())); + let request = self.http.request(reqwest::Method::DELETE, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn list_show_members(&self, params: ListShowMembersParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/members".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn update_show_member_role(&self, params: UpdateShowMemberRoleParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/members/{user_id}".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let path = path.replace("{user_id}", &encode_path(¶ms.user_id.to_string())); + let request = self.http.request(reqwest::Method::PUT, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn remove_show_member(&self, params: RemoveShowMemberParams) -> reqwest::RequestBuilder { + let path = "/s/shows/{show_slug}/members/{user_id}".to_owned(); + let path = path.replace("{show_slug}", &encode_path(¶ms.show_slug.to_string())); + let path = path.replace("{user_id}", &encode_path(¶ms.user_id.to_string())); + let request = self.http.request(reqwest::Method::DELETE, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn create_team_invitation(&self, params: CreateTeamInvitationParams) -> reqwest::RequestBuilder { + let path = "/s/team/invitations".to_owned(); + let request = self.http.request(reqwest::Method::POST, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn update_team_invitation_role(&self, params: UpdateTeamInvitationRoleParams) -> reqwest::RequestBuilder { + let path = "/s/team/invitations/{invitation_id}".to_owned(); + let path = path.replace("{invitation_id}", &encode_path(¶ms.invitation_id.to_string())); + let request = self.http.request(reqwest::Method::PUT, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn revoke_team_invitation(&self, params: RevokeTeamInvitationParams) -> reqwest::RequestBuilder { + let path = "/s/team/invitations/{invitation_id}".to_owned(); + let path = path.replace("{invitation_id}", &encode_path(¶ms.invitation_id.to_string())); + let request = self.http.request(reqwest::Method::DELETE, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn list_team_members(&self) -> reqwest::RequestBuilder { + let path = "/s/team/members".to_owned(); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn update_team_member_role(&self, params: UpdateTeamMemberRoleParams) -> reqwest::RequestBuilder { + let path = "/s/team/members/{user_id}".to_owned(); + let path = path.replace("{user_id}", &encode_path(¶ms.user_id.to_string())); + let request = self.http.request(reqwest::Method::PUT, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + let request = match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request }; + request.json(¶ms.body) + } + pub fn remove_team_member(&self, params: RemoveTeamMemberParams) -> reqwest::RequestBuilder { + let path = "/s/team/members/{user_id}".to_owned(); + let path = path.replace("{user_id}", &encode_path(¶ms.user_id.to_string())); + let request = self.http.request(reqwest::Method::DELETE, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } + pub fn whoami(&self) -> reqwest::RequestBuilder { + let path = "/s/whoami".to_owned(); + let request = self.http.request(reqwest::Method::GET, format!("{}{}", self.base_url, path)).header("Accept", "application/json"); + match &self.bearer_token { Some(token) => request.bearer_auth(token), None => request } + } +} diff --git a/src/youtube.rs b/src/youtube.rs new file mode 100644 index 0000000..a393fb0 --- /dev/null +++ b/src/youtube.rs @@ -0,0 +1,342 @@ +use crate::{ + api::{Api, string}, + events::Progress, + innertube::YouTube, + publicapi as p, +}; +use anyhow::{Context, Result, ensure}; +use serde::Deserialize; +use serde_json::json; +use sha2::{Digest, Sha256}; +use std::{collections::HashSet, path::Path, time::Duration}; +use tokio::io::AsyncWriteExt; +use url::Url; + +pub fn is_source(source: &str) -> bool { + Url::parse(source).ok().is_some_and(|url| { + matches!( + url.host_str(), + Some("youtube.com" | "www.youtube.com" | "m.youtube.com" | "youtu.be") + ) + }) +} + +#[derive(Deserialize)] +struct Media { + title: String, + description: String, + video: Stream, + audio: Option, +} + +#[derive(Deserialize)] +struct Stream { + url: String, + user_agent: String, +} + +pub async fn import(api: &Api, source: &str, requested_slug: Option<&str>) -> Result<()> { + let url = Url::parse(source)?; + ensure!( + url.scheme() == "https" + && url.username().is_empty() + && url.password().is_none() + && url.port().is_none(), + "invalid YouTube source URL" + ); + let youtube = YouTube::new(api).await?; + let (title, videos) = + if let Some((_, playlist)) = url.query_pairs().find(|(key, _)| key == "list") { + ensure!(valid_youtube_id(&playlist), "invalid playlist ID"); + let page = youtube + .call(api, "playlist", json!({"id": playlist})) + .await?; + ( + string(&page, "title")?.to_owned(), + serde_json::from_value::>(page["videos"].clone())?, + ) + } else { + let id = if url.host_str() == Some("youtu.be") { + url.path().trim_matches('/').to_owned() + } else if let Some((_, id)) = url.query_pairs().find(|(key, _)| key == "v") { + id.into_owned() + } else { + url.path() + .strip_prefix("/shorts/") + .or_else(|| url.path().strip_prefix("/embed/")) + .unwrap_or("") + .into() + }; + ensure!( + id.len() == 11 && valid_youtube_id(&id), + "invalid YouTube video ID" + ); + let media = youtube.call(api, "media", json!({"id": id})).await?; + (string(&media, "title")?.into(), vec![id]) + }; + ensure!(!videos.is_empty(), "YouTube playlist has no public videos"); + let slug = requested_slug + .map(str::to_owned) + .unwrap_or_else(|| format!("youtube-{}", &hex::encode(Sha256::digest(source))[..16])); + let slug = crate::slug(&slug).map_err(anyhow::Error::msg)?; + let shows: Vec = + serde_json::from_value(api.json(api.client().list_shows(), &[200]).await?)?; + let show = match shows.into_iter().find(|show| show.slug == slug) { + Some(show) => { + ensure!( + show.source_kind == p::ShowSourceKind::Video, + "show {slug:?} is not a video show" + ); + show + } + None => { + let response = api + .json( + api.client().create_show(p::CreateShowParams { + body: p::CreateShow { + id: format!("shw_{}", &uuid::Uuid::new_v4().simple().to_string()[..16]), + title, + slug: slug.clone(), + source_kind: p::ShowSourceKind::Video, + language: "en".into(), + image_asset_id: None, + }, + }), + &[201], + ) + .await?; + serde_json::from_value(response)? + } + }; + let mut progress = Progress::default(); + let mut seen = HashSet::new(); + for (index, id) in videos.iter().enumerate() { + ensure!( + id.len() == 11 && valid_youtube_id(id), + "invalid YouTube playlist video ID" + ); + if seen.insert(id) { + import_video(api, &youtube, &slug, id) + .await + .with_context(|| format!("import YouTube video {id:?}"))?; + } + progress.update(((index + 1) * 100 / videos.len()) as i64)?; + } + progress.update(100)?; + println!("{slug}"); + eprintln!( + "Open in Listenbox: {}", + api.config.show_url(&show.team_id, &show.id)? + ); + Ok(()) +} + +fn valid_youtube_id(id: &str) -> bool { + !id.is_empty() + && id + .bytes() + .all(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'-') +} + +async fn import_video(api: &Api, youtube: &YouTube, slug: &str, id: &str) -> Result<()> { + let media: Media = + serde_json::from_value(youtube.call(api, "media", json!({"id": id})).await?)?; + let directory = tempfile::tempdir()?; + download(api, &media.video, &directory.path().join("source-video")).await?; + if let Some(audio) = &media.audio { + download(api, audio, &directory.path().join("source-audio")).await?; + } + let root = directory.path().to_owned(); + let separate_audio = media.audio.is_some(); + let cancel = api.cancel.clone(); + // The worker checks cancellation between packets; joining owns closure before temp files are removed. + let duration = + tokio::task::spawn_blocking(move || crate::media::prepare(&root, separate_audio, &cancel)) + .await??; + let objects = inventory(directory.path()).await?; + let response = api + .send( + api.client() + .create_episode_package(p::CreateEpisodePackageParams { + body: p::CreateEpisodePackage { + show_slug: slug.into(), + source_url: format!("https://www.youtube.com/watch?v={id}"), + title: media.title, + description: Some(media.description), + duration_seconds: duration, + objects: objects.clone(), + }, + }), + ) + .await?; + if response.status() != reqwest::StatusCode::CREATED { + return Err(api.response_error(response).await); + } + // Keep the trace until after reading admission, so every admitted session has a cleanup owner. + let trace = response + .headers() + .get("X-Trace-Id") + .and_then(|h| h.to_str().ok()) + .map(str::to_owned); + let session: p::EpisodePackage = api.decode(response).await?; + if session.status == p::EpisodePackageStatus::Completed { + return Ok(()); + } + let result: Result<()> = async { + if api.config.print_trace_ids { + let trace = trace.context("response missing X-Trace-Id")?; + crate::config::check_trace(&trace)?; + println!("Trace ID: {trace}"); + } + ensure!(session.part_size >= 5 << 20, "invalid package part size"); + for (ordinal, object) in objects.iter().enumerate() { + let mut offset = 0; + let mut number = 1; + while offset < object.byte_length { + let signed = api + .json( + api.client().presign_episode_package_parts( + p::PresignEpisodePackagePartsParams { + upload_session_id: session.upload_session_id.clone(), + object_index: ordinal as i64, + body: p::PresignEpisodeUploadSessionParts { + part_numbers: vec![number], + }, + }, + ), + &[200], + ) + .await?; + let parts = signed["parts"] + .as_array() + .context("missing package signed parts")?; + if parts.is_empty() { + break; + } + ensure!( + parts.len() == 1 && parts[0]["part_number"] == number, + "invalid signed package part" + ); + let length = session.part_size.min(object.byte_length - offset); + crate::episodes::upload_part( + api, + &directory.path().join(&object.name), + offset as u64, + length as u64, + "PUT", + string(&parts[0], "upload_url")?, + ) + .await?; + offset += length; + number += 1; + } + } + let completed = api + .json( + api.client() + .complete_episode_package(p::CompleteEpisodePackageParams { + upload_session_id: session.upload_session_id.clone(), + }), + &[200], + ) + .await?; + ensure!( + string(&completed, "status")? == "completed", + "prepared media did not complete" + ); + Ok(()) + } + .await; + if let Err(error) = result { + let mut cleanup = api.clone(); + cleanup.cancel = tokio_util::sync::CancellationToken::new(); + let request = cleanup + .client() + .cancel_episode_package(p::CancelEpisodePackageParams { + upload_session_id: session.upload_session_id, + }); + let cancelled = tokio::time::timeout(Duration::from_secs(10), cleanup.send(request)).await; + match cancelled { + Ok(Ok(response)) if [204, 409].contains(&response.status().as_u16()) => {} + Ok(Ok(response)) => { + return Err(error).context(format!( + "cancel pending package returned HTTP {}", + response.status() + )); + } + Ok(Err(cleanup_error)) => { + return Err(error).context(format!("cancel pending package: {cleanup_error}")); + } + Err(_) => return Err(error).context("cancel pending package timed out"), + } + return Err(error); + } + Ok(()) +} + +async fn download(api: &Api, stream: &Stream, path: &Path) -> Result<()> { + let mut response = api + .send( + api.http + .get(&stream.url) + .header("Origin", "https://youtube.com") + .header("User-Agent", &stream.user_agent), + ) + .await?; + ensure!( + response.status() == reqwest::StatusCode::OK, + "download YouTube media returned HTTP {}", + response.status() + ); + let mut file = tokio::fs::File::create(path).await?; + while let Some(chunk) = api.wait(async { Ok(response.chunk().await?) }).await? { + file.write_all(&chunk).await?; + } + file.flush().await?; + Ok(()) +} + +async fn inventory(root: &Path) -> Result> { + let mut directories = vec![root.to_owned()]; + let mut names = Vec::new(); + while let Some(directory) = directories.pop() { + for entry in std::fs::read_dir(directory)? { + let entry = entry?; + if entry.file_type()?.is_dir() { + directories.push(entry.path()); + continue; + } + ensure!( + entry.file_type()?.is_file(), + "media package contains a non-regular object" + ); + let path = entry.path(); + let name = path + .strip_prefix(root)? + .to_string_lossy() + .replace('\\', "/"); + if name == "video.mp4" || name.starts_with("hls/") { + names.push(name); + } + } + } + names.sort(); + let mut objects = Vec::new(); + for name in names { + let content_type = if name.ends_with(".m3u8") { + "application/vnd.apple.mpegurl" + } else if name.starts_with("hls/audio/") { + "audio/mp4" + } else { + "video/mp4" + }; + let (length, sha256) = crate::episodes::file_hash(&root.join(&name)).await?; + objects.push(p::PreparedMediaObject { + name, + content_type: serde_json::from_value(json!(content_type))?, + byte_length: length as i64, + sha256, + }); + } + Ok(objects) +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..81f82d1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "noEmit": true, + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "types": ["node"] + }, + "include": ["js/**/*.ts", "prepare-ffmpeg.ts"] +} From d788481725efb2f9d58fa0742afc54e91395875f Mon Sep 17 00:00:00 2001 From: adelnizamutdinov Date: Thu, 24 Sep 2026 15:27:05 +0000 Subject: [PATCH 2/6] Record Go and Rust startup, CPU, memory and binary-size measurements --- README.md | 9 + bench/README.md | 101 +++ bench/results/binaries.json | 14 + bench/results/environment.json | 36 + bench/results/linux-amd64-cpu7.csv | 1201 +++++++++++++++++++++++++++ bench/results/linux-amd64-cpu7.json | 322 +++++++ bench/results/linux-amd64.csv | 1201 +++++++++++++++++++++++++++ bench/results/linux-amd64.json | 322 +++++++ bench/startup.c | 2 +- bench/summarize.py | 41 + 10 files changed, 3248 insertions(+), 1 deletion(-) create mode 100644 bench/README.md create mode 100644 bench/results/binaries.json create mode 100644 bench/results/environment.json create mode 100644 bench/results/linux-amd64-cpu7.csv create mode 100644 bench/results/linux-amd64-cpu7.json create mode 100644 bench/results/linux-amd64.csv create mode 100644 bench/results/linux-amd64.json create mode 100644 bench/summarize.py diff --git a/README.md b/README.md index cbce146..b8fdf5d 100644 --- a/README.md +++ b/README.md @@ -72,3 +72,12 @@ regenerates it from the canonical public contract before building. origins are validated. No schema interpreter runs during startup. Integrated coverage lives in `apps/api/e2e` in that workspace and runs this Rust executable against deterministic local services, including YouTube responses. + +Production origins and disabled trace-ID output are compiled into the same binary +used in E2E tests. Tests supply an isolated YAML config with their dynamic service +origins and `print_trace_ids: true`; no test-only executable or Cargo feature is +required. + +The [startup and footprint report](bench/README.md) compares this implementation +against Go with statically linked go-astiav and kkdai/youtube, with raw wall-time, +CPU, RSS, and binary-size measurements. diff --git a/bench/README.md b/bench/README.md new file mode 100644 index 0000000..09b8943 --- /dev/null +++ b/bench/README.md @@ -0,0 +1,101 @@ +# CLI startup and footprint comparison + +Measured 2026-09-24 on Linux x86-64. These are measurements of two complete CLI +implementations, not a general claim about Go versus Rust. + +The Go baseline is [3d0f569](https://github.com/listenbox/cli/commit/3d0f56927a9fc0ae8a97d1cb64d209505ea2ae14) +from [the go-astiav PR](https://github.com/listenbox/cli/pull/7), including +**go-astiav v0.43.0 and kkdai/youtube v2.10.6**. It statically links FFmpeg; +this comparison does not use the earlier CLI with FFmpeg subprocesses. +The Rust executable is [5bd42d5](https://github.com/listenbox/cli/commit/5bd42d509f33ae08bb6ef5cd21546cf3943bdaca), +including **ffmpeg-the-third 6.0.0, YouTube.js 18.0.0 and rquickjs 0.11.0**. + +## Normal CPU affinity (all 8 vCPUs) + +| Command | Build | Wall p50 / p95 (µs) | Mean user / system CPU (µs) | Mean total CPU (µs) | CPU utilization | Peak RSS p50 / max (KiB) | +|---|---|---:|---:|---:|---:|---:| +| `--help` | Go | 6,004 / 9,277 | 3,449 / 4,589 | 8,039 | 126.8% | 13,404 / 13,604 | +| `--help` | Rust | 2,545 / 3,842 | 638 / 1,930 | 2,568 | 95.5% | 6,640 / 6,860 | +| `episodes create --help` | Go | 5,562 / 9,144 | 3,112 / 4,520 | 7,632 | 126.4% | 13,416 / 13,628 | +| `episodes create --help` | Rust | 2,435 / 3,920 | 664 / 1,835 | 2,499 | 95.6% | 6,734 / 6,944 | + +Rust's root-help median wall time is 57.6% lower, +mean total CPU time is 68.0% lower, +and median peak RSS is 50.5% lower. + +## Binary size + +| Artifact | Go + go-astiav + kkdai/youtube | Rust + ffmpeg-the-third + YouTube.js/rquickjs | +|---|---:|---:| +| Stripped executable | 22,639,472 bytes (21.59 MiB) | 20,525,240 bytes (19.57 MiB) | +| Executable compressed with gzip level 9 | 8,728,187 bytes | 8,141,703 bytes | + +The Rust executable is 9.3% smaller uncompressed. +Both include their media and extraction implementations even though help does not +initialize or execute them. Neither needs FFmpeg, FFprobe, Node, or Bun executables +at runtime. FFmpeg is static; platform C libraries are dynamic. The Go binding +also requires avdevice, avfilter, and swscale libraries at build time. Rust only +requests its used FFmpeg libraries. Codec/source selections otherwise match. + +## Controlled single-CPU run (CPU 7) + +| Command | Build | Wall p50 / p95 (µs) | Mean user / system CPU (µs) | Mean total CPU (µs) | CPU utilization | Peak RSS p50 / max (KiB) | +|---|---|---:|---:|---:|---:|---:| +| `--help` | Go | 4,622 / 5,913 | 1,953 / 2,462 | 4,415 | 93.5% | 13,176 / 13,276 | +| `--help` | Rust | 1,960 / 2,629 | 563 / 1,283 | 1,846 | 92.8% | 6,632 / 6,940 | +| `episodes create --help` | Go | 4,344 / 5,545 | 1,937 / 2,202 | 4,140 | 93.6% | 13,300 / 13,348 | +| `episodes create --help` | Rust | 1,849 / 2,520 | 480 / 1,242 | 1,722 | 91.8% | 6,758 / 6,988 | + +The same process affinity applies to each child. The normal-affinity run above +retains each runtime's ordinary CPU discovery and thread behavior. + +## Method + +- AMD EPYC-Rome KVM guest, 8 vCPUs; Linux 7.0.0-29-generic. Full environment, + compiler versions, optimization flags and dependencies: [environment.json](results/environment.json). +- Go: CGO enabled, `go build -trimpath -ldflags="-s -w"`; Rust: + `cargo build --locked --release`, opt-level 3, thin LTO, symbols stripped. + Both use checksum-pinned FFmpeg 9.0.2, compiled with GCC 15.2.0. +- [`startup.c`](startup.c) directly uses `posix_spawn`, `CLOCK_MONOTONIC`, and + `wait4`. Wall time covers spawn through process exit, including loading, + runtime initialization, argument parsing, and help generation. It includes the + common spawn/wait overhead; it is not a timestamp inside `main`. +- No HTTP requests, JSON response decoding, media processing, or terminal + rendering. Both programs run identical argument vectors; stdout/stderr and stdin + use `/dev/null`. The Go implementation also decodes its embedded YAML defaults + on the help path; Rust has typed compiled defaults and exits from Clap help. +- 30 discarded warmups then 300 samples per binary per command, alternating Go + and Rust order each iteration. Two commands and two affinity settings yield + 2,400 measured processes. Page/file caches are warm; no cache flushing. +- CPU time is per-child user plus system time from `wait4`, excluding the harness. + CPU utilization is total CPU time divided by total wall time, so a process + using multiple threads can exceed 100%. For these short processes, individual + user/system accounting samples can be zero; their means and combined CPU time + are more useful than separate medians. Values are reported in microseconds, + not interpreted as nanosecond measurement precision. +- RAM is per-process maximum resident set size (`ru_maxrss` on Linux), not heap + allocation, virtual address space, or media-import peak memory. RSS includes + resident shared pages; it is not proportional set size. +- Benchmarks ran without concurrent builds or test suites. Local service + containers remained running. This is one virtual host/run, not a hardware-wide + guarantee, a cold-disk benchmark, or evidence of media/network throughput. +- Raw samples: [normal affinity](results/linux-amd64.csv), + [CPU 7](results/linux-amd64-cpu7.csv). Corresponding JSON summaries contain + means, medians, p95, ranges, standard deviations, page faults and context + switches. [`binaries.json`](results/binaries.json) records exact commits, + SHA-256 hashes, file lengths, and deterministic gzip sizes. + +## Reproduce + +Build the two commits in separate checkouts with their Moon package tasks and +required tools installed. In the Rust checkout: + +```sh +cc -O2 -Wall -Wextra -Werror bench/startup.c -o /tmp/cli-startup +env -u GOMAXPROCS -u GOGC -u GOMEMLIMIT -u RUST_LOG \ + /tmp/cli-startup /absolute/go/dist/listenbox /absolute/rust/dist/release/listenbox > samples.csv +python3 bench/summarize.py samples.csv +# Repeat with `taskset -c 7 /tmp/cli-startup ...` for the controlled affinity run. +``` + +The summarizer uses the Python standard library; p95 uses nearest rank. diff --git a/bench/results/binaries.json b/bench/results/binaries.json new file mode 100644 index 0000000..fd93ea8 --- /dev/null +++ b/bench/results/binaries.json @@ -0,0 +1,14 @@ +{ + "go": { + "commit": "3d0f56927a9fc0ae8a97d1cb64d209505ea2ae14", + "bytes": 22639472, + "gzip_9_bytes": 8728187, + "sha256": "b14d4f4fe699d2995d21f9da7211a36fab4f1a2c7c2c98c9aa7c1101ed6a9692" + }, + "rust": { + "commit": "5bd42d509f33ae08bb6ef5cd21546cf3943bdaca", + "bytes": 20525240, + "gzip_9_bytes": 8141703, + "sha256": "ffd778f8d1dc2ae07678d11ddeedcb1515c2f4f1e93e9b40c990342d135ea26b" + } +} diff --git a/bench/results/environment.json b/bench/results/environment.json new file mode 100644 index 0000000..a67f87e --- /dev/null +++ b/bench/results/environment.json @@ -0,0 +1,36 @@ +{ + "date_utc": "2026-09-24", + "platform": "Linux-7.0.0-29-generic-x86_64-with-glibc2.43", + "cpu": "AMD EPYC-Rome, KVM guest, 8 vCPUs, 1 thread/core", + "kernel": "7.0.0-29-generic", + "go": "go1.27.1 linux/amd64", + "rust": "rustc 1.98.1 (48a229cea 2026-09-01)", + "cargo": "cargo 1.98.1 (797e8a9bc 2026-08-05)", + "c_compiler": "GCC 15.2.0", + "ffmpeg": "9.0.2, static, same source archive and codec selection", + "go_astiav": "v0.43.0", + "go_youtube": "github.com/kkdai/youtube/v2 v2.10.6", + "rust_ffmpeg": "ffmpeg-the-third 6.0.0+ffmpeg-9.0", + "rust_youtube": "youtubei.js 18.0.0 through rquickjs 0.11.0", + "go_flags": "CGO_ENABLED=1 go build -trimpath -ldflags=\"-s -w\"", + "rust_flags": "cargo build --locked --release; opt-level=3, strip=true, lto=thin", + "warmups_per_command_per_binary": 30, + "samples_per_command_per_binary": 300, + "default_affinity": "0-7", + "controlled_affinity": "7", + "unset_environment": [ + "GOMAXPROCS", + "GOGC", + "GOMEMLIMIT", + "RUST_LOG" + ], + "go_shared_libraries": [ + "libm.so.6", + "libc.so.6" + ], + "rust_shared_libraries": [ + "libgcc_s.so.1", + "libm.so.6", + "libc.so.6" + ] +} diff --git a/bench/results/linux-amd64-cpu7.csv b/bench/results/linux-amd64-cpu7.csv new file mode 100644 index 0000000..024094a --- /dev/null +++ b/bench/results/linux-amd64-cpu7.csv @@ -0,0 +1,1201 @@ +implementation,command,sample,wall_us,user_us,system_us,peak_rss_kib,minor_faults,major_faults,voluntary_context_switches,involuntary_context_switches +go,root-help,0,4182.707,1026.000,3080.000,13176,703,0,42,35 +rust,root-help,0,1754.055,853.000,853.000,6556,349,0,1,1 +rust,root-help,1,1347.562,0.000,1309.000,6516,351,0,1,1 +go,root-help,1,4022.837,909.000,2728.000,13180,692,0,38,34 +go,root-help,2,4252.489,3126.000,1042.000,13244,706,0,28,24 +rust,root-help,2,1749.566,846.000,846.000,6808,351,0,1,1 +rust,root-help,3,1336.540,0.000,1287.000,6488,349,0,1,2 +go,root-help,3,4074.614,2780.000,926.000,13128,701,0,41,34 +go,root-help,4,4515.684,775.000,3103.000,13176,701,0,39,34 +rust,root-help,4,1661.141,1610.000,0.000,6332,355,0,1,2 +rust,root-help,5,1849.915,0.000,1458.000,6852,351,0,1,2 +go,root-help,5,4020.773,1974.000,1974.000,13216,693,0,40,37 +go,root-help,6,3375.221,2214.000,1107.000,13180,690,0,22,17 +rust,root-help,6,1714.341,0.000,1664.000,6692,349,0,1,2 +rust,root-help,7,1571.062,1524.000,0.000,6756,350,0,1,1 +go,root-help,7,4240.857,2881.000,960.000,13176,692,0,42,37 +go,root-help,8,3839.864,3717.000,0.000,13216,713,0,36,32 +rust,root-help,8,2062.374,0.000,1643.000,6556,350,0,1,1 +rust,root-help,9,1852.139,0.000,1790.000,6600,351,0,1,1 +go,root-help,9,5723.162,1734.000,3469.000,13176,687,0,20,15 +go,root-help,10,4641.018,1693.000,2540.000,13220,705,0,22,20 +rust,root-help,10,1649.029,0.000,1604.000,6676,351,0,1,1 +rust,root-help,11,1964.080,1529.000,0.000,6792,352,0,1,1 +go,root-help,11,3740.097,2758.000,919.000,13164,691,0,38,37 +go,root-help,12,3736.409,918.000,2756.000,13240,692,0,34,28 +rust,root-help,12,1598.502,1557.000,0.000,6520,350,0,1,1 +rust,root-help,13,1299.673,0.000,1251.000,6496,349,0,1,2 +go,root-help,13,3743.743,3682.000,0.000,13160,707,0,41,34 +go,root-help,14,3777.317,2788.000,929.000,13220,694,0,40,36 +rust,root-help,14,1798.408,0.000,1701.000,6344,350,0,1,1 +rust,root-help,15,1205.684,1168.000,0.000,6484,349,0,1,1 +go,root-help,15,4162.900,3062.000,1020.000,13092,693,0,36,30 +go,root-help,16,4200.490,1016.000,3049.000,13148,691,0,22,17 +rust,root-help,16,1745.068,0.000,1695.000,6664,350,0,1,1 +rust,root-help,17,1436.098,0.000,1390.000,6660,349,0,1,1 +go,root-help,17,4536.373,936.000,2810.000,13176,691,0,38,35 +go,root-help,18,4622.895,3157.000,789.000,13176,687,0,42,36 +rust,root-help,18,1731.122,838.000,838.000,6628,349,0,1,2 +rust,root-help,19,1711.435,1652.000,0.000,6484,350,0,1,1 +go,root-help,19,5209.507,2800.000,1866.000,13176,702,0,44,41 +go,root-help,20,4232.661,2075.000,2075.000,13180,697,0,16,12 +rust,root-help,20,2514.974,0.000,2099.000,6792,351,0,1,2 +rust,root-help,21,2016.348,0.000,1911.000,6600,350,0,1,2 +go,root-help,21,4761.887,0.000,4637.000,13164,697,0,18,13 +go,root-help,22,4539.157,817.000,3269.000,13148,689,0,33,29 +rust,root-help,22,1608.142,0.000,1562.000,6668,351,0,1,1 +rust,root-help,23,1330.790,0.000,1288.000,6576,350,0,1,1 +go,root-help,23,4313.644,984.000,2953.000,13176,707,0,41,32 +go,root-help,24,5208.626,4090.000,1022.000,13164,701,0,45,41 +rust,root-help,24,2392.744,774.000,1548.000,6852,351,0,1,1 +rust,root-help,25,1712.998,0.000,1649.000,6612,349,0,1,1 +go,root-help,25,4541.011,0.000,4430.000,13180,688,0,16,12 +go,root-help,26,4645.056,2726.000,1817.000,13176,699,0,16,13 +rust,root-help,26,2625.503,1103.000,1103.000,6680,350,0,1,2 +rust,root-help,27,2000.960,958.000,958.000,6824,350,0,1,2 +go,root-help,27,4940.412,898.000,3593.000,13148,691,0,39,34 +go,root-help,28,5266.253,4101.000,1025.000,13176,688,0,18,12 +rust,root-help,28,2418.885,0.000,2331.000,6552,350,0,1,1 +rust,root-help,29,2225.560,0.000,2142.000,6560,349,0,1,1 +go,root-help,29,5601.052,2736.000,2736.000,13160,692,0,50,44 +go,root-help,30,4546.129,3324.000,1108.000,13148,688,0,36,30 +rust,root-help,30,1800.262,873.000,873.000,6716,350,0,1,1 +rust,root-help,31,2126.374,0.000,1756.000,6596,351,0,1,2 +go,root-help,31,4454.188,3482.000,870.000,13160,688,0,39,33 +go,root-help,32,4392.963,1076.000,3230.000,13172,686,0,41,34 +rust,root-help,32,2297.485,0.000,1892.000,6568,350,0,0,3 +rust,root-help,33,1578.315,0.000,1517.000,6784,351,0,1,2 +go,root-help,33,3868.479,1893.000,1893.000,13172,698,0,14,10 +go,root-help,34,3696.324,905.000,2715.000,13048,679,0,14,10 +rust,root-help,34,1849.183,1707.000,0.000,6660,350,0,1,1 +rust,root-help,35,1736.553,1684.000,0.000,6472,349,0,1,1 +go,root-help,35,4354.972,1595.000,2393.000,13164,705,0,41,36 +go,root-help,36,4343.821,1977.000,1977.000,13148,691,0,38,33 +rust,root-help,36,1724.840,839.000,839.000,6720,352,0,1,1 +rust,root-help,37,1235.701,1199.000,0.000,6780,352,0,1,1 +go,root-help,37,3901.470,3816.000,0.000,13172,691,0,26,20 +go,root-help,38,4153.864,3058.000,1019.000,13180,692,0,40,36 +rust,root-help,38,1731.733,0.000,1684.000,6732,349,0,1,1 +rust,root-help,39,1667.202,1287.000,0.000,6728,349,0,1,2 +go,root-help,39,4053.625,2716.000,905.000,13160,701,0,40,31 +go,root-help,40,3499.845,0.000,3421.000,13216,693,0,14,12 +rust,root-help,40,1672.673,0.000,1628.000,6596,350,0,1,1 +rust,root-help,41,1377.938,1336.000,0.000,6624,349,0,1,1 +go,root-help,41,4168.250,950.000,2852.000,13220,703,0,38,39 +go,root-help,42,4808.525,3191.000,797.000,13184,691,0,38,33 +rust,root-help,42,1784.873,862.000,862.000,6876,352,0,1,2 +rust,root-help,43,2153.597,0.000,1716.000,6588,351,0,1,3 +go,root-help,43,4259.001,2076.000,2076.000,13172,690,0,42,34 +go,root-help,44,4694.200,1692.000,2538.000,13176,687,0,41,32 +rust,root-help,44,1801.674,0.000,1748.000,6672,349,0,1,2 +rust,root-help,45,1758.604,0.000,1409.000,6608,350,0,1,2 +go,root-help,45,3671.839,0.000,3606.000,13216,715,0,38,35 +go,root-help,46,4391.710,1593.000,2389.000,13164,696,0,43,35 +rust,root-help,46,1933.372,941.000,941.000,6748,351,0,1,2 +rust,root-help,47,1351.588,0.000,1310.000,6472,349,0,1,1 +go,root-help,47,4571.408,896.000,3587.000,13084,682,0,41,35 +go,root-help,48,4785.070,2187.000,2187.000,13148,700,0,40,36 +rust,root-help,48,1822.965,1738.000,0.000,6628,350,0,1,1 +rust,root-help,49,1494.607,722.000,722.000,6644,351,0,1,1 +go,root-help,49,4135.289,928.000,2784.000,13180,684,0,15,13 +go,root-help,50,4240.236,0.000,3839.000,13180,688,0,15,12 +rust,root-help,50,1921.961,928.000,928.000,6696,350,0,1,2 +rust,root-help,51,1628.719,783.000,783.000,6612,350,0,1,1 +go,root-help,51,4597.356,1127.000,3382.000,13176,702,0,42,35 +go,root-help,52,4203.979,1647.000,2471.000,13148,700,0,40,36 +rust,root-help,52,1773.340,0.000,1724.000,6676,350,0,1,1 +rust,root-help,53,1488.986,722.000,722.000,6500,349,0,1,1 +go,root-help,53,4324.184,2122.000,2122.000,13164,699,0,20,13 +go,root-help,54,3932.939,1928.000,1928.000,13216,707,0,39,34 +rust,root-help,54,1538.149,747.000,747.000,6640,350,0,1,1 +rust,root-help,55,1526.037,0.000,1465.000,6492,349,0,1,2 +go,root-help,55,4437.175,870.000,3483.000,13164,691,0,47,38 +go,root-help,56,5173.129,940.000,3760.000,13164,692,0,42,37 +rust,root-help,56,1965.823,949.000,949.000,6736,351,0,1,1 +rust,root-help,57,2162.682,0.000,1731.000,6632,351,0,1,2 +go,root-help,57,4355.112,2126.000,2126.000,13148,687,0,18,11 +go,root-help,58,4520.353,2469.000,1646.000,13148,706,0,40,35 +rust,root-help,58,1895.620,1843.000,0.000,6684,349,0,1,1 +rust,root-help,59,1904.227,767.000,767.000,6656,350,0,1,2 +go,root-help,59,4112.025,1830.000,1830.000,13240,693,0,21,17 +go,root-help,60,4754.573,1719.000,2578.000,13148,686,0,42,38 +rust,root-help,60,1678.934,0.000,1631.000,6672,350,0,1,1 +rust,root-help,61,1307.706,0.000,1269.000,6152,355,0,1,1 +go,root-help,61,4396.630,996.000,2988.000,13148,707,0,43,36 +go,root-help,62,4989.754,1801.000,2702.000,13236,693,0,37,33 +rust,root-help,62,2242.313,1089.000,1089.000,6772,351,0,1,1 +rust,root-help,63,2164.466,1046.000,1046.000,6844,351,0,1,1 +go,root-help,63,5816.868,2626.000,2626.000,13164,688,0,46,44 +go,root-help,64,5362.225,1609.000,3219.000,13180,693,0,47,38 +rust,root-help,64,2441.897,0.000,2010.000,6564,350,0,1,2 +rust,root-help,65,1619.613,780.000,780.000,6512,350,0,1,2 +go,root-help,65,4076.818,1002.000,3008.000,13176,702,0,42,38 +go,root-help,66,3791.723,1849.000,1849.000,13148,688,0,15,11 +rust,root-help,66,1749.036,0.000,1694.000,6512,350,0,1,1 +rust,root-help,67,1410.079,0.000,1362.000,6788,350,0,1,1 +go,root-help,67,3632.185,0.000,3571.000,13180,702,0,41,34 +go,root-help,68,4620.591,0.000,4117.000,13180,692,0,47,32 +rust,root-help,68,1869.461,906.000,906.000,6600,350,0,1,2 +rust,root-help,69,1383.580,0.000,1341.000,6668,351,0,1,1 +go,root-help,69,4930.162,2686.000,1790.000,13176,694,0,37,34 +go,root-help,70,3887.003,2858.000,952.000,13176,690,0,37,32 +rust,root-help,70,2089.976,1014.000,1014.000,6636,350,0,1,1 +rust,root-help,71,2278.009,726.000,1453.000,6652,350,0,1,2 +go,root-help,71,5287.773,1799.000,2699.000,13128,701,0,31,26 +go,root-help,72,4546.231,3347.000,1115.000,13180,699,0,41,36 +rust,root-help,72,1801.264,1751.000,0.000,6668,351,0,1,1 +rust,root-help,73,1679.345,0.000,1579.000,6684,349,0,1,1 +go,root-help,73,5551.549,836.000,4180.000,13164,703,0,49,41 +go,root-help,74,5126.150,3015.000,2010.000,13224,692,0,16,13 +rust,root-help,74,1922.360,0.000,1865.000,6428,350,0,1,1 +rust,root-help,75,2538.738,0.000,2111.000,6608,350,0,1,2 +go,root-help,75,5569.875,842.000,4212.000,13080,686,0,45,44 +go,root-help,76,4583.599,3374.000,1124.000,13180,707,0,41,33 +rust,root-help,76,2453.439,1588.000,794.000,6344,350,0,1,1 +rust,root-help,77,2296.765,0.000,2196.000,6588,349,0,1,1 +go,root-help,77,5672.216,3397.000,1698.000,13164,692,0,43,43 +go,root-help,78,5774.408,1896.000,3792.000,13220,700,0,51,47 +rust,root-help,78,2210.822,1066.000,1066.000,6656,349,0,1,2 +rust,root-help,79,1978.106,1894.000,0.000,6544,350,0,1,1 +go,root-help,79,5255.343,1885.000,2828.000,13160,696,0,23,20 +go,root-help,80,5230.426,1745.000,2618.000,13180,687,0,15,13 +rust,root-help,80,2424.615,0.000,2341.000,6644,350,0,1,2 +rust,root-help,81,2296.945,1104.000,1104.000,6484,349,0,1,1 +go,root-help,81,5700.931,3388.000,1694.000,13148,686,0,15,13 +go,root-help,82,6304.204,1891.000,3783.000,13032,679,0,17,13 +rust,root-help,82,2685.113,0.000,2561.000,6540,350,0,1,2 +rust,root-help,83,2440.023,1171.000,1171.000,6236,356,0,1,1 +go,root-help,83,6599.870,1716.000,4292.000,13184,708,0,55,50 +go,root-help,84,5173.609,1014.000,4058.000,13176,690,0,20,14 +rust,root-help,84,2374.551,0.000,2308.000,6716,349,0,1,1 +rust,root-help,85,2141.383,0.000,2068.000,6628,349,0,1,1 +go,root-help,85,5228.271,3050.000,2033.000,13140,705,0,21,15 +go,root-help,86,5912.527,2668.000,2668.000,13160,692,0,31,27 +rust,root-help,86,2315.681,0.000,2251.000,6620,350,0,1,1 +rust,root-help,87,2648.376,1414.000,707.000,6468,350,0,1,2 +go,root-help,87,5177.447,2031.000,3046.000,13180,688,0,16,12 +go,root-help,88,6234.743,3811.000,1905.000,13148,691,0,50,42 +rust,root-help,88,2914.716,0.000,2429.000,6716,350,0,1,2 +rust,root-help,89,2354.673,752.000,1505.000,6688,350,0,1,2 +go,root-help,89,5419.951,0.000,5316.000,13148,701,0,16,12 +go,root-help,90,5720.347,3737.000,1868.000,13184,711,0,48,43 +rust,root-help,90,2581.730,1253.000,1253.000,6472,349,0,1,1 +rust,root-help,91,2366.055,0.000,2266.000,6632,348,0,1,2 +go,root-help,91,5951.942,3194.000,2129.000,13160,691,0,28,21 +go,root-help,92,5677.125,2226.000,3339.000,13216,701,0,43,39 +rust,root-help,92,2447.588,0.000,2379.000,6556,350,0,1,1 +rust,root-help,93,2074.978,997.000,997.000,6600,350,0,1,1 +go,root-help,93,5499.612,1944.000,2917.000,13180,688,0,16,13 +go,root-help,94,5538.024,1080.000,4322.000,13184,701,0,39,36 +rust,root-help,94,3014.834,823.000,1647.000,6716,350,0,1,2 +rust,root-help,95,2365.032,0.000,2272.000,6576,350,0,1,2 +go,root-help,95,5107.676,1516.000,3032.000,13180,707,0,41,37 +go,root-help,96,4127.955,1012.000,3037.000,13184,704,0,15,7 +rust,root-help,96,1750.989,0.000,1698.000,6724,350,0,1,1 +rust,root-help,97,1949.983,0.000,1550.000,6600,351,0,1,2 +go,root-help,97,3874.019,1898.000,1898.000,13148,700,0,39,35 +go,root-help,98,4551.671,2460.000,1640.000,13180,685,0,40,34 +rust,root-help,98,1881.305,1822.000,0.000,6536,351,0,1,2 +rust,root-help,99,1638.038,0.000,1575.000,6664,350,0,1,2 +go,root-help,99,3859.240,948.000,2845.000,13148,686,0,41,34 +go,root-help,100,4882.753,2628.000,1752.000,13176,698,0,25,20 +rust,root-help,100,1881.584,910.000,910.000,6464,350,0,1,2 +rust,root-help,101,1813.688,0.000,1749.000,6644,349,0,1,1 +go,root-help,101,4272.205,3122.000,1040.000,13084,692,0,42,37 +go,root-help,102,4741.900,856.000,3426.000,13164,687,0,38,36 +rust,root-help,102,1803.178,876.000,876.000,6516,350,0,1,1 +rust,root-help,103,1446.497,1399.000,0.000,6544,349,0,1,1 +go,root-help,103,3904.155,2878.000,959.000,13164,691,0,39,31 +go,root-help,104,4654.655,1664.000,2497.000,13164,691,0,41,36 +rust,root-help,104,1939.685,0.000,1884.000,6788,351,0,1,1 +rust,root-help,105,1960.313,0.000,1896.000,6572,350,0,1,1 +go,root-help,105,6314.492,3545.000,1772.000,13148,704,0,46,40 +go,root-help,106,6310.405,4538.000,907.000,13160,687,0,39,37 +rust,root-help,106,2031.747,975.000,975.000,6628,348,0,1,1 +rust,root-help,107,2715.612,0.000,2236.000,6684,350,0,1,2 +go,root-help,107,4831.205,2828.000,1885.000,13184,687,0,42,39 +go,root-help,108,4872.924,1908.000,2863.000,13184,696,0,34,32 +rust,root-help,108,2149.348,0.000,2060.000,6744,352,0,1,1 +rust,root-help,109,2325.259,918.000,918.000,6648,348,0,1,2 +go,root-help,109,4501.997,2637.000,1758.000,13176,690,0,36,34 +go,root-help,110,4418.121,2164.000,2164.000,13220,698,0,16,11 +rust,root-help,110,2389.158,0.000,2292.000,6296,357,0,1,2 +rust,root-help,111,2055.572,991.000,991.000,6664,351,0,1,1 +go,root-help,111,4512.237,0.000,4071.000,13176,691,0,18,15 +go,root-help,112,5119.577,2756.000,1837.000,13148,708,0,40,35 +rust,root-help,112,2275.295,0.000,2208.000,6640,350,0,1,1 +rust,root-help,113,2074.979,2007.000,0.000,6660,350,0,1,1 +go,root-help,113,4369.228,983.000,2950.000,13180,689,0,14,9 +go,root-help,114,4661.768,902.000,3608.000,13180,692,0,20,12 +rust,root-help,114,2064.528,1998.000,0.000,6508,350,0,1,1 +rust,root-help,115,2426.789,0.000,1944.000,6620,349,0,1,2 +go,root-help,115,5409.222,3203.000,1601.000,13216,690,0,44,40 +go,root-help,116,4694.960,2280.000,2280.000,13180,687,0,33,28 +rust,root-help,116,2112.169,0.000,2049.000,6792,351,0,1,1 +rust,root-help,117,1855.094,0.000,1771.000,6848,350,0,1,1 +go,root-help,117,4501.798,2016.000,2016.000,13184,695,0,14,9 +go,root-help,118,4549.948,798.000,3195.000,13020,681,0,15,11 +rust,root-help,118,1782.889,0.000,1722.000,6760,351,0,1,2 +rust,root-help,119,1684.365,0.000,1631.000,6576,350,0,1,1 +go,root-help,119,4480.917,872.000,3490.000,13164,697,0,18,14 +go,root-help,120,5401.408,2911.000,1941.000,13172,707,0,42,34 +rust,root-help,120,2100.185,0.000,2035.000,6604,350,0,1,1 +rust,root-help,121,2861.567,1555.000,777.000,6360,351,0,1,3 +go,root-help,121,5891.928,4411.000,882.000,13012,680,0,13,8 +go,root-help,122,6242.106,2827.000,2827.000,13148,704,0,30,24 +rust,root-help,122,3357.978,901.000,1802.000,6664,350,0,1,2 +rust,root-help,123,2518.842,0.000,2418.000,6800,351,0,1,2 +go,root-help,123,4921.325,3835.000,958.000,13180,693,0,14,10 +go,root-help,124,6173.798,2796.000,2796.000,13084,689,0,26,23 +rust,root-help,124,2381.584,0.000,2314.000,6672,351,0,1,1 +rust,root-help,125,2731.782,0.000,2649.000,6472,349,0,1,1 +go,root-help,125,4515.592,1760.000,2640.000,13220,691,0,13,8 +go,root-help,126,5158.170,2807.000,1871.000,13140,698,0,22,19 +rust,root-help,126,1837.103,0.000,1762.000,6668,350,0,1,2 +rust,root-help,127,1569.729,0.000,1504.000,6728,350,0,1,2 +go,root-help,127,3957.104,2587.000,1293.000,13176,695,0,40,37 +go,root-help,128,4826.237,2162.000,2162.000,13148,692,0,40,38 +rust,root-help,128,1925.637,930.000,930.000,6568,351,0,1,1 +rust,root-help,129,1678.573,0.000,1587.000,6700,350,0,1,1 +go,root-help,129,4159.555,939.000,2819.000,13160,692,0,41,34 +go,root-help,130,4000.335,0.000,3920.000,13084,701,0,13,8 +rust,root-help,130,2008.362,975.000,975.000,6648,348,0,1,1 +rust,root-help,131,2130.834,1696.000,0.000,6672,350,0,1,2 +go,root-help,131,4627.974,0.000,4483.000,13228,688,0,43,35 +go,root-help,132,4491.828,1101.000,3305.000,13148,687,0,15,11 +rust,root-help,132,2315.570,1271.000,635.000,6616,350,0,1,2 +rust,root-help,133,1612.990,1557.000,0.000,6568,350,0,1,2 +go,root-help,133,4634.446,0.000,4553.000,13164,696,0,37,33 +go,root-help,134,5176.334,3543.000,885.000,13180,688,0,17,12 +rust,root-help,134,1808.237,0.000,1716.000,6540,348,0,1,1 +rust,root-help,135,1554.010,0.000,1498.000,6620,349,0,1,1 +go,root-help,135,4983.632,1107.000,3322.000,13184,692,0,41,35 +go,root-help,136,4453.436,2191.000,2191.000,13180,691,0,41,34 +rust,root-help,136,1853.212,0.000,1802.000,6716,349,0,1,1 +rust,root-help,137,1448.601,0.000,1402.000,6628,350,0,1,1 +go,root-help,137,3842.469,942.000,2827.000,13176,693,0,34,28 +go,root-help,138,4138.065,3046.000,1015.000,13148,692,0,40,33 +rust,root-help,138,1835.447,0.000,1785.000,6412,352,0,1,1 +rust,root-help,139,2168.463,1733.000,0.000,6720,351,0,1,2 +go,root-help,139,5032.344,2737.000,1825.000,13160,698,0,37,36 +go,root-help,140,5255.743,3181.000,1590.000,13240,704,0,44,36 +rust,root-help,140,1851.248,896.000,896.000,6432,351,0,1,2 +rust,root-help,141,1783.211,0.000,1437.000,6344,350,0,1,2 +go,root-help,141,3940.673,2903.000,967.000,13164,701,0,39,35 +go,root-help,142,3950.251,969.000,2909.000,13164,691,0,36,37 +rust,root-help,142,1571.431,0.000,1515.000,6680,349,0,1,1 +rust,root-help,143,1294.352,0.000,1245.000,6632,351,0,1,1 +go,root-help,143,3623.557,2671.000,890.000,13180,703,0,39,29 +go,root-help,144,4840.142,2595.000,1730.000,13176,685,0,24,20 +rust,root-help,144,1962.717,0.000,1908.000,6516,350,0,1,1 +rust,root-help,145,2609.682,0.000,2148.000,6864,352,0,1,2 +go,root-help,145,4596.525,1675.000,2513.000,13176,686,0,40,32 +go,root-help,146,4265.124,963.000,2891.000,13216,692,0,27,23 +rust,root-help,146,1915.749,0.000,1856.000,6788,351,0,1,2 +rust,root-help,147,1830.410,736.000,736.000,6664,349,0,1,2 +go,root-help,147,4272.858,2091.000,2091.000,13164,708,0,45,36 +go,root-help,148,4844.551,2613.000,1742.000,13148,691,0,41,37 +rust,root-help,148,2042.708,0.000,1978.000,6572,350,0,1,2 +rust,root-help,149,1940.566,0.000,1855.000,6560,352,0,1,2 +go,root-help,149,4756.917,1735.000,2603.000,13176,688,0,39,38 +go,root-help,150,4917.618,2669.000,1779.000,13216,703,0,41,37 +rust,root-help,150,1932.059,938.000,938.000,6808,350,0,1,1 +rust,root-help,151,1577.334,0.000,1526.000,6856,350,0,1,1 +go,root-help,151,4438.198,2381.000,1587.000,13128,701,0,22,17 +go,root-help,152,4311.129,1041.000,3123.000,12956,690,0,16,14 +rust,root-help,152,2141.724,1037.000,1037.000,6520,350,0,1,1 +rust,root-help,153,1970.190,950.000,950.000,6744,350,0,1,1 +go,root-help,153,4766.656,2805.000,1870.000,13180,692,0,45,38 +go,root-help,154,5964.536,2498.000,2498.000,13148,694,0,23,18 +rust,root-help,154,2234.958,0.000,2150.000,6724,349,0,1,2 +rust,root-help,155,2216.845,0.000,2092.000,6696,351,0,1,2 +go,root-help,155,5416.787,1636.000,3272.000,13164,691,0,45,40 +go,root-help,156,5327.929,1782.000,2673.000,13180,682,0,38,34 +rust,root-help,156,1963.159,0.000,1893.000,6604,350,0,1,2 +rust,root-help,157,1916.331,0.000,1839.000,6636,349,0,1,2 +go,root-help,157,4717.703,3701.000,925.000,13176,690,0,43,37 +go,root-help,158,5275.560,2849.000,1899.000,13176,692,0,43,36 +rust,root-help,158,2864.301,1133.000,1133.000,6704,349,0,1,2 +rust,root-help,159,2206.456,1055.000,1055.000,6732,352,0,1,2 +go,root-help,159,4614.881,1796.000,2695.000,13084,703,0,16,13 +go,root-help,160,5374.277,1934.000,2901.000,13184,704,0,44,36 +rust,root-help,160,2860.844,0.000,2346.000,6664,350,0,1,2 +rust,root-help,161,2139.610,1032.000,1032.000,6664,350,0,1,2 +go,root-help,161,4943.487,971.000,3885.000,13240,689,0,17,13 +go,root-help,162,5330.975,0.000,5132.000,13224,689,0,16,12 +rust,root-help,162,2232.554,2167.000,0.000,6448,351,0,1,1 +rust,root-help,163,2548.668,1033.000,1033.000,6672,350,0,1,2 +go,root-help,163,4333.471,2544.000,1696.000,13164,692,0,40,34 +go,root-help,164,4162.231,3066.000,1022.000,13232,703,0,39,34 +rust,root-help,164,2269.523,0.000,2190.000,6544,351,0,1,1 +rust,root-help,165,1876.365,910.000,910.000,6544,348,0,1,1 +go,root-help,165,4914.874,1929.000,2894.000,13220,706,0,46,40 +go,root-help,166,5165.705,2016.000,3025.000,13148,692,0,43,37 +rust,root-help,166,2095.826,1019.000,1019.000,6844,350,0,1,1 +rust,root-help,167,1946.568,0.000,1884.000,6664,351,0,1,1 +go,root-help,167,5015.592,1686.000,2530.000,13216,704,0,15,12 +go,root-help,168,4596.355,831.000,3324.000,13180,691,0,40,35 +rust,root-help,168,1884.730,0.000,1820.000,6828,352,0,1,2 +rust,root-help,169,1721.193,1655.000,0.000,6736,350,0,1,1 +go,root-help,169,5757.887,1044.000,4179.000,13176,689,0,43,40 +go,root-help,170,4634.617,1803.000,2705.000,13084,688,0,41,35 +rust,root-help,170,2045.362,1989.000,0.000,6684,350,0,1,1 +rust,root-help,171,1677.552,809.000,809.000,6592,349,0,1,1 +go,root-help,171,4576.578,1023.000,3069.000,13176,685,0,40,38 +go,root-help,172,4422.218,2579.000,1719.000,13176,702,0,17,12 +rust,root-help,172,2084.226,1008.000,1008.000,6656,350,0,1,2 +rust,root-help,173,2145.181,0.000,1751.000,6548,350,0,1,2 +go,root-help,173,4347.538,1060.000,3182.000,13208,705,0,17,12 +go,root-help,174,5019.048,1796.000,2694.000,13164,696,0,42,37 +rust,root-help,174,1987.194,0.000,1922.000,6596,351,0,1,1 +rust,root-help,175,1678.133,0.000,1623.000,6720,350,0,1,1 +go,root-help,175,5115.821,2792.000,1861.000,13176,695,0,46,40 +go,root-help,176,5074.945,1997.000,2996.000,13128,701,0,49,46 +rust,root-help,176,2212.147,0.000,2141.000,6712,350,0,1,1 +rust,root-help,177,2175.317,0.000,1954.000,6628,348,0,1,1 +go,root-help,177,4435.363,1083.000,3250.000,13176,697,0,14,10 +go,root-help,178,6004.740,1815.000,3630.000,13148,686,0,46,42 +rust,root-help,178,2629.261,713.000,1427.000,6568,350,0,1,2 +rust,root-help,179,2005.958,966.000,966.000,6720,349,0,1,2 +go,root-help,179,5037.143,905.000,3623.000,13220,696,0,14,11 +go,root-help,180,5176.034,3034.000,2023.000,13172,705,0,25,23 +rust,root-help,180,2361.196,2289.000,0.000,6720,350,0,1,1 +rust,root-help,181,2613.801,717.000,1435.000,6656,351,0,1,2 +go,root-help,181,4970.778,1796.000,2694.000,13164,705,0,30,23 +go,root-help,182,4843.039,2198.000,2198.000,13164,683,0,15,12 +rust,root-help,182,1673.293,0.000,1613.000,6588,351,0,1,2 +rust,root-help,183,1410.530,0.000,1368.000,6736,350,0,1,1 +go,root-help,183,4680.764,3381.000,845.000,13148,701,0,35,33 +go,root-help,184,4292.143,967.000,2902.000,13220,695,0,38,32 +rust,root-help,184,1689.755,819.000,819.000,6408,352,0,1,2 +rust,root-help,185,1517.040,1456.000,0.000,6432,350,0,1,1 +go,root-help,185,3842.338,1879.000,1879.000,13084,687,0,16,12 +go,root-help,186,3959.940,2903.000,967.000,13160,687,0,38,35 +rust,root-help,186,1841.700,895.000,895.000,6600,351,0,1,1 +rust,root-help,187,1453.821,0.000,1396.000,6600,348,0,1,1 +go,root-help,187,4267.015,771.000,3087.000,13184,700,0,40,34 +go,root-help,188,4546.952,2223.000,2223.000,13148,707,0,42,36 +rust,root-help,188,2292.597,1080.000,1080.000,6660,351,0,1,1 +rust,root-help,189,1904.248,0.000,1823.000,6596,351,0,1,1 +go,root-help,189,5614.298,1711.000,3423.000,13216,692,0,48,41 +go,root-help,190,6058.873,2745.000,2745.000,13180,686,0,39,36 +rust,root-help,190,2083.223,0.000,2014.000,6752,350,0,1,1 +rust,root-help,191,2456.565,0.000,1950.000,6620,350,0,1,2 +go,root-help,191,5364.899,1047.000,4191.000,13148,699,0,44,41 +go,root-help,192,5420.042,1961.000,2942.000,13032,681,0,17,13 +rust,root-help,192,2195.084,0.000,2110.000,6728,351,0,1,2 +rust,root-help,193,1928.964,0.000,1852.000,6716,350,0,1,2 +go,root-help,193,4558.253,2227.000,2227.000,13180,686,0,13,7 +go,root-help,194,4614.980,2719.000,1812.000,13148,702,0,20,13 +rust,root-help,194,2255.749,1094.000,1094.000,6556,350,0,1,1 +rust,root-help,195,2096.227,1012.000,1012.000,6460,350,0,1,1 +go,root-help,195,5212.221,2037.000,3056.000,13172,698,0,13,7 +go,root-help,196,5386.319,1761.000,3523.000,13084,703,0,47,40 +rust,root-help,196,2316.943,1127.000,1127.000,6748,349,0,1,1 +rust,root-help,197,2102.831,0.000,2033.000,6664,350,0,1,1 +go,root-help,197,5603.086,1667.000,3335.000,13172,704,0,32,27 +go,root-help,198,5000.213,1960.000,2940.000,13180,688,0,20,13 +rust,root-help,198,2548.076,1237.000,1237.000,6664,349,0,1,1 +rust,root-help,199,2626.144,1069.000,1069.000,6576,349,0,1,2 +go,root-help,199,5117.545,1993.000,2990.000,13084,689,0,27,25 +go,root-help,200,4866.993,1911.000,2867.000,13048,679,0,16,11 +rust,root-help,200,2785.252,690.000,1381.000,6548,350,0,1,2 +rust,root-help,201,2033.691,976.000,976.000,6664,350,0,1,2 +go,root-help,201,4335.815,2118.000,2118.000,13148,701,0,15,12 +go,root-help,202,4111.143,1005.000,3015.000,13180,683,0,40,33 +rust,root-help,202,2217.627,593.000,1187.000,6664,349,0,1,2 +rust,root-help,203,1348.884,0.000,1306.000,6504,349,0,1,1 +go,root-help,203,4124.960,2019.000,2019.000,13164,703,0,13,8 +go,root-help,204,4176.085,4077.000,0.000,13180,689,0,14,8 +rust,root-help,204,2126.054,0.000,2062.000,6488,350,0,1,1 +rust,root-help,205,1770.166,1709.000,0.000,6864,352,0,1,1 +go,root-help,205,4791.371,0.000,4339.000,13160,690,0,41,37 +go,root-help,206,5113.237,4604.000,0.000,13180,692,0,26,21 +rust,root-help,206,2129.400,0.000,2052.000,6668,351,0,1,1 +rust,root-help,207,1819.899,0.000,1755.000,6656,350,0,1,1 +go,root-help,207,4750.524,2584.000,1722.000,13180,692,0,40,37 +go,root-help,208,4991.437,1799.000,2699.000,13180,686,0,22,17 +rust,root-help,208,1959.381,948.000,948.000,6600,349,0,1,2 +rust,root-help,209,1874.201,1809.000,0.000,6624,350,0,1,1 +go,root-help,209,5361.522,2899.000,1932.000,13164,696,0,47,41 +go,root-help,210,4792.644,863.000,3453.000,13216,697,0,14,9 +rust,root-help,210,2307.144,0.000,2231.000,6864,350,0,1,2 +rust,root-help,211,2049.981,989.000,989.000,6600,351,0,1,1 +go,root-help,211,4821.759,870.000,3482.000,13184,687,0,16,13 +go,root-help,212,5417.447,807.000,4039.000,13164,690,0,30,27 +rust,root-help,212,2349.565,1132.000,1132.000,6548,349,0,1,2 +rust,root-help,213,1963.568,0.000,1883.000,6672,350,0,1,1 +go,root-help,213,4630.640,1813.000,2719.000,13148,693,0,16,12 +go,root-help,214,5380.237,2944.000,1962.000,13180,687,0,41,36 +rust,root-help,214,1952.939,948.000,948.000,6676,350,0,1,1 +rust,root-help,215,2140.571,0.000,2068.000,6480,349,0,1,1 +go,root-help,215,5499.642,2862.000,1908.000,13084,692,0,42,41 +go,root-help,216,5389.875,1057.000,4231.000,13084,692,0,50,43 +rust,root-help,216,2417.932,2291.000,0.000,6668,350,0,1,1 +rust,root-help,217,2145.501,0.000,2060.000,6940,351,0,1,1 +go,root-help,217,5265.852,2810.000,1873.000,13176,706,0,20,15 +go,root-help,218,5443.396,4257.000,1064.000,13160,703,0,48,41 +rust,root-help,218,2728.817,736.000,1472.000,6716,349,0,1,2 +rust,root-help,219,2143.106,0.000,2055.000,6712,350,0,1,1 +go,root-help,219,6362.523,1654.000,4137.000,13176,698,0,53,48 +go,root-help,220,6992.305,2550.000,3401.000,13176,704,0,54,48 +rust,root-help,220,2383.157,2287.000,0.000,6708,350,0,1,2 +rust,root-help,221,2224.248,1065.000,1065.000,6472,349,0,1,2 +go,root-help,221,4828.070,1892.000,2838.000,13220,703,0,46,40 +go,root-help,222,6193.134,1733.000,3467.000,13172,681,0,19,12 +rust,root-help,222,1983.177,959.000,959.000,6876,351,0,1,2 +rust,root-help,223,1719.811,0.000,1651.000,6888,351,0,1,2 +go,root-help,223,4694.288,1842.000,2764.000,13148,686,0,44,40 +go,root-help,224,5276.722,2846.000,1897.000,13172,688,0,37,34 +rust,root-help,224,2148.075,906.000,906.000,6644,349,0,1,2 +rust,root-help,225,1437.290,1387.000,0.000,6648,348,0,1,2 +go,root-help,225,4222.283,1036.000,3110.000,13180,691,0,42,40 +go,root-help,226,4584.273,1121.000,3363.000,13172,691,0,40,36 +rust,root-help,226,2372.757,0.000,1936.000,6600,351,0,1,2 +rust,root-help,227,1634.391,0.000,1577.000,6720,350,0,1,2 +go,root-help,227,4521.093,2220.000,2220.000,13148,709,0,45,38 +go,root-help,228,4639.486,2729.000,1819.000,13220,702,0,42,36 +rust,root-help,228,1978.757,964.000,964.000,6588,351,0,1,1 +rust,root-help,229,1684.414,1628.000,0.000,6496,351,0,1,1 +go,root-help,229,5476.689,2300.000,2300.000,13180,691,0,42,39 +go,root-help,230,5795.057,4363.000,872.000,13148,691,0,44,39 +rust,root-help,230,2678.262,1083.000,1083.000,6552,350,0,1,2 +rust,root-help,231,1967.166,942.000,942.000,6536,351,0,1,2 +go,root-help,231,4744.755,2779.000,1853.000,13176,688,0,30,26 +go,root-help,232,4810.337,0.000,4726.000,13148,707,0,41,36 +rust,root-help,232,1963.309,0.000,1911.000,6612,350,0,1,1 +rust,root-help,233,2323.826,0.000,1901.000,6604,350,0,1,2 +go,root-help,233,4995.424,3603.000,900.000,13180,705,0,42,37 +go,root-help,234,4794.998,1875.000,2813.000,13216,682,0,35,32 +rust,root-help,234,2237.153,940.000,940.000,6504,350,0,1,2 +rust,root-help,235,1794.050,0.000,1731.000,6836,350,0,1,1 +go,root-help,235,4917.428,890.000,3561.000,13180,704,0,45,38 +go,root-help,236,5309.165,968.000,3872.000,13144,698,0,36,33 +rust,root-help,236,1801.795,0.000,1736.000,6620,349,0,1,1 +rust,root-help,237,1596.909,0.000,1528.000,6672,351,0,1,2 +go,root-help,237,3743.233,0.000,3665.000,13176,703,0,38,29 +go,root-help,238,4677.958,0.000,4214.000,13220,704,0,41,39 +rust,root-help,238,1901.071,918.000,918.000,6476,349,0,1,1 +rust,root-help,239,1638.017,793.000,793.000,6668,353,0,1,1 +go,root-help,239,4189.571,2057.000,2057.000,13164,691,0,20,12 +go,root-help,240,5073.010,2744.000,1829.000,13140,706,0,44,39 +rust,root-help,240,1830.649,0.000,1769.000,6600,351,0,1,2 +rust,root-help,241,1530.396,741.000,741.000,6408,353,0,1,1 +go,root-help,241,3848.600,2507.000,1253.000,13164,700,0,14,8 +go,root-help,242,4927.938,2691.000,1794.000,13176,687,0,35,33 +rust,root-help,242,2208.229,0.000,1848.000,6688,349,0,1,2 +rust,root-help,243,2744.376,0.000,2203.000,6584,350,0,1,2 +go,root-help,243,4201.894,0.000,4094.000,13180,684,0,39,31 +go,root-help,244,4498.120,1005.000,3015.000,13176,697,0,40,33 +rust,root-help,244,1764.526,0.000,1699.000,6600,351,0,1,2 +rust,root-help,245,1674.795,799.000,799.000,6744,349,0,0,3 +go,root-help,245,4415.367,1079.000,3238.000,13164,700,0,15,11 +go,root-help,246,4100.924,2013.000,2013.000,13176,692,0,14,7 +rust,root-help,246,1997.181,0.000,1944.000,6528,349,0,1,1 +rust,root-help,247,2161.240,887.000,887.000,6588,350,0,1,2 +go,root-help,247,4326.077,1690.000,2536.000,13172,691,0,42,38 +go,root-help,248,5434.660,1966.000,2950.000,13184,705,0,45,39 +rust,root-help,248,2173.935,1054.000,1054.000,6508,350,0,1,1 +rust,root-help,249,2058.947,0.000,1986.000,6412,356,0,1,1 +go,root-help,249,5973.422,3051.000,2034.000,13180,691,0,47,45 +go,root-help,250,5354.709,0.000,5254.000,13184,700,0,46,39 +rust,root-help,250,2255.578,916.000,916.000,6808,351,0,1,2 +rust,root-help,251,1542.689,1482.000,0.000,6368,351,0,1,2 +go,root-help,251,4344.593,1954.000,1954.000,13084,691,0,38,36 +go,root-help,252,4057.592,1982.000,1982.000,13176,703,0,38,32 +rust,root-help,252,2187.390,885.000,885.000,6528,349,0,1,2 +rust,root-help,253,1572.454,0.000,1515.000,6208,356,0,1,2 +go,root-help,253,4572.361,1044.000,3133.000,13220,692,0,43,39 +go,root-help,254,4180.826,0.000,4089.000,13220,706,0,38,35 +rust,root-help,254,2316.792,898.000,898.000,6568,350,0,1,2 +rust,root-help,255,1558.418,0.000,1502.000,6604,350,0,1,1 +go,root-help,255,4608.418,1672.000,2508.000,13220,692,0,40,34 +go,root-help,256,4621.873,2081.000,2081.000,13140,700,0,38,35 +rust,root-help,256,1794.190,0.000,1731.000,6684,351,0,1,2 +rust,root-help,257,2569.227,0.000,2046.000,6236,356,0,1,2 +go,root-help,257,3831.099,937.000,2811.000,13180,687,0,14,9 +go,root-help,258,4313.284,1941.000,1941.000,13148,698,0,26,22 +rust,root-help,258,2092.461,0.000,2019.000,6560,350,0,1,1 +rust,root-help,259,1648.787,0.000,1589.000,6632,348,0,1,1 +go,root-help,259,4000.505,1812.000,1812.000,13164,687,0,16,10 +go,root-help,260,3937.687,1278.000,2556.000,13176,698,0,15,9 +rust,root-help,260,1919.406,922.000,922.000,6612,351,0,1,2 +rust,root-help,261,1733.285,0.000,1678.000,6720,350,0,1,1 +go,root-help,261,4144.235,3038.000,1012.000,13084,695,0,36,34 +go,root-help,262,4509.001,3316.000,1105.000,13148,711,0,43,38 +rust,root-help,262,2289.962,0.000,1882.000,6536,349,0,1,2 +rust,root-help,263,1571.632,0.000,1514.000,6520,350,0,1,2 +go,root-help,263,4372.494,2132.000,2132.000,13036,680,0,38,33 +go,root-help,264,4107.807,2016.000,2016.000,13220,706,0,41,37 +rust,root-help,264,1802.296,0.000,1752.000,6236,356,0,1,1 +rust,root-help,265,1948.492,0.000,1564.000,6624,348,0,1,1 +go,root-help,265,4855.852,873.000,3493.000,13180,692,0,39,34 +go,root-help,266,4352.457,853.000,3413.000,13164,688,0,16,9 +rust,root-help,266,1680.618,1631.000,0.000,6524,350,0,1,1 +rust,root-help,267,1512.793,0.000,1455.000,6684,351,0,1,1 +go,root-help,267,3952.624,1792.000,1792.000,13180,701,0,40,32 +go,root-help,268,3920.025,964.000,2893.000,13220,703,0,41,35 +rust,root-help,268,1665.068,1621.000,0.000,6680,350,0,1,1 +rust,root-help,269,1369.894,0.000,1320.000,6644,349,0,1,1 +go,root-help,269,4176.947,1898.000,1898.000,13220,694,0,41,37 +go,root-help,270,4170.915,2039.000,2039.000,13148,704,0,26,25 +rust,root-help,270,1754.145,848.000,848.000,6684,350,0,1,1 +rust,root-help,271,1406.613,0.000,1356.000,6608,349,0,1,1 +go,root-help,271,4088.571,3011.000,1003.000,13180,705,0,40,37 +go,root-help,272,5124.608,1697.000,2545.000,13044,695,0,18,13 +rust,root-help,272,2181.388,1055.000,1055.000,6668,349,0,1,1 +rust,root-help,273,2279.502,943.000,943.000,6608,350,0,1,2 +go,root-help,273,4469.286,3269.000,1089.000,13216,709,0,33,29 +go,root-help,274,4825.906,2205.000,2205.000,13180,707,0,42,37 +rust,root-help,274,2025.896,0.000,1973.000,6212,356,0,1,1 +rust,root-help,275,1502.092,0.000,1455.000,6604,350,0,1,1 +go,root-help,275,4218.916,3089.000,1029.000,13176,697,0,41,34 +go,root-help,276,4497.529,1762.000,2644.000,13184,691,0,39,33 +rust,root-help,276,2567.655,2065.000,0.000,6712,349,0,1,2 +rust,root-help,277,2031.326,0.000,1953.000,6676,350,0,1,1 +go,root-help,277,4789.077,1725.000,2588.000,13148,688,0,15,11 +go,root-help,278,4258.641,968.000,2905.000,13164,691,0,40,33 +rust,root-help,278,1776.668,0.000,1724.000,6720,350,0,1,2 +rust,root-help,279,1660.459,804.000,804.000,6812,351,0,1,1 +go,root-help,279,4419.132,3019.000,1006.000,13148,703,0,15,12 +go,root-help,280,4725.898,2775.000,1850.000,13148,687,0,44,37 +rust,root-help,280,2118.890,0.000,2052.000,6816,351,0,1,1 +rust,root-help,281,2233.976,2132.000,0.000,6664,350,0,1,1 +go,root-help,281,5264.119,1576.000,3152.000,13148,691,0,43,37 +go,root-help,282,4771.192,2333.000,2333.000,13084,692,0,32,28 +rust,root-help,282,2335.617,1496.000,748.000,6732,350,0,1,1 +rust,root-help,283,1679.896,0.000,1615.000,6596,349,0,1,1 +go,root-help,283,4903.702,2169.000,2169.000,13176,691,0,43,37 +go,root-help,284,5192.946,780.000,3903.000,13148,692,0,44,38 +rust,root-help,284,2092.461,0.000,2010.000,6624,350,0,1,2 +rust,root-help,285,2008.053,0.000,1932.000,6212,356,0,1,1 +go,root-help,285,4912.549,1759.000,2639.000,13160,697,0,27,26 +go,root-help,286,4210.811,1026.000,3079.000,13148,688,0,15,11 +rust,root-help,286,2250.388,1086.000,1086.000,6720,350,0,1,2 +rust,root-help,287,1663.134,1607.000,0.000,6664,350,0,1,1 +go,root-help,287,4553.815,1932.000,1932.000,13276,693,0,40,33 +go,root-help,288,4282.616,838.000,3353.000,13148,691,0,40,34 +rust,root-help,288,2012.070,1947.000,0.000,6596,351,0,1,1 +rust,root-help,289,1479.119,1431.000,0.000,6484,350,0,1,1 +go,root-help,289,3582.310,876.000,2630.000,13164,698,0,16,11 +go,root-help,290,3985.877,3896.000,0.000,13148,709,0,41,35 +rust,root-help,290,2627.517,0.000,2153.000,6760,348,0,1,2 +rust,root-help,291,1897.744,908.000,908.000,6712,350,0,1,2 +go,root-help,291,5005.143,912.000,3648.000,13084,702,0,27,26 +go,root-help,292,4301.651,832.000,3329.000,13164,704,0,40,36 +rust,root-help,292,1839.517,0.000,1773.000,6628,350,0,1,1 +rust,root-help,293,1955.684,0.000,1891.000,6800,350,0,1,1 +go,root-help,293,4207.635,3077.000,1025.000,13148,701,0,15,9 +go,root-help,294,4638.354,1667.000,2501.000,13244,701,0,41,36 +rust,root-help,294,1867.278,0.000,1805.000,6624,350,0,1,2 +rust,root-help,295,1935.887,772.000,772.000,6792,350,0,1,2 +go,root-help,295,4441.102,2176.000,2176.000,13176,710,0,18,13 +go,root-help,296,4218.906,2072.000,2072.000,13216,702,0,40,35 +rust,root-help,296,2115.173,1021.000,1021.000,6688,350,0,1,1 +rust,root-help,297,2753.442,748.000,1496.000,6516,350,0,1,2 +go,root-help,297,4471.049,1740.000,2610.000,13084,692,0,39,33 +go,root-help,298,4062.081,998.000,2995.000,13180,700,0,15,11 +rust,root-help,298,1657.984,1609.000,0.000,6724,350,0,1,1 +rust,root-help,299,1646.003,794.000,794.000,6640,350,0,1,2 +go,root-help,299,4294.549,3107.000,1035.000,13148,692,0,40,33 +go,episodes-create-help,0,5954.396,2669.000,2669.000,13312,698,0,20,14 +rust,episodes-create-help,0,2741.410,1312.000,1312.000,6576,352,0,1,2 +rust,episodes-create-help,1,2395.540,0.000,2300.000,6796,351,0,1,1 +go,episodes-create-help,1,5665.865,922.000,4612.000,13308,713,0,21,14 +go,episodes-create-help,2,5473.584,1075.000,4301.000,13300,689,0,22,16 +rust,episodes-create-help,2,3116.085,637.000,1913.000,6700,351,0,1,3 +rust,episodes-create-help,3,2301.864,2216.000,0.000,6684,352,0,1,2 +go,episodes-create-help,3,5510.533,970.000,3881.000,13276,688,0,21,13 +go,episodes-create-help,4,4955.510,971.000,3887.000,13304,692,0,44,40 +rust,episodes-create-help,4,2600.615,840.000,1680.000,6880,353,0,1,1 +rust,episodes-create-help,5,1978.586,0.000,1900.000,6720,351,0,1,1 +go,episodes-create-help,5,4658.942,2550.000,1700.000,13292,707,0,44,39 +go,episodes-create-help,6,5268.828,3096.000,2064.000,13312,693,0,45,42 +rust,episodes-create-help,6,2364.472,1148.000,1148.000,6728,352,0,1,1 +rust,episodes-create-help,7,2391.082,1285.000,642.000,6876,353,0,1,2 +go,episodes-create-help,7,4048.145,907.000,2721.000,13348,695,0,17,10 +go,episodes-create-help,8,4002.179,896.000,2688.000,13292,693,0,19,18 +rust,episodes-create-help,8,1722.686,0.000,1674.000,6836,352,0,1,2 +rust,episodes-create-help,9,1698.923,0.000,1326.000,6684,352,0,1,2 +go,episodes-create-help,9,4360.953,1712.000,2568.000,13348,706,0,44,41 +go,episodes-create-help,10,4261.626,1925.000,1925.000,13288,692,0,39,33 +rust,episodes-create-help,10,1607.308,0.000,1539.000,6736,350,0,1,2 +rust,episodes-create-help,11,1258.734,0.000,1220.000,6828,351,0,1,1 +go,episodes-create-help,11,3499.374,2296.000,1148.000,13304,704,0,32,27 +go,episodes-create-help,12,4634.887,1476.000,2214.000,13304,700,0,34,32 +rust,episodes-create-help,12,1587.892,0.000,1541.000,6720,351,0,0,2 +rust,episodes-create-help,13,1268.202,0.000,1226.000,6716,352,0,1,1 +go,episodes-create-help,13,3421.168,2242.000,1121.000,13304,686,0,34,28 +go,episodes-create-help,14,3982.792,904.000,2712.000,13288,702,0,38,25 +rust,episodes-create-help,14,1619.002,1571.000,0.000,6772,351,0,1,2 +rust,episodes-create-help,15,1676.259,672.000,672.000,6816,352,0,1,2 +go,episodes-create-help,15,3477.183,1139.000,2278.000,13300,707,0,31,26 +go,episodes-create-help,16,3413.612,1122.000,2244.000,13276,700,0,36,29 +rust,episodes-create-help,16,2137.927,0.000,1729.000,6612,350,0,1,2 +rust,episodes-create-help,17,1411.772,1367.000,0.000,6772,351,0,1,1 +go,episodes-create-help,17,4166.307,941.000,2823.000,13312,687,0,37,32 +go,episodes-create-help,18,4158.242,2790.000,930.000,13288,708,0,34,29 +rust,episodes-create-help,18,1948.269,0.000,1609.000,6664,351,0,1,1 +rust,episodes-create-help,19,1278.622,618.000,618.000,6704,351,0,1,2 +go,episodes-create-help,19,3682.097,1115.000,2231.000,13268,697,0,33,28 +go,episodes-create-help,20,4551.330,895.000,3581.000,13344,699,0,18,11 +rust,episodes-create-help,20,2053.147,0.000,1719.000,6820,350,0,1,2 +rust,episodes-create-help,21,1309.420,0.000,1265.000,6788,351,0,1,2 +go,episodes-create-help,21,4263.129,1943.000,1943.000,13308,699,0,40,33 +go,episodes-create-help,22,3919.042,0.000,3823.000,13292,694,0,39,33 +rust,episodes-create-help,22,2005.577,0.000,1674.000,6824,352,0,1,2 +rust,episodes-create-help,23,1373.650,0.000,1328.000,6708,352,0,1,2 +go,episodes-create-help,23,3542.255,2328.000,1164.000,13212,701,0,40,31 +go,episodes-create-help,24,4286.703,0.000,4206.000,13344,709,0,36,33 +rust,episodes-create-help,24,1728.097,1685.000,0.000,6832,351,0,1,1 +rust,episodes-create-help,25,1293.982,601.000,601.000,6624,350,0,1,1 +go,episodes-create-help,25,3787.145,3406.000,0.000,13288,693,0,39,27 +go,episodes-create-help,26,3536.524,1066.000,2132.000,13312,693,0,28,21 +rust,episodes-create-help,26,1448.130,0.000,1410.000,6500,356,0,1,1 +rust,episodes-create-help,27,1306.343,0.000,1264.000,6496,357,0,1,2 +go,episodes-create-help,27,3957.766,903.000,2709.000,13308,693,0,40,34 +go,episodes-create-help,28,3543.587,2142.000,1071.000,13276,704,0,18,15 +rust,episodes-create-help,28,2495.699,1934.000,0.000,6744,351,0,1,2 +rust,episodes-create-help,29,1649.810,0.000,1604.000,6940,351,0,1,2 +go,episodes-create-help,29,4007.299,913.000,2740.000,13212,690,0,37,33 +go,episodes-create-help,30,3663.913,2382.000,1191.000,13304,703,0,38,33 +rust,episodes-create-help,30,1694.503,825.000,825.000,6832,350,0,1,1 +rust,episodes-create-help,31,1748.154,0.000,1395.000,6888,351,0,1,2 +go,episodes-create-help,31,3632.334,3575.000,0.000,13308,693,0,38,35 +go,episodes-create-help,32,3589.813,872.000,2617.000,13308,685,0,39,33 +rust,episodes-create-help,32,1493.395,1454.000,0.000,6700,352,0,1,1 +rust,episodes-create-help,33,1427.170,1378.000,0.000,6864,353,0,1,1 +go,episodes-create-help,33,3634.338,1192.000,2385.000,13308,705,0,15,12 +go,episodes-create-help,34,3429.434,777.000,2333.000,13292,693,0,35,28 +rust,episodes-create-help,34,1401.974,0.000,1366.000,6876,351,0,1,1 +rust,episodes-create-help,35,1649.919,0.000,1327.000,6888,352,0,1,2 +go,episodes-create-help,35,3746.448,2754.000,918.000,13212,707,0,42,37 +go,episodes-create-help,36,3846.778,1157.000,2314.000,13292,689,0,15,9 +rust,episodes-create-help,36,1428.984,0.000,1389.000,6784,351,0,1,2 +rust,episodes-create-help,37,1171.471,0.000,1138.000,6532,355,0,1,1 +go,episodes-create-help,37,3791.563,1863.000,1863.000,13332,710,0,36,27 +go,episodes-create-help,38,4040.210,2715.000,905.000,13276,698,0,37,31 +rust,episodes-create-help,38,1797.336,744.000,744.000,6892,352,0,1,2 +rust,episodes-create-help,39,1241.141,0.000,1202.000,6804,351,0,1,2 +go,episodes-create-help,39,3380.541,1107.000,2215.000,13276,692,0,33,28 +go,episodes-create-help,40,3609.631,1774.000,1774.000,13292,706,0,36,28 +rust,episodes-create-help,40,1571.202,767.000,767.000,6496,356,0,1,1 +rust,episodes-create-help,41,1619.543,0.000,1249.000,6712,352,0,1,2 +go,episodes-create-help,41,3785.081,855.000,2567.000,13308,689,0,16,12 +go,episodes-create-help,42,5632.271,2740.000,2740.000,13236,698,0,31,27 +rust,episodes-create-help,42,2434.814,0.000,2362.000,6860,352,0,1,1 +rust,episodes-create-help,43,1722.766,0.000,1661.000,6492,356,0,1,1 +go,episodes-create-help,43,5825.223,2418.000,2418.000,13288,704,0,44,44 +go,episodes-create-help,44,4768.487,2332.000,2332.000,13304,703,0,35,29 +rust,episodes-create-help,44,2127.327,0.000,2068.000,6884,350,0,1,1 +rust,episodes-create-help,45,2069.056,0.000,1994.000,6760,351,0,1,1 +go,episodes-create-help,45,5276.993,4763.000,0.000,13276,693,0,30,24 +go,episodes-create-help,46,5341.324,2838.000,1892.000,13288,688,0,15,10 +rust,episodes-create-help,46,2194.012,712.000,1424.000,6800,352,0,1,1 +rust,episodes-create-help,47,2519.624,0.000,2057.000,6848,352,0,1,2 +go,episodes-create-help,47,4648.363,2722.000,1814.000,13308,705,0,40,35 +go,episodes-create-help,48,4852.296,1767.000,2651.000,13276,692,0,43,35 +rust,episodes-create-help,48,2901.581,1175.000,1175.000,6792,352,0,1,2 +rust,episodes-create-help,49,1887.897,0.000,1817.000,6856,352,0,1,1 +go,episodes-create-help,49,5018.708,1207.000,3623.000,13292,705,0,46,38 +go,episodes-create-help,50,5886.318,2125.000,3188.000,13308,690,0,20,17 +rust,episodes-create-help,50,2644.769,0.000,2176.000,6684,351,0,1,2 +rust,episodes-create-help,51,2366.485,1139.000,1139.000,6724,353,0,1,2 +go,episodes-create-help,51,4701.532,3438.000,1146.000,13216,687,0,17,13 +go,episodes-create-help,52,4194.530,3081.000,1027.000,13304,709,0,20,12 +rust,episodes-create-help,52,2669.575,740.000,1481.000,6876,352,0,1,2 +rust,episodes-create-help,53,1945.615,0.000,1862.000,6804,351,0,1,2 +go,episodes-create-help,53,4953.295,3806.000,951.000,13276,692,0,21,13 +go,episodes-create-help,54,5363.806,3167.000,2111.000,13288,693,0,48,41 +rust,episodes-create-help,54,2849.062,0.000,2354.000,6780,350,0,1,2 +rust,episodes-create-help,55,1837.432,0.000,1748.000,6756,350,0,1,3 +go,episodes-create-help,55,4995.725,3914.000,978.000,13300,687,0,46,43 +go,episodes-create-help,56,5021.093,2675.000,1783.000,13304,707,0,41,35 +rust,episodes-create-help,56,1867.479,902.000,902.000,6880,353,0,1,2 +rust,episodes-create-help,57,1810.651,872.000,872.000,6876,351,0,1,1 +go,episodes-create-help,57,4841.626,2612.000,1741.000,13308,703,0,41,36 +go,episodes-create-help,58,4520.253,0.000,4446.000,13312,709,0,42,35 +rust,episodes-create-help,58,1956.685,1893.000,0.000,6840,350,0,1,1 +rust,episodes-create-help,59,2359.093,631.000,1263.000,6640,350,0,1,2 +go,episodes-create-help,59,4284.679,3142.000,1047.000,13344,694,0,41,36 +go,episodes-create-help,60,5257.047,958.000,3835.000,13304,698,0,44,39 +rust,episodes-create-help,60,1990.320,0.000,1884.000,6668,354,0,1,2 +rust,episodes-create-help,61,1590.388,766.000,766.000,6768,351,0,1,1 +go,episodes-create-help,61,4480.989,1015.000,3045.000,13288,685,0,39,33 +go,episodes-create-help,62,4243.050,1037.000,3111.000,13292,682,0,39,33 +rust,episodes-create-help,62,2265.697,1721.000,0.000,6724,354,0,1,2 +rust,episodes-create-help,63,1725.041,0.000,1676.000,6684,351,0,1,1 +go,episodes-create-help,63,4630.128,2066.000,2066.000,13308,693,0,42,35 +go,episodes-create-help,64,4894.194,3502.000,875.000,13308,702,0,25,20 +rust,episodes-create-help,64,2117.058,0.000,2052.000,6900,353,0,1,2 +rust,episodes-create-help,65,2522.468,0.000,2055.000,6696,350,0,1,2 +go,episodes-create-help,65,4833.481,2352.000,2352.000,13276,700,0,17,13 +go,episodes-create-help,66,4377.444,1073.000,3219.000,13268,689,0,15,11 +rust,episodes-create-help,66,1863.753,1793.000,0.000,6848,351,0,1,2 +rust,episodes-create-help,67,1553.818,1500.000,0.000,6664,350,0,1,1 +go,episodes-create-help,67,4408.081,2376.000,1584.000,13304,688,0,12,12 +go,episodes-create-help,68,4527.033,2060.000,2060.000,13292,693,0,35,31 +rust,episodes-create-help,68,2109.623,0.000,2049.000,6792,351,0,1,1 +rust,episodes-create-help,69,1856.328,0.000,1779.000,6824,351,0,1,1 +go,episodes-create-help,69,4449.268,1718.000,2577.000,13312,700,0,40,38 +go,episodes-create-help,70,4083.131,1001.000,3005.000,13212,692,0,40,35 +rust,episodes-create-help,70,1787.980,871.000,871.000,6716,354,0,1,1 +rust,episodes-create-help,71,1306.824,0.000,1269.000,6876,352,0,1,1 +go,episodes-create-help,71,4239.245,0.000,3829.000,13268,693,0,30,27 +go,episodes-create-help,72,4166.006,2030.000,2030.000,13344,684,0,41,36 +rust,episodes-create-help,72,2193.001,586.000,1172.000,6624,354,0,1,2 +rust,episodes-create-help,73,1449.954,0.000,1404.000,6876,352,0,1,1 +go,episodes-create-help,73,4146.139,2825.000,941.000,13348,703,0,40,33 +go,episodes-create-help,74,4745.336,1032.000,3098.000,13276,693,0,41,34 +rust,episodes-create-help,74,1909.457,1842.000,0.000,6712,352,0,1,2 +rust,episodes-create-help,75,2033.500,0.000,1960.000,6804,351,0,1,1 +go,episodes-create-help,75,5359.980,1976.000,2964.000,13348,710,0,46,43 +go,episodes-create-help,76,5378.705,809.000,4045.000,13304,712,0,28,26 +rust,episodes-create-help,76,2426.659,2024.000,0.000,6888,350,0,1,2 +rust,episodes-create-help,77,1638.229,788.000,788.000,6840,350,0,1,2 +go,episodes-create-help,77,4181.275,2054.000,2054.000,13312,695,0,41,33 +go,episodes-create-help,78,5590.804,1685.000,3371.000,13312,701,0,41,36 +rust,episodes-create-help,78,2334.535,0.000,2266.000,6884,350,0,1,1 +rust,episodes-create-help,79,1984.448,0.000,1917.000,6576,353,0,1,1 +go,episodes-create-help,79,5100.391,3685.000,921.000,13300,702,0,27,24 +go,episodes-create-help,80,4961.692,1782.000,2673.000,13308,708,0,21,18 +rust,episodes-create-help,80,2147.405,0.000,2083.000,6408,357,0,1,2 +rust,episodes-create-help,81,2246.211,1806.000,0.000,6756,349,0,1,2 +go,episodes-create-help,81,5194.890,1014.000,4056.000,13292,700,0,46,38 +go,episodes-create-help,82,4493.361,2031.000,2031.000,13344,694,0,34,30 +rust,episodes-create-help,82,2101.668,1020.000,1020.000,6788,349,0,1,1 +rust,episodes-create-help,83,1957.837,944.000,944.000,6744,351,0,1,1 +go,episodes-create-help,83,4896.950,2631.000,1754.000,13308,693,0,43,35 +go,episodes-create-help,84,4726.420,2562.000,1708.000,13308,693,0,21,19 +rust,episodes-create-help,84,1777.038,862.000,862.000,6632,351,0,1,2 +rust,episodes-create-help,85,1875.974,751.000,751.000,6828,351,0,1,2 +go,episodes-create-help,85,3784.920,2473.000,1236.000,13220,707,0,40,35 +go,episodes-create-help,86,4568.653,826.000,3304.000,13292,702,0,40,31 +rust,episodes-create-help,86,1904.358,0.000,1840.000,6632,353,0,1,2 +rust,episodes-create-help,87,1405.951,0.000,1360.000,6668,349,0,1,1 +go,episodes-create-help,87,3926.166,1917.000,1917.000,13292,706,0,14,8 +go,episodes-create-help,88,5513.959,1546.000,3092.000,13308,699,0,44,39 +rust,episodes-create-help,88,1954.502,1896.000,0.000,6624,354,0,1,2 +rust,episodes-create-help,89,1804.431,0.000,1747.000,6868,353,0,1,1 +go,episodes-create-help,89,4747.158,1060.000,3181.000,13292,706,0,28,23 +go,episodes-create-help,90,4147.842,1016.000,3049.000,13288,701,0,41,33 +rust,episodes-create-help,90,2125.303,0.000,2058.000,6768,353,0,1,1 +rust,episodes-create-help,91,1433.082,1388.000,0.000,6908,352,0,1,1 +go,episodes-create-help,91,4888.794,1758.000,2637.000,13300,704,0,45,40 +go,episodes-create-help,92,4228.433,2868.000,956.000,13300,693,0,37,30 +rust,episodes-create-help,92,1810.722,878.000,878.000,6792,351,0,1,1 +rust,episodes-create-help,93,1919.194,929.000,929.000,6712,352,0,1,1 +go,episodes-create-help,93,4710.970,0.000,3968.000,13300,701,0,30,22 +go,episodes-create-help,94,3911.517,1914.000,1914.000,13292,692,0,38,34 +rust,episodes-create-help,94,2122.858,865.000,865.000,6408,356,0,1,2 +rust,episodes-create-help,95,1523.943,0.000,1469.000,6600,350,0,1,2 +go,episodes-create-help,95,4495.896,816.000,3267.000,13308,703,0,41,35 +go,episodes-create-help,96,5181.895,1157.000,3473.000,13300,703,0,16,12 +rust,episodes-create-help,96,1740.680,1683.000,0.000,6784,351,0,1,2 +rust,episodes-create-help,97,1720.623,0.000,1667.000,6888,351,0,1,1 +go,episodes-create-help,97,4328.281,2882.000,960.000,13300,683,0,14,8 +go,episodes-create-help,98,4273.157,0.000,3902.000,13288,692,0,13,8 +rust,episodes-create-help,98,1959.832,950.000,950.000,6728,351,0,1,2 +rust,episodes-create-help,99,1533.612,0.000,1489.000,6792,351,0,1,1 +go,episodes-create-help,99,4505.534,1106.000,3320.000,13276,692,0,45,41 +go,episodes-create-help,100,5298.413,1937.000,2906.000,13308,707,0,43,39 +rust,episodes-create-help,100,1727.987,837.000,837.000,6820,350,0,1,2 +rust,episodes-create-help,101,1567.105,757.000,757.000,6740,351,0,1,1 +go,episodes-create-help,101,4312.341,2108.000,2108.000,13276,690,0,16,13 +go,episodes-create-help,102,4042.515,2971.000,990.000,13304,691,0,14,7 +rust,episodes-create-help,102,2346.939,1131.000,1131.000,6708,350,0,1,2 +rust,episodes-create-help,103,1614.312,0.000,1543.000,6664,355,0,1,1 +go,episodes-create-help,103,4123.648,934.000,2804.000,13344,694,0,21,16 +go,episodes-create-help,104,5568.892,2471.000,2471.000,13212,689,0,39,34 +rust,episodes-create-help,104,2163.344,0.000,2102.000,6872,350,0,1,1 +rust,episodes-create-help,105,1653.326,799.000,799.000,6644,350,0,1,1 +go,episodes-create-help,105,3872.867,0.000,3766.000,13312,685,0,40,35 +go,episodes-create-help,106,4735.776,3238.000,1079.000,13308,689,0,38,33 +rust,episodes-create-help,106,2236.682,0.000,1752.000,6788,352,0,1,1 +rust,episodes-create-help,107,1603.682,1552.000,0.000,6728,351,0,1,1 +go,episodes-create-help,107,3989.755,2928.000,976.000,13344,700,0,16,13 +go,episodes-create-help,108,4033.168,2965.000,988.000,13308,688,0,16,13 +rust,episodes-create-help,108,2114.983,883.000,883.000,6756,350,0,1,2 +rust,episodes-create-help,109,1681.028,0.000,1621.000,6580,351,0,1,1 +go,episodes-create-help,109,5254.682,2873.000,1915.000,13304,699,0,44,36 +go,episodes-create-help,110,4356.975,841.000,3364.000,13344,698,0,39,30 +rust,episodes-create-help,110,1934.354,0.000,1885.000,6660,350,0,1,1 +rust,episodes-create-help,111,1825.149,1760.000,0.000,6584,351,0,1,2 +go,episodes-create-help,111,5033.596,1977.000,2966.000,13308,702,0,46,37 +go,episodes-create-help,112,4639.737,1821.000,2732.000,13212,685,0,14,8 +rust,episodes-create-help,112,2323.786,956.000,956.000,6764,350,0,1,2 +rust,episodes-create-help,113,1781.316,858.000,858.000,6492,357,0,1,1 +go,episodes-create-help,113,4241.930,1037.000,3111.000,13304,687,0,15,11 +go,episodes-create-help,114,4899.135,3848.000,962.000,13304,696,0,43,37 +rust,episodes-create-help,114,2283.149,1107.000,1107.000,6492,357,0,1,1 +rust,episodes-create-help,115,2282.548,1857.000,0.000,6964,353,0,1,2 +go,episodes-create-help,115,4059.156,2976.000,992.000,13292,689,0,15,11 +go,episodes-create-help,116,3999.454,3915.000,0.000,13308,687,0,39,37 +rust,episodes-create-help,116,1868.181,1801.000,0.000,6916,350,0,1,1 +rust,episodes-create-help,117,2260.316,0.000,1818.000,6864,351,0,1,2 +go,episodes-create-help,117,4614.317,2254.000,2254.000,13312,705,0,45,40 +go,episodes-create-help,118,4580.334,2683.000,1789.000,13276,692,0,19,12 +rust,episodes-create-help,118,2119.613,1023.000,1023.000,6676,350,0,1,1 +rust,episodes-create-help,119,1723.197,0.000,1668.000,6876,353,0,1,1 +go,episodes-create-help,119,4476.639,1014.000,3043.000,13348,686,0,16,13 +go,episodes-create-help,120,4145.759,4053.000,0.000,13304,696,0,14,8 +rust,episodes-create-help,120,2332.432,1245.000,622.000,6736,352,0,1,2 +rust,episodes-create-help,121,1541.485,0.000,1492.000,6876,352,0,1,1 +go,episodes-create-help,121,4793.114,3413.000,853.000,13308,688,0,20,13 +go,episodes-create-help,122,4626.242,908.000,3633.000,13344,689,0,16,12 +rust,episodes-create-help,122,2108.501,1022.000,1022.000,6880,353,0,1,1 +rust,episodes-create-help,123,1994.367,0.000,1625.000,6792,351,0,1,2 +go,episodes-create-help,123,3858.709,1891.000,1891.000,13348,694,0,38,35 +go,episodes-create-help,124,4125.181,1833.000,1833.000,13308,698,0,18,16 +rust,episodes-create-help,124,2183.742,0.000,2108.000,6872,352,0,1,2 +rust,episodes-create-help,125,1972.055,0.000,1909.000,6852,353,0,1,1 +go,episodes-create-help,125,4163.742,3057.000,1019.000,13276,692,0,29,27 +go,episodes-create-help,126,4305.919,3153.000,1051.000,13308,696,0,40,33 +rust,episodes-create-help,126,2039.561,0.000,1977.000,6740,351,0,1,1 +rust,episodes-create-help,127,1621.336,0.000,1563.000,6672,349,0,1,1 +go,episodes-create-help,127,4837.478,1746.000,2619.000,13312,692,0,37,30 +go,episodes-create-help,128,4510.182,1637.000,2455.000,13312,692,0,40,36 +rust,episodes-create-help,128,2145.491,0.000,2067.000,6740,351,0,1,2 +rust,episodes-create-help,129,1826.712,0.000,1760.000,6848,350,0,1,1 +go,episodes-create-help,129,4482.081,1011.000,3033.000,13292,692,0,38,35 +go,episodes-create-help,130,3874.028,951.000,2855.000,13288,702,0,19,12 +rust,episodes-create-help,130,2379.670,1972.000,0.000,6988,353,0,1,2 +rust,episodes-create-help,131,1858.011,0.000,1795.000,6684,355,0,1,2 +go,episodes-create-help,131,4519.241,2458.000,1638.000,13344,686,0,40,34 +go,episodes-create-help,132,4702.915,1061.000,3184.000,13308,703,0,37,32 +rust,episodes-create-help,132,2012.741,0.000,1957.000,6504,356,0,1,1 +rust,episodes-create-help,133,1759.526,1687.000,0.000,6680,353,0,1,1 +go,episodes-create-help,133,5369.106,2225.000,2225.000,13212,693,0,24,19 +go,episodes-create-help,134,5686.342,3067.000,2045.000,13308,685,0,38,33 +rust,episodes-create-help,134,2761.259,756.000,1512.000,6652,351,0,1,2 +rust,episodes-create-help,135,1993.576,961.000,961.000,6864,352,0,1,2 +go,episodes-create-help,135,5026.613,1811.000,2717.000,13276,689,0,41,36 +go,episodes-create-help,136,4482.652,2610.000,1740.000,13276,692,0,42,35 +rust,episodes-create-help,136,1921.589,0.000,1872.000,6744,351,0,1,1 +rust,episodes-create-help,137,2224.620,874.000,874.000,6792,352,0,1,2 +go,episodes-create-help,137,3977.172,1950.000,1950.000,13148,693,0,14,11 +go,episodes-create-help,138,4733.693,2098.000,2098.000,13300,692,0,26,22 +rust,episodes-create-help,138,1843.634,1792.000,0.000,6668,352,0,1,1 +rust,episodes-create-help,139,1736.402,0.000,1667.000,6892,352,0,1,1 +go,episodes-create-help,139,4096.456,1850.000,1850.000,13308,689,0,34,28 +go,episodes-create-help,140,3703.016,2728.000,909.000,13288,692,0,39,33 +rust,episodes-create-help,140,1809.088,877.000,877.000,6476,358,0,1,2 +rust,episodes-create-help,141,1219.822,1184.000,0.000,6688,351,0,1,1 +go,episodes-create-help,141,3436.606,0.000,3385.000,13172,679,0,30,24 +go,episodes-create-help,142,4179.703,2232.000,1488.000,13300,704,0,39,28 +rust,episodes-create-help,142,1666.561,1614.000,0.000,6816,351,0,1,2 +rust,episodes-create-help,143,1565.731,752.000,752.000,6692,350,0,1,2 +go,episodes-create-help,143,4038.046,1985.000,1985.000,13292,698,0,41,34 +go,episodes-create-help,144,5629.715,3393.000,1696.000,13312,702,0,47,42 +rust,episodes-create-help,144,2346.379,0.000,2281.000,6708,351,0,1,1 +rust,episodes-create-help,145,1997.052,956.000,956.000,6808,350,0,1,2 +go,episodes-create-help,145,4304.617,1939.000,1939.000,13308,689,0,39,31 +go,episodes-create-help,146,4582.338,1629.000,2444.000,13344,708,0,41,35 +rust,episodes-create-help,146,2032.087,968.000,968.000,6644,350,0,1,2 +rust,episodes-create-help,147,1742.783,835.000,835.000,6792,351,0,1,2 +go,episodes-create-help,147,4340.655,979.000,2938.000,13276,703,0,41,36 +go,episodes-create-help,148,4793.686,1716.000,2574.000,13276,692,0,42,38 +rust,episodes-create-help,148,2021.588,983.000,983.000,6756,349,0,1,1 +rust,episodes-create-help,149,1840.377,0.000,1767.000,6676,351,0,1,1 +go,episodes-create-help,149,4643.423,1045.000,3137.000,13292,709,0,40,32 +go,episodes-create-help,150,4970.690,974.000,3896.000,13304,700,0,43,38 +rust,episodes-create-help,150,2234.487,604.000,1209.000,6740,351,0,1,2 +rust,episodes-create-help,151,1507.843,1460.000,0.000,6952,351,0,1,1 +go,episodes-create-help,151,4372.514,1981.000,1981.000,13308,687,0,40,34 +go,episodes-create-help,152,4889.286,3267.000,1089.000,13288,690,0,41,35 +rust,episodes-create-help,152,1880.181,913.000,913.000,6876,353,0,1,2 +rust,episodes-create-help,153,2084.856,814.000,814.000,6728,352,0,1,2 +go,episodes-create-help,153,4389.387,1716.000,2574.000,13332,703,0,40,35 +go,episodes-create-help,154,4773.758,2597.000,1731.000,13212,705,0,41,35 +rust,episodes-create-help,154,1847.571,0.000,1791.000,6792,351,0,1,1 +rust,episodes-create-help,155,1944.272,0.000,1563.000,6712,350,0,1,1 +go,episodes-create-help,155,4131.503,1006.000,3018.000,13308,702,0,38,33 +go,episodes-create-help,156,4449.439,2175.000,2175.000,13312,693,0,44,39 +rust,episodes-create-help,156,2127.015,0.000,2060.000,6860,353,0,1,1 +rust,episodes-create-help,157,2129.902,857.000,857.000,6492,357,0,1,2 +go,episodes-create-help,157,4350.804,1581.000,2372.000,13288,693,0,39,33 +go,episodes-create-help,158,4364.169,2144.000,2144.000,13348,694,0,43,38 +rust,episodes-create-help,158,1949.222,0.000,1894.000,6660,351,0,1,1 +rust,episodes-create-help,159,1567.244,757.000,757.000,6788,353,0,1,1 +go,episodes-create-help,159,4361.945,982.000,2946.000,13292,688,0,40,34 +go,episodes-create-help,160,4190.403,2053.000,2053.000,13276,700,0,15,11 +rust,episodes-create-help,160,2285.895,0.000,1894.000,6748,350,0,1,2 +rust,episodes-create-help,161,1746.441,844.000,844.000,6684,353,0,1,2 +go,episodes-create-help,161,4293.626,3154.000,1051.000,13288,689,0,35,34 +go,episodes-create-help,162,4454.940,1012.000,3037.000,13344,693,0,27,24 +rust,episodes-create-help,162,2696.926,730.000,1461.000,6752,350,0,1,3 +rust,episodes-create-help,163,1611.939,1544.000,0.000,6684,355,0,1,2 +go,episodes-create-help,163,4044.569,1838.000,1838.000,13344,698,0,38,35 +go,episodes-create-help,164,3716.322,2738.000,912.000,13300,691,0,38,34 +rust,episodes-create-help,164,1538.330,0.000,1500.000,6884,351,0,1,1 +rust,episodes-create-help,165,1290.846,0.000,1240.000,6600,354,0,1,1 +go,episodes-create-help,165,4583.090,3160.000,1053.000,13344,710,0,44,38 +go,episodes-create-help,166,4598.911,0.000,4248.000,13176,693,0,15,12 +rust,episodes-create-help,166,1612.138,782.000,782.000,6476,359,0,1,2 +rust,episodes-create-help,167,1352.421,1311.000,0.000,6800,352,0,1,1 +go,episodes-create-help,167,3981.150,0.000,3619.000,13308,693,0,40,34 +go,episodes-create-help,168,4116.283,0.000,4031.000,13300,688,0,16,11 +rust,episodes-create-help,168,2243.054,0.000,1793.000,6828,351,0,1,1 +rust,episodes-create-help,169,1552.476,0.000,1502.000,6748,349,0,1,1 +go,episodes-create-help,169,4929.320,2894.000,1929.000,13276,696,0,48,41 +go,episodes-create-help,170,4386.110,1988.000,1988.000,13304,687,0,39,35 +rust,episodes-create-help,170,1850.626,0.000,1797.000,6740,351,0,1,2 +rust,episodes-create-help,171,1654.619,0.000,1577.000,6792,351,0,1,2 +go,episodes-create-help,171,4400.247,0.000,4321.000,13164,697,0,16,12 +go,episodes-create-help,172,4283.126,1546.000,2319.000,13276,704,0,38,34 +rust,episodes-create-help,172,1790.864,1736.000,0.000,6792,352,0,1,2 +rust,episodes-create-help,173,1810.431,0.000,1436.000,6872,353,0,1,2 +go,episodes-create-help,173,3754.413,3691.000,0.000,13276,714,0,40,35 +go,episodes-create-help,174,3954.820,971.000,2913.000,13348,711,0,42,35 +rust,episodes-create-help,174,1787.598,872.000,872.000,6864,352,0,1,1 +rust,episodes-create-help,175,1655.590,0.000,1604.000,6908,351,0,1,1 +go,episodes-create-help,175,4986.818,2686.000,1790.000,13304,706,0,39,32 +go,episodes-create-help,176,4321.319,1565.000,2347.000,13312,687,0,23,18 +rust,episodes-create-help,176,1664.988,0.000,1619.000,6836,350,0,1,2 +rust,episodes-create-help,177,1395.121,0.000,1354.000,6684,351,0,1,1 +go,episodes-create-help,177,4409.884,988.000,2965.000,13288,692,0,41,35 +go,episodes-create-help,178,4291.322,841.000,3364.000,13276,708,0,24,20 +rust,episodes-create-help,178,1903.326,1850.000,0.000,6740,351,0,1,1 +rust,episodes-create-help,179,2139.459,0.000,1476.000,6592,350,0,1,2 +go,episodes-create-help,179,4372.264,1070.000,3210.000,13344,702,0,43,39 +go,episodes-create-help,180,4251.627,2096.000,2096.000,13312,687,0,38,31 +rust,episodes-create-help,180,1858.110,0.000,1808.000,6668,350,0,1,1 +rust,episodes-create-help,181,1790.674,692.000,692.000,6692,351,0,1,2 +go,episodes-create-help,181,4515.934,2032.000,2032.000,13312,694,0,43,36 +go,episodes-create-help,182,3882.454,3817.000,0.000,13288,694,0,39,34 +rust,episodes-create-help,182,1693.089,0.000,1651.000,6792,352,0,1,1 +rust,episodes-create-help,183,1799.972,0.000,1734.000,6756,351,0,1,1 +go,episodes-create-help,183,4303.615,2728.000,909.000,13348,702,0,24,19 +go,episodes-create-help,184,3839.773,1885.000,1885.000,13276,711,0,34,32 +rust,episodes-create-help,184,1606.518,0.000,1571.000,6828,352,0,1,1 +rust,episodes-create-help,185,1648.637,0.000,1317.000,6752,352,0,1,2 +go,episodes-create-help,185,4348.670,784.000,3138.000,13212,703,0,41,37 +go,episodes-create-help,186,4141.531,2005.000,2005.000,13212,703,0,42,37 +rust,episodes-create-help,186,1921.860,935.000,935.000,6748,351,0,1,1 +rust,episodes-create-help,187,1913.706,772.000,772.000,6772,351,0,1,2 +go,episodes-create-help,187,4004.392,3595.000,0.000,13344,693,0,38,31 +go,episodes-create-help,188,4319.846,1940.000,1940.000,13288,707,0,39,35 +rust,episodes-create-help,188,1716.745,834.000,834.000,6756,350,0,1,2 +rust,episodes-create-help,189,1457.037,0.000,1416.000,6780,351,0,1,1 +go,episodes-create-help,189,4112.296,934.000,2803.000,13276,693,0,40,36 +go,episodes-create-help,190,4023.108,897.000,2693.000,13292,701,0,15,11 +rust,episodes-create-help,190,1641.774,795.000,795.000,6856,351,0,1,2 +rust,episodes-create-help,191,1586.330,764.000,764.000,6804,352,0,1,2 +go,episodes-create-help,191,3763.871,0.000,3700.000,13276,698,0,15,11 +go,episodes-create-help,192,4747.329,2419.000,1613.000,13292,703,0,39,31 +rust,episodes-create-help,192,1692.329,0.000,1645.000,6936,352,0,1,2 +rust,episodes-create-help,193,1404.068,0.000,1365.000,6748,351,0,1,1 +go,episodes-create-help,193,3841.528,1883.000,1883.000,13308,686,0,38,33 +go,episodes-create-help,194,5130.367,2014.000,3021.000,13300,699,0,45,38 +rust,episodes-create-help,194,2258.824,0.000,2172.000,6760,351,0,1,1 +rust,episodes-create-help,195,2416.791,0.000,1949.000,6836,353,0,1,2 +go,episodes-create-help,195,4574.623,3580.000,895.000,13292,692,0,34,30 +go,episodes-create-help,196,5369.869,976.000,3906.000,13276,704,0,43,39 +rust,episodes-create-help,196,2592.610,0.000,2144.000,6784,353,0,1,2 +rust,episodes-create-help,197,1770.276,0.000,1704.000,6668,355,0,1,2 +go,episodes-create-help,197,4280.731,837.000,3349.000,13292,701,0,43,38 +go,episodes-create-help,198,4359.079,2136.000,2136.000,13292,700,0,42,37 +rust,episodes-create-help,198,2506.779,0.000,2062.000,6744,352,0,1,2 +rust,episodes-create-help,199,2417.912,0.000,1896.000,6728,351,0,1,1 +go,episodes-create-help,199,4869.579,1894.000,2841.000,13292,709,0,27,20 +go,episodes-create-help,200,5946.442,1799.000,3599.000,13344,708,0,43,41 +rust,episodes-create-help,200,2431.608,974.000,974.000,6780,351,0,1,2 +rust,episodes-create-help,201,1861.969,0.000,1791.000,6824,351,0,1,2 +go,episodes-create-help,201,4209.728,2062.000,2062.000,13308,702,0,42,38 +go,episodes-create-help,202,4780.041,939.000,3757.000,13292,693,0,42,38 +rust,episodes-create-help,202,2523.761,683.000,1367.000,6848,353,0,1,2 +rust,episodes-create-help,203,1880.301,0.000,1812.000,6820,352,0,1,2 +go,episodes-create-help,203,4742.039,3491.000,1163.000,13292,701,0,17,12 +go,episodes-create-help,204,4979.844,2699.000,1799.000,13308,706,0,43,40 +rust,episodes-create-help,204,1859.002,0.000,1809.000,6732,350,0,1,1 +rust,episodes-create-help,205,1577.574,764.000,764.000,6684,352,0,1,1 +go,episodes-create-help,205,5939.017,1704.000,3409.000,13292,696,0,41,38 +go,episodes-create-help,206,4619.609,2269.000,2269.000,13308,701,0,42,38 +rust,episodes-create-help,206,2786.224,0.000,2333.000,6824,351,0,1,2 +rust,episodes-create-help,207,2248.615,0.000,2167.000,6648,350,0,1,2 +go,episodes-create-help,207,4448.447,1738.000,2607.000,13300,683,0,15,11 +go,episodes-create-help,208,4323.412,2119.000,2119.000,13312,692,0,40,38 +rust,episodes-create-help,208,2116.086,0.000,2034.000,6680,355,0,1,1 +rust,episodes-create-help,209,1566.883,0.000,1515.000,6912,353,0,1,1 +go,episodes-create-help,209,5838.708,3506.000,1753.000,13160,693,0,41,37 +go,episodes-create-help,210,4335.345,3186.000,1062.000,13292,700,0,18,11 +rust,episodes-create-help,210,2050.272,0.000,1991.000,6792,350,0,1,1 +rust,episodes-create-help,211,2000.317,814.000,814.000,6672,350,0,1,2 +go,episodes-create-help,211,4238.093,3110.000,1036.000,13212,688,0,32,31 +go,episodes-create-help,212,4650.507,0.000,4554.000,13292,707,0,42,38 +rust,episodes-create-help,212,2036.886,991.000,991.000,6876,353,0,1,1 +rust,episodes-create-help,213,1587.503,0.000,1534.000,6824,352,0,1,1 +go,episodes-create-help,213,4360.000,986.000,2959.000,13344,696,0,37,33 +go,episodes-create-help,214,4467.984,3490.000,872.000,13236,694,0,42,36 +rust,episodes-create-help,214,2308.617,0.000,1885.000,6660,354,0,1,2 +rust,episodes-create-help,215,1624.712,0.000,1570.000,6756,349,0,1,2 +go,episodes-create-help,215,5323.931,962.000,3848.000,13148,682,0,18,16 +go,episodes-create-help,216,5067.570,1827.000,2740.000,13344,688,0,20,17 +rust,episodes-create-help,216,2148.686,1040.000,1040.000,6764,350,0,1,2 +rust,episodes-create-help,217,1653.827,1599.000,0.000,6732,351,0,1,1 +go,episodes-create-help,217,4592.548,2083.000,2083.000,13244,704,0,32,30 +go,episodes-create-help,218,4347.097,828.000,3315.000,13276,701,0,40,35 +rust,episodes-create-help,218,2146.743,1045.000,1045.000,6712,353,0,1,1 +rust,episodes-create-help,219,1423.674,0.000,1376.000,6852,352,0,1,1 +go,episodes-create-help,219,3795.702,930.000,2792.000,13304,688,0,16,11 +go,episodes-create-help,220,5211.500,3550.000,887.000,13276,692,0,42,39 +rust,episodes-create-help,220,2007.351,968.000,968.000,6824,352,0,1,2 +rust,episodes-create-help,221,2245.649,0.000,1766.000,6756,354,0,1,2 +go,episodes-create-help,221,5211.110,3025.000,2016.000,13268,688,0,47,42 +go,episodes-create-help,222,4828.952,2848.000,1898.000,13292,706,0,42,36 +rust,episodes-create-help,222,2618.109,0.000,2175.000,6656,350,0,1,2 +rust,episodes-create-help,223,1916.831,0.000,1849.000,6752,350,0,1,2 +go,episodes-create-help,223,5027.885,1970.000,2956.000,13308,694,0,46,38 +go,episodes-create-help,224,5388.764,1605.000,3211.000,13208,683,0,38,35 +rust,episodes-create-help,224,2107.639,1020.000,1020.000,6668,352,0,1,2 +rust,episodes-create-help,225,2185.005,0.000,1743.000,6888,352,0,1,2 +go,episodes-create-help,225,4763.799,1867.000,2801.000,13304,687,0,16,11 +go,episodes-create-help,226,4976.368,2724.000,1816.000,13292,684,0,46,38 +rust,episodes-create-help,226,2087.933,1015.000,1015.000,6800,352,0,1,1 +rust,episodes-create-help,227,1601.308,0.000,1554.000,6792,349,0,0,2 +go,episodes-create-help,227,4255.193,3128.000,1042.000,13308,693,0,41,35 +go,episodes-create-help,228,4467.103,2186.000,2186.000,13304,691,0,22,15 +rust,episodes-create-help,228,2405.108,0.000,2337.000,6736,351,0,1,1 +rust,episodes-create-help,229,1952.318,0.000,1884.000,6816,350,0,1,1 +go,episodes-create-help,229,5002.277,2696.000,1797.000,13304,685,0,13,8 +go,episodes-create-help,230,5184.219,3808.000,952.000,13240,703,0,44,37 +rust,episodes-create-help,230,1966.745,0.000,1911.000,6720,350,0,1,2 +rust,episodes-create-help,231,1838.504,716.000,716.000,6856,353,0,1,2 +go,episodes-create-help,231,3640.750,1195.000,2391.000,13348,694,0,39,31 +go,episodes-create-help,232,3693.810,893.000,2679.000,13308,693,0,40,31 +rust,episodes-create-help,232,1811.152,0.000,1764.000,6648,350,0,1,1 +rust,episodes-create-help,233,1329.188,0.000,1287.000,6720,352,0,1,1 +go,episodes-create-help,233,3987.310,904.000,2713.000,13348,695,0,36,29 +go,episodes-create-help,234,3209.058,2109.000,1054.000,13304,704,0,24,17 +rust,episodes-create-help,234,1886.614,0.000,1536.000,6868,353,0,1,2 +rust,episodes-create-help,235,1254.877,0.000,1215.000,6784,351,0,1,2 +go,episodes-create-help,235,3550.491,3494.000,0.000,13348,690,0,39,30 +go,episodes-create-help,236,4182.657,1898.000,1898.000,13308,701,0,26,24 +rust,episodes-create-help,236,1703.841,0.000,1650.000,6732,351,0,1,1 +rust,episodes-create-help,237,1733.346,0.000,1393.000,6672,350,0,1,2 +go,episodes-create-help,237,4043.166,1839.000,1839.000,13260,693,0,42,36 +go,episodes-create-help,238,3996.408,2939.000,979.000,13304,704,0,40,33 +rust,episodes-create-help,238,1749.837,0.000,1708.000,6556,352,0,1,1 +rust,episodes-create-help,239,1331.712,0.000,1291.000,6824,352,0,1,1 +go,episodes-create-help,239,4306.120,2888.000,962.000,13292,690,0,37,35 +go,episodes-create-help,240,4201.201,937.000,2813.000,13348,712,0,37,29 +rust,episodes-create-help,240,2169.206,867.000,867.000,6652,352,0,1,2 +rust,episodes-create-help,241,1311.604,1273.000,0.000,6824,352,0,1,1 +go,episodes-create-help,241,4008.671,1969.000,1969.000,13308,694,0,41,37 +go,episodes-create-help,242,4019.020,0.000,3593.000,13344,690,0,15,13 +rust,episodes-create-help,242,2132.526,868.000,868.000,6884,350,0,1,2 +rust,episodes-create-help,243,1490.321,721.000,721.000,6660,356,0,1,2 +go,episodes-create-help,243,4159.203,932.000,2796.000,13276,683,0,34,31 +go,episodes-create-help,244,3468.736,2272.000,1136.000,13276,704,0,13,10 +rust,episodes-create-help,244,1848.001,768.000,768.000,6664,351,0,1,2 +rust,episodes-create-help,245,1283.131,1247.000,0.000,6812,352,0,1,1 +go,episodes-create-help,245,3703.968,0.000,3370.000,13308,693,0,28,22 +go,episodes-create-help,246,3267.959,2140.000,1070.000,13300,690,0,24,18 +rust,episodes-create-help,246,1656.081,0.000,1616.000,6792,351,0,1,1 +rust,episodes-create-help,247,1342.261,1301.000,0.000,6692,350,0,1,1 +go,episodes-create-help,247,3885.230,880.000,2640.000,13312,689,0,16,11 +go,episodes-create-help,248,3425.265,840.000,2520.000,13300,687,0,35,27 +rust,episodes-create-help,248,1661.089,0.000,1622.000,6880,351,0,1,1 +rust,episodes-create-help,249,1362.992,0.000,1325.000,6832,350,0,1,1 +go,episodes-create-help,249,4141.390,886.000,2658.000,13300,693,0,39,33 +go,episodes-create-help,250,3452.666,2236.000,1118.000,13348,708,0,39,32 +rust,episodes-create-help,250,1537.999,0.000,1503.000,6700,351,0,1,1 +rust,episodes-create-help,251,1312.485,0.000,1273.000,6932,352,0,1,1 +go,episodes-create-help,251,4050.830,998.000,2994.000,13312,693,0,38,31 +go,episodes-create-help,252,3948.679,878.000,2635.000,13292,703,0,38,26 +rust,episodes-create-help,252,1901.172,885.000,885.000,6860,351,0,1,1 +rust,episodes-create-help,253,1620.163,786.000,786.000,6744,351,0,1,1 +go,episodes-create-help,253,5394.505,905.000,3622.000,13308,696,0,37,34 +go,episodes-create-help,254,4358.057,2349.000,1566.000,13308,708,0,20,15 +rust,episodes-create-help,254,1804.069,0.000,1745.000,6860,352,0,1,2 +rust,episodes-create-help,255,2188.512,0.000,1700.000,6832,351,0,1,2 +go,episodes-create-help,255,4850.163,3561.000,1187.000,13304,691,0,42,36 +go,episodes-create-help,256,4699.959,2093.000,2093.000,13276,688,0,40,35 +rust,episodes-create-help,256,2243.364,0.000,1884.000,6716,352,0,1,2 +rust,episodes-create-help,257,1518.472,731.000,731.000,6792,351,0,1,2 +go,episodes-create-help,257,4503.290,4409.000,0.000,13288,709,0,22,13 +go,episodes-create-help,258,5505.353,3016.000,2010.000,13216,681,0,16,14 +rust,episodes-create-help,258,2154.608,0.000,2091.000,6864,352,0,1,2 +rust,episodes-create-help,259,2601.037,1405.000,702.000,6768,352,0,1,2 +go,episodes-create-help,259,4934.720,1916.000,2875.000,13308,702,0,46,42 +go,episodes-create-help,260,5267.986,2845.000,1897.000,13264,705,0,43,34 +rust,episodes-create-help,260,2067.225,0.000,2014.000,6688,354,0,1,1 +rust,episodes-create-help,261,1716.144,833.000,833.000,6864,353,0,1,1 +go,episodes-create-help,261,4839.200,949.000,3796.000,13308,704,0,35,30 +go,episodes-create-help,262,4754.263,3488.000,1162.000,13148,679,0,16,11 +rust,episodes-create-help,262,2010.978,837.000,837.000,6656,351,0,1,2 +rust,episodes-create-help,263,1302.687,0.000,1261.000,6844,351,0,1,2 +go,episodes-create-help,263,3972.414,1817.000,1817.000,13212,704,0,15,14 +go,episodes-create-help,264,3699.320,1210.000,2420.000,13276,684,0,38,28 +rust,episodes-create-help,264,1633.439,0.000,1592.000,6812,349,0,1,1 +rust,episodes-create-help,265,1590.087,1268.000,0.000,6880,351,0,1,2 +go,episodes-create-help,265,3518.680,1151.000,2302.000,13344,693,0,38,30 +go,episodes-create-help,266,3699.721,1815.000,1815.000,13276,707,0,36,27 +rust,episodes-create-help,266,1582.323,0.000,1544.000,6728,350,0,1,1 +rust,episodes-create-help,267,1296.646,0.000,1258.000,6872,352,0,1,1 +go,episodes-create-help,267,3856.795,2845.000,948.000,13276,684,0,39,33 +go,episodes-create-help,268,3712.154,1118.000,2236.000,13292,694,0,36,27 +rust,episodes-create-help,268,1807.676,0.000,1493.000,6752,350,0,1,2 +rust,episodes-create-help,269,1211.947,0.000,1177.000,6860,351,0,1,1 +go,episodes-create-help,269,3553.666,1606.000,1606.000,13308,695,0,24,19 +go,episodes-create-help,270,3295.110,2162.000,1081.000,13308,691,0,36,31 +rust,episodes-create-help,270,1516.069,739.000,739.000,6664,350,0,1,1 +rust,episodes-create-help,271,1317.084,1268.000,0.000,6680,355,0,1,2 +go,episodes-create-help,271,3268.071,0.000,3219.000,13276,703,0,22,16 +go,episodes-create-help,272,3716.341,1677.000,1677.000,13300,691,0,33,26 +rust,episodes-create-help,272,1948.059,0.000,1571.000,6812,350,0,1,2 +rust,episodes-create-help,273,1204.012,0.000,1164.000,6720,350,0,1,2 +go,episodes-create-help,273,3309.569,2441.000,813.000,13268,703,0,16,10 +go,episodes-create-help,274,3943.679,1673.000,1673.000,13344,693,0,29,24 +rust,episodes-create-help,274,1510.869,1475.000,0.000,6744,352,0,1,1 +rust,episodes-create-help,275,1196.397,0.000,1161.000,6716,350,0,1,1 +go,episodes-create-help,275,4299.307,2189.000,1459.000,13300,693,0,39,32 +go,episodes-create-help,276,3556.762,2329.000,1164.000,13276,692,0,38,32 +rust,episodes-create-help,276,1577.144,0.000,1538.000,6736,351,0,1,1 +rust,episodes-create-help,277,1289.372,0.000,1248.000,6860,352,0,1,1 +go,episodes-create-help,277,3685.985,0.000,3630.000,13344,698,0,39,33 +go,episodes-create-help,278,4029.170,2746.000,915.000,13300,708,0,40,29 +rust,episodes-create-help,278,2267.740,0.000,1885.000,6876,350,0,1,2 +rust,episodes-create-help,279,1707.919,821.000,821.000,6804,351,0,1,2 +go,episodes-create-help,279,4335.616,978.000,2934.000,13276,705,0,14,9 +go,episodes-create-help,280,4867.704,2865.000,1910.000,13184,681,0,17,11 +rust,episodes-create-help,280,2202.578,0.000,1849.000,6872,353,0,1,2 +rust,episodes-create-help,281,1540.975,1482.000,0.000,6656,351,0,1,2 +go,episodes-create-help,281,4256.937,1934.000,1934.000,13276,703,0,39,33 +go,episodes-create-help,282,3933.280,1929.000,1929.000,13304,684,0,39,33 +rust,episodes-create-help,282,2161.871,0.000,1743.000,6716,350,0,1,2 +rust,episodes-create-help,283,1434.805,0.000,1376.000,6740,352,0,1,2 +go,episodes-create-help,283,4551.821,2229.000,2229.000,13224,693,0,43,40 +go,episodes-create-help,284,4213.045,4133.000,0.000,13292,702,0,39,33 +rust,episodes-create-help,284,2256.509,0.000,1821.000,6804,351,0,1,2 +rust,episodes-create-help,285,1793.029,0.000,1735.000,6724,353,0,1,1 +go,episodes-create-help,285,4591.476,1665.000,2497.000,13212,693,0,44,35 +go,episodes-create-help,286,3937.436,2706.000,902.000,13300,688,0,33,28 +rust,episodes-create-help,286,1542.779,0.000,1492.000,6740,352,0,1,2 +rust,episodes-create-help,287,1447.669,0.000,1403.000,6492,357,0,1,1 +go,episodes-create-help,287,4055.971,1831.000,1831.000,13308,693,0,40,33 +go,episodes-create-help,288,3572.581,1751.000,1751.000,13276,689,0,14,8 +rust,episodes-create-help,288,1900.058,790.000,790.000,6536,354,0,1,2 +rust,episodes-create-help,289,1373.790,0.000,1330.000,6944,355,0,1,2 +go,episodes-create-help,289,4521.364,4439.000,0.000,13308,689,0,16,12 +go,episodes-create-help,290,5626.449,2017.000,3026.000,13312,705,0,48,43 +rust,episodes-create-help,290,2333.896,0.000,2252.000,6860,350,0,1,2 +rust,episodes-create-help,291,2358.531,1117.000,1117.000,6816,350,0,1,2 +go,episodes-create-help,291,5544.907,3263.000,2175.000,13276,697,0,44,39 +go,episodes-create-help,292,6043.053,925.000,4628.000,13312,703,0,17,12 +rust,episodes-create-help,292,2441.005,781.000,1562.000,6840,350,0,1,2 +rust,episodes-create-help,293,2384.180,0.000,2285.000,6580,352,0,1,2 +go,episodes-create-help,293,5413.679,1769.000,3538.000,13236,693,0,35,29 +go,episodes-create-help,294,6533.395,3996.000,1998.000,13276,692,0,51,43 +rust,episodes-create-help,294,2410.258,0.000,2349.000,6684,352,0,1,1 +rust,episodes-create-help,295,1852.852,0.000,1781.000,6820,353,0,1,2 +go,episodes-create-help,295,3861.575,3794.000,0.000,13300,715,0,42,35 +go,episodes-create-help,296,3681.616,1810.000,1810.000,13304,696,0,14,10 +rust,episodes-create-help,296,2007.251,833.000,833.000,6740,352,0,1,2 +rust,episodes-create-help,297,1533.271,728.000,728.000,6756,350,0,1,3 +go,episodes-create-help,297,4222.102,3786.000,0.000,13276,697,0,44,36 +go,episodes-create-help,298,4020.823,988.000,2966.000,13300,704,0,37,33 +rust,episodes-create-help,298,1956.356,0.000,1530.000,6488,357,0,1,2 +rust,episodes-create-help,299,1462.367,0.000,1421.000,6864,351,0,1,1 +go,episodes-create-help,299,3838.522,1879.000,1879.000,13292,693,0,39,35 diff --git a/bench/results/linux-amd64-cpu7.json b/bench/results/linux-amd64-cpu7.json new file mode 100644 index 0000000..3160f6a --- /dev/null +++ b/bench/results/linux-amd64-cpu7.json @@ -0,0 +1,322 @@ +[ + { + "implementation": "go", + "command": "root-help", + "samples": 300, + "cpu_percent": 93.50948253283174, + "metrics": { + "wall_us": { + "mean": 4720.968626666667, + "median": 4622.384, + "p95": 5912.527, + "min": 3375.221, + "max": 6992.305, + "stddev": 631.6451131576396 + }, + "user_us": { + "mean": 1952.5066666666667, + "median": 1928.5, + "p95": 3682.0, + "min": 0.0, + "max": 4604.0, + "stddev": 1015.33600183497 + }, + "system_us": { + "mean": 2462.0466666666666, + "median": 2539.0, + "p95": 4180.0, + "min": 0.0, + "max": 5316.0, + "stddev": 1029.4935087162469 + }, + "peak_rss_kib": { + "mean": 13166.08, + "median": 13176.0, + "p95": 13220.0, + "min": 12956.0, + "max": 13276.0, + "stddev": 41.14162855308477 + }, + "minor_faults": { + "mean": 695.0166666666667, + "median": 692.5, + "p95": 707.0, + "min": 679.0, + "max": 715.0, + "stddev": 7.46032096420046 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 33.15, + "median": 39.0, + "p95": 47.0, + "min": 13.0, + "max": 55.0, + "stddev": 11.628735958822007 + }, + "involuntary_context_switches": { + "mean": 28.283333333333335, + "median": 33.5, + "p95": 41.0, + "min": 7.0, + "max": 50.0, + "stddev": 11.386090442094492 + }, + "total_cpu_us": { + "mean": 4414.553333333333, + "median": 4352.5, + "p95": 5336.0, + "min": 3321.0, + "max": 6008.0, + "stddev": 518.9405172935419 + } + } + }, + { + "implementation": "rust", + "command": "root-help", + "samples": 300, + "cpu_percent": 92.83125340154282, + "metrics": { + "wall_us": { + "mean": 1988.4359333333334, + "median": 1959.8470000000002, + "p95": 2629.261, + "min": 1205.684, + "max": 3357.978, + "stddev": 367.70908589376586 + }, + "user_us": { + "mean": 562.8766666666667, + "median": 0.0, + "p95": 1738.0, + "min": 0.0, + "max": 2291.0, + "stddev": 649.7072736668073 + }, + "system_us": { + "mean": 1283.0133333333333, + "median": 1399.0, + "p95": 2231.0, + "min": 0.0, + "max": 2649.0, + "stddev": 684.7279653182634 + }, + "peak_rss_kib": { + "mean": 6624.106666666667, + "median": 6632.0, + "p95": 6824.0, + "min": 6152.0, + "max": 6940.0, + "stddev": 127.51026869323985 + }, + "minor_faults": { + "mean": 350.2266666666667, + "median": 350.0, + "p95": 352.0, + "min": 348.0, + "max": 357.0, + "stddev": 1.3839396261719255 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 0.9933333333333333, + "median": 1.0, + "p95": 1.0, + "min": 0.0, + "max": 1.0, + "stddev": 0.08137703743822469 + }, + "involuntary_context_switches": { + "mean": 1.4466666666666668, + "median": 1.0, + "p95": 2.0, + "min": 1.0, + "max": 3.0, + "stddev": 0.5232802520850774 + }, + "total_cpu_us": { + "mean": 1845.89, + "median": 1841.0, + "p95": 2322.0, + "min": 1168.0, + "max": 2703.0, + "stddev": 294.91329443301356 + } + } + }, + { + "implementation": "go", + "command": "episodes-create-help", + "samples": 300, + "cpu_percent": 93.64261759923433, + "metrics": { + "wall_us": { + "mean": 4420.87884, + "median": 4343.876, + "p95": 5544.907, + "min": 3209.058, + "max": 6533.395, + "stddev": 611.5106530930111 + }, + "user_us": { + "mean": 1937.4866666666667, + "median": 1927.0, + "p95": 3685.0, + "min": 0.0, + "max": 4763.0, + "stddev": 1013.5703148551439 + }, + "system_us": { + "mean": 2202.34, + "median": 2125.5, + "p95": 3887.0, + "min": 0.0, + "max": 4628.0, + "stddev": 1023.4428486241916 + }, + "peak_rss_kib": { + "mean": 13292.546666666667, + "median": 13300.0, + "p95": 13344.0, + "min": 13148.0, + "max": 13348.0, + "stddev": 37.14576452601591 + }, + "minor_faults": { + "mean": 696.2433333333333, + "median": 694.0, + "p95": 709.0, + "min": 679.0, + "max": 715.0, + "stddev": 7.637896016283251 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 33.36666666666667, + "median": 38.0, + "p95": 45.0, + "min": 12.0, + "max": 51.0, + "stddev": 10.44759408774203 + }, + "involuntary_context_switches": { + "mean": 28.006666666666668, + "median": 32.0, + "p95": 40.0, + "min": 7.0, + "max": 44.0, + "stddev": 10.13607199834115 + }, + "total_cpu_us": { + "mean": 4139.826666666667, + "median": 4079.5, + "p95": 5089.0, + "min": 3110.0, + "max": 5994.0, + "stddev": 538.3954958537038 + } + } + }, + { + "implementation": "rust", + "command": "episodes-create-help", + "samples": 300, + "cpu_percent": 91.7690330649965, + "metrics": { + "wall_us": { + "mean": 1876.3809633333333, + "median": 1849.3135, + "p95": 2519.624, + "min": 1171.471, + "max": 3116.085, + "stddev": 372.07468855878295 + }, + "user_us": { + "mean": 480.33, + "median": 0.0, + "p95": 1721.0, + "min": 0.0, + "max": 2216.0, + "stddev": 600.9832064486772 + }, + "system_us": { + "mean": 1241.6066666666666, + "median": 1357.0, + "p95": 2108.0, + "min": 0.0, + "max": 2362.0, + "stddev": 643.8449129686089 + }, + "peak_rss_kib": { + "mean": 6756.066666666667, + "median": 6758.0, + "p95": 6888.0, + "min": 6408.0, + "max": 6988.0, + "stddev": 105.63658877911995 + }, + "minor_faults": { + "mean": 351.68, + "median": 351.0, + "p95": 355.0, + "min": 349.0, + "max": 359.0, + "stddev": 1.7255723688098392 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 0.9933333333333333, + "median": 1.0, + "p95": 1.0, + "min": 0.0, + "max": 1.0, + "stddev": 0.08137703743822469 + }, + "involuntary_context_switches": { + "mean": 1.5433333333333332, + "median": 2.0, + "p95": 2.0, + "min": 1.0, + "max": 3.0, + "stddev": 0.5242030988928708 + }, + "total_cpu_us": { + "mean": 1721.9366666666667, + "median": 1720.0, + "p95": 2268.0, + "min": 1138.0, + "max": 2624.0, + "stddev": 299.9694973196812 + } + } + } +] diff --git a/bench/results/linux-amd64.csv b/bench/results/linux-amd64.csv new file mode 100644 index 0000000..39d9199 --- /dev/null +++ b/bench/results/linux-amd64.csv @@ -0,0 +1,1201 @@ +implementation,command,sample,wall_us,user_us,system_us,peak_rss_kib,minor_faults,major_faults,voluntary_context_switches,involuntary_context_switches +go,root-help,0,6631.167,3199.000,5332.000,13436,819,0,76,0 +rust,root-help,0,3218.116,0.000,3154.000,6772,351,0,1,0 +rust,root-help,1,3226.871,1052.000,2105.000,6644,350,0,1,0 +go,root-help,1,5929.760,4900.000,2940.000,13176,749,0,67,0 +go,root-help,2,6136.658,2953.000,4922.000,13304,793,0,64,0 +rust,root-help,2,3200.284,0.000,3128.000,6584,349,0,1,0 +rust,root-help,3,2279.401,0.000,2170.000,6556,351,0,1,0 +go,root-help,3,8655.641,3100.000,7233.000,13432,798,0,79,0 +go,root-help,4,6526.011,2089.000,6269.000,13556,837,0,70,2 +rust,root-help,4,2657.953,850.000,1701.000,6672,350,0,1,1 +rust,root-help,5,3772.848,1237.000,2474.000,6600,348,0,1,0 +go,root-help,5,7324.591,4841.000,4841.000,13172,768,0,74,0 +go,root-help,6,6128.943,5810.000,2324.000,13524,852,0,68,0 +rust,root-help,6,2328.294,0.000,2254.000,6452,351,0,1,0 +rust,root-help,7,2157.434,0.000,2071.000,6720,349,0,1,0 +go,root-help,7,8615.014,3204.000,7476.000,13276,804,0,92,0 +go,root-help,8,6660.242,2278.000,5695.000,13564,827,0,74,0 +rust,root-help,8,3543.778,1132.000,2264.000,6792,351,0,1,1 +rust,root-help,9,3002.000,944.000,1889.000,6648,349,0,1,1 +go,root-help,9,6513.817,1020.000,7146.000,13204,765,0,74,0 +go,root-help,10,6462.451,5797.000,2319.000,13152,806,0,77,0 +rust,root-help,10,2965.922,0.000,2866.000,6548,349,0,1,0 +rust,root-help,11,3503.293,0.000,3392.000,6676,351,0,1,0 +go,root-help,11,4522.126,4497.000,1499.000,13428,815,0,61,0 +go,root-help,12,8255.109,4683.000,5854.000,13548,842,0,82,0 +rust,root-help,12,3936.316,0.000,3835.000,6572,349,0,1,0 +rust,root-help,13,2514.503,0.000,2404.000,6712,349,0,1,0 +go,root-help,13,9008.434,4698.000,7048.000,13308,802,0,93,0 +go,root-help,14,7076.154,2181.000,6543.000,13532,836,0,84,0 +rust,root-help,14,3482.162,849.000,2547.000,6692,351,0,1,0 +rust,root-help,15,3595.886,1158.000,2316.000,6740,351,0,1,1 +go,root-help,15,5922.317,1540.000,6162.000,13244,799,0,68,1 +go,root-help,16,5861.251,2257.000,5643.000,13164,769,0,69,0 +rust,root-help,16,2466.464,793.000,1586.000,6680,350,0,1,0 +rust,root-help,17,3604.271,0.000,3535.000,6592,349,0,1,0 +go,root-help,17,8202.770,4143.000,6215.000,13212,805,0,79,0 +go,root-help,18,9314.849,4413.000,7356.000,13420,802,0,97,0 +rust,root-help,18,4323.923,2106.000,2106.000,6452,349,0,1,0 +rust,root-help,19,2722.995,0.000,2601.000,6800,351,0,1,0 +go,root-help,19,6460.418,1998.000,6993.000,13308,813,0,70,0 +go,root-help,20,8972.957,2204.000,8818.000,13564,822,0,82,1 +rust,root-help,20,2608.600,837.000,1675.000,6496,349,0,1,0 +rust,root-help,21,3473.125,1133.000,2266.000,6396,355,0,1,0 +go,root-help,21,6495.271,2700.000,6751.000,13164,773,0,71,0 +go,root-help,22,8398.518,5885.000,4904.000,13532,837,0,91,0 +rust,root-help,22,4194.961,1644.000,2466.000,6632,349,0,1,0 +rust,root-help,23,3749.384,1220.000,2441.000,6604,351,0,1,0 +go,root-help,23,9007.792,6598.000,5498.000,13532,847,0,92,0 +go,root-help,24,7901.133,5607.000,4486.000,13292,794,0,74,1 +rust,root-help,24,3351.687,0.000,3194.000,6548,349,0,1,1 +rust,root-help,25,2011.048,0.000,1902.000,6284,351,0,1,0 +go,root-help,25,6185.330,3466.000,4621.000,13176,746,0,64,0 +go,root-help,26,7346.752,6230.000,3115.000,13476,806,0,71,0 +rust,root-help,26,2414.346,0.000,2301.000,6736,351,0,1,1 +rust,root-help,27,2308.387,1113.000,1113.000,6660,349,0,1,0 +go,root-help,27,5704.978,2749.000,4582.000,13240,803,0,73,0 +go,root-help,28,8143.689,6304.000,4202.000,13420,816,0,76,0 +rust,root-help,28,3376.514,0.000,3258.000,6632,351,0,1,0 +rust,root-help,29,2184.593,1045.000,1045.000,6624,350,0,1,0 +go,root-help,29,6755.321,4452.000,3339.000,13544,833,0,65,2 +go,root-help,30,5328.640,1971.000,4929.000,13272,831,0,72,0 +rust,root-help,30,2437.570,1185.000,1185.000,6600,349,0,1,0 +rust,root-help,31,1857.730,0.000,1800.000,6800,351,0,1,0 +go,root-help,31,5922.667,1735.000,6072.000,13420,829,0,60,2 +go,root-help,32,6535.046,0.000,8282.000,13436,804,0,62,0 +rust,root-help,32,3540.412,1713.000,1713.000,6668,349,0,1,0 +rust,root-help,33,2286.656,0.000,2204.000,6660,350,0,1,0 +go,root-help,33,8223.619,3096.000,7224.000,13404,794,0,87,0 +go,root-help,34,7461.808,3553.000,5923.000,13564,836,0,71,0 +rust,root-help,34,2624.401,1699.000,849.000,6684,350,0,1,0 +rust,root-help,35,3535.162,2300.000,1150.000,6492,350,0,1,0 +go,root-help,35,6596.552,4195.000,4195.000,13556,827,0,79,0 +go,root-help,36,6747.978,5049.000,3786.000,13148,754,0,80,0 +rust,root-help,36,2437.659,1181.000,1181.000,6840,350,0,1,0 +rust,root-help,37,2889.108,926.000,1853.000,6688,350,0,1,0 +go,root-help,37,5330.264,3120.000,4160.000,13420,829,0,70,2 +go,root-help,38,8008.775,2252.000,5632.000,13104,751,0,48,0 +rust,root-help,38,3477.754,0.000,3381.000,6732,350,0,1,1 +rust,root-help,39,2294.470,0.000,2215.000,6588,350,0,1,0 +go,root-help,39,6447.513,4038.000,4038.000,13532,822,0,75,0 +go,root-help,40,6060.816,2180.000,5451.000,13404,788,0,68,2 +rust,root-help,40,3658.041,1191.000,2382.000,6520,350,0,1,0 +rust,root-help,41,1628.720,0.000,1534.000,6704,351,0,1,0 +go,root-help,41,6646.446,4820.000,3615.000,13304,783,0,73,0 +go,root-help,42,5217.171,1679.000,4197.000,13436,797,0,69,1 +rust,root-help,42,4271.596,2065.000,2065.000,6668,350,0,1,1 +rust,root-help,43,1796.655,0.000,1679.000,6760,350,0,1,0 +go,root-help,43,4753.912,2138.000,4277.000,13020,762,0,66,0 +go,root-help,44,4871.002,2368.000,3552.000,13212,803,0,60,0 +rust,root-help,44,2556.431,0.000,1981.000,6700,350,0,1,1 +rust,root-help,45,1789.743,844.000,844.000,6556,353,0,1,0 +go,root-help,45,4216.561,5481.000,0.000,13180,751,0,58,0 +go,root-help,46,4026.955,2078.000,3118.000,13148,746,0,45,0 +rust,root-help,46,1911.261,0.000,1860.000,6496,349,0,1,0 +rust,root-help,47,1768.453,858.000,858.000,6472,352,0,1,0 +go,root-help,47,4190.373,1584.000,3960.000,13600,838,0,56,0 +go,root-help,48,4725.296,2011.000,4022.000,13468,834,0,59,0 +rust,root-help,48,2402.012,0.000,2354.000,6668,350,0,1,0 +rust,root-help,49,2013.272,0.000,1965.000,6688,350,0,1,0 +go,root-help,49,6098.287,3255.000,4340.000,13416,793,0,73,1 +go,root-help,50,5596.425,1113.000,5566.000,13560,829,0,76,1 +rust,root-help,50,1869.501,0.000,1790.000,6344,350,0,1,1 +rust,root-help,51,1609.183,0.000,1540.000,6856,351,0,1,0 +go,root-help,51,4593.299,3922.000,1307.000,13292,801,0,57,1 +go,root-help,52,5700.769,2074.000,5186.000,13604,858,0,74,0 +rust,root-help,52,2410.038,1160.000,1160.000,6476,349,0,1,0 +rust,root-help,53,1644.239,0.000,1570.000,6712,348,0,1,0 +go,root-help,53,4466.119,4087.000,1362.000,13604,836,0,58,0 +go,root-help,54,4641.330,771.000,4626.000,13436,819,0,59,2 +rust,root-help,54,1866.777,0.000,1811.000,6656,351,0,1,0 +rust,root-help,55,2004.915,967.000,967.000,6528,350,0,1,0 +go,root-help,55,4262.397,2563.000,2563.000,13404,818,0,59,0 +go,root-help,56,4410.376,2680.000,2680.000,13432,817,0,58,0 +rust,root-help,56,1742.825,0.000,1691.000,6652,350,0,1,0 +rust,root-help,57,1528.260,0.000,1476.000,6548,350,0,1,0 +go,root-help,57,3921.427,1624.000,3249.000,13420,836,0,57,0 +go,root-help,58,4333.461,2735.000,2735.000,13428,804,0,57,0 +rust,root-help,58,1799.352,0.000,1756.000,6648,350,0,1,0 +rust,root-help,59,1387.266,0.000,1349.000,6600,352,0,1,0 +go,root-help,59,6560.164,2562.000,2562.000,13276,770,0,54,0 +go,root-help,60,4559.656,0.000,5725.000,13308,783,0,54,0 +rust,root-help,60,2396.362,0.000,2343.000,6716,349,0,1,0 +rust,root-help,61,1892.095,0.000,1858.000,6632,350,0,1,0 +go,root-help,61,4212.563,2690.000,2690.000,13180,757,0,55,0 +go,root-help,62,5624.497,1286.000,5789.000,13420,795,0,69,0 +rust,root-help,62,2391.703,1163.000,1163.000,6672,349,0,1,0 +rust,root-help,63,3087.210,0.000,3035.000,6828,352,0,1,0 +go,root-help,63,5630.508,2404.000,4809.000,13564,836,0,67,0 +go,root-help,64,6786.039,2259.000,6779.000,13564,853,0,70,0 +rust,root-help,64,2582.391,1246.000,1246.000,6680,351,0,1,0 +rust,root-help,65,2459.851,0.000,2380.000,6648,349,0,1,0 +go,root-help,65,4200.792,3199.000,2399.000,13420,807,0,59,0 +go,root-help,66,5070.375,2120.000,4240.000,13440,811,0,61,0 +rust,root-help,66,2463.859,0.000,2391.000,6544,349,0,1,0 +rust,root-help,67,1942.861,1821.000,0.000,6412,356,0,1,1 +go,root-help,67,5411.786,4033.000,2688.000,13560,830,0,64,0 +go,root-help,68,4520.453,4297.000,1432.000,13204,795,0,62,0 +rust,root-help,68,3267.499,0.000,2629.000,6696,351,0,1,1 +rust,root-help,69,3060.810,1971.000,985.000,6560,349,0,1,1 +go,root-help,69,6691.682,6203.000,2067.000,13436,798,0,81,1 +go,root-help,70,6426.525,1136.000,6818.000,13432,811,0,74,0 +rust,root-help,70,2475.991,0.000,2379.000,6852,351,0,1,0 +rust,root-help,71,3239.706,0.000,3112.000,6480,350,0,1,0 +go,root-help,71,5779.688,3064.000,4596.000,13164,769,0,71,0 +go,root-help,72,5720.177,4836.000,2418.000,13420,805,0,70,0 +rust,root-help,72,2549.460,0.000,2467.000,6716,350,0,1,0 +rust,root-help,73,3341.047,1086.000,2173.000,6812,351,0,1,1 +go,root-help,73,5574.102,4562.000,2737.000,13276,776,0,66,0 +go,root-help,74,8685.326,6656.000,3993.000,13436,809,0,90,1 +rust,root-help,74,4632.603,986.000,2960.000,6664,351,0,1,0 +rust,root-help,75,2693.210,0.000,2584.000,6564,350,0,1,0 +go,root-help,75,7412.937,5321.000,4256.000,13180,768,0,84,0 +go,root-help,76,5689.880,2831.000,4718.000,13300,804,0,74,0 +rust,root-help,76,2311.603,0.000,2221.000,6488,350,0,1,0 +rust,root-help,77,2817.353,0.000,2737.000,6580,349,0,1,0 +go,root-help,77,6884.113,6236.000,2494.000,13340,814,0,72,0 +go,root-help,78,8405.972,3620.000,7240.000,13416,834,0,79,2 +rust,root-help,78,3327.471,0.000,3245.000,6564,350,0,1,1 +rust,root-help,79,2873.599,928.000,1857.000,6612,350,0,1,0 +go,root-help,79,4554.276,1226.000,4906.000,13436,800,0,65,0 +go,root-help,80,6521.091,5380.000,2690.000,13472,839,0,81,0 +rust,root-help,80,2007.881,972.000,972.000,6588,349,0,1,1 +rust,root-help,81,2619.442,2563.000,0.000,6588,349,0,1,0 +go,root-help,81,4406.137,889.000,4446.000,13556,833,0,62,0 +go,root-help,82,4772.855,2886.000,2886.000,13420,816,0,66,0 +rust,root-help,82,2395.892,1164.000,1164.000,6592,351,0,1,0 +rust,root-help,83,2389.629,1152.000,1152.000,6580,351,0,1,0 +go,root-help,83,4071.258,0.000,4945.000,13180,770,0,58,0 +go,root-help,84,4211.823,2728.000,2728.000,13436,814,0,60,1 +rust,root-help,84,2216.944,0.000,2125.000,6648,349,0,1,1 +rust,root-help,85,2280.574,1108.000,1108.000,6720,349,0,1,0 +go,root-help,85,9454.873,3301.000,7703.000,13396,836,0,96,1 +go,root-help,86,7800.455,2826.000,7065.000,13440,819,0,86,0 +rust,root-help,86,2555.170,1212.000,1212.000,6556,350,0,1,0 +rust,root-help,87,2985.599,0.000,2837.000,6632,348,0,1,0 +go,root-help,87,6012.906,2149.000,5372.000,13436,822,0,74,0 +go,root-help,88,4346.035,2695.000,2695.000,13216,753,0,48,0 +rust,root-help,88,1845.398,0.000,1755.000,6640,349,0,1,0 +rust,root-help,89,3275.213,779.000,2339.000,6644,349,0,1,0 +go,root-help,89,7370.287,4218.000,5273.000,13564,824,0,82,0 +go,root-help,90,8091.331,4475.000,5370.000,13276,775,0,85,1 +rust,root-help,90,2212.296,1051.000,1051.000,6696,348,0,1,0 +rust,root-help,91,1683.312,0.000,1549.000,6420,351,0,1,1 +go,root-help,91,8732.976,3958.000,6928.000,13288,801,0,81,0 +go,root-help,92,6887.699,3529.000,5882.000,13604,849,0,68,0 +rust,root-help,92,2084.526,0.000,1955.000,6632,348,0,1,0 +rust,root-help,93,2401.832,1134.000,1134.000,6280,356,0,1,0 +go,root-help,93,7817.726,6336.000,3168.000,13432,803,0,89,0 +go,root-help,94,5533.976,1084.000,5965.000,13404,806,0,71,0 +rust,root-help,94,2265.767,1105.000,1105.000,6472,351,0,1,0 +rust,root-help,95,2663.654,1728.000,864.000,6572,350,0,1,0 +go,root-help,95,5113.856,6757.000,0.000,13420,814,0,60,0 +go,root-help,96,5355.772,2041.000,5104.000,13292,804,0,59,0 +rust,root-help,96,3581.378,865.000,2595.000,6428,351,0,1,1 +rust,root-help,97,3670.506,2362.000,1181.000,6512,350,0,1,0 +go,root-help,97,5992.148,1453.000,5815.000,13176,751,0,63,1 +go,root-help,98,6650.274,3636.000,4848.000,13436,816,0,76,2 +rust,root-help,98,2038.018,0.000,1962.000,6612,349,0,1,0 +rust,root-help,99,1909.658,0.000,1844.000,6560,352,0,1,0 +go,root-help,99,5878.945,4854.000,2427.000,13168,782,0,80,0 +go,root-help,100,6914.730,6048.000,2419.000,13260,803,0,82,0 +rust,root-help,100,1998.865,972.000,972.000,6556,349,0,1,0 +rust,root-help,101,2655.719,866.000,1732.000,6680,349,0,1,0 +go,root-help,101,5343.919,2322.000,4644.000,13436,794,0,62,0 +go,root-help,102,5105.282,4432.000,1773.000,13084,764,0,48,1 +rust,root-help,102,2019.845,0.000,1955.000,6540,352,0,1,0 +rust,root-help,103,2626.825,0.000,2558.000,6664,350,0,1,0 +go,root-help,103,4936.244,1880.000,4700.000,13492,851,0,65,1 +go,root-help,104,5441.493,4443.000,2221.000,13404,835,0,61,0 +rust,root-help,104,1996.952,0.000,1933.000,6724,349,0,1,0 +rust,root-help,105,2076.361,0.000,1997.000,6336,355,0,1,0 +go,root-help,105,4897.521,2414.000,4024.000,13476,804,0,69,2 +go,root-help,106,4873.927,3108.000,3108.000,13476,821,0,68,2 +rust,root-help,106,2742.683,894.000,1789.000,6724,350,0,1,0 +rust,root-help,107,3417.800,0.000,3354.000,6712,350,0,1,0 +go,root-help,107,7301.508,2243.000,6730.000,13476,805,0,79,0 +go,root-help,108,8228.768,5665.000,4532.000,13420,832,0,82,2 +rust,root-help,108,3485.067,2274.000,1137.000,6668,351,0,1,0 +rust,root-help,109,2971.864,0.000,2878.000,6812,351,0,1,0 +go,root-help,109,6817.307,6442.000,2576.000,13160,764,0,79,0 +go,root-help,110,8402.586,5677.000,4542.000,13120,784,0,89,3 +rust,root-help,110,2992.713,1446.000,1446.000,6792,350,0,1,0 +rust,root-help,111,3153.624,0.000,3086.000,6556,351,0,1,0 +go,root-help,111,6883.862,4715.000,3536.000,13216,756,0,72,2 +go,root-help,112,7296.779,6465.000,2586.000,13224,822,0,87,0 +rust,root-help,112,3225.188,1047.000,2095.000,6588,351,0,1,0 +rust,root-help,113,3654.815,0.000,3602.000,6600,349,0,1,0 +go,root-help,113,4839.633,1095.000,5475.000,13240,802,0,60,3 +go,root-help,114,4383.966,1944.000,3888.000,13428,788,0,58,1 +rust,root-help,114,2886.974,1896.000,948.000,6228,356,0,1,0 +rust,root-help,115,1617.158,0.000,1569.000,6660,348,0,1,0 +go,root-help,115,7718.901,5664.000,4248.000,13344,795,0,91,0 +go,root-help,116,8823.135,5486.000,4572.000,13404,800,0,66,3 +rust,root-help,116,2549.580,0.000,2487.000,6572,349,0,1,1 +rust,root-help,117,2617.106,1281.000,1281.000,6372,350,0,1,0 +go,root-help,117,5925.643,3637.000,5456.000,13204,782,0,66,1 +go,root-help,118,8314.520,6460.000,3876.000,13436,812,0,75,2 +rust,root-help,118,2489.989,1180.000,1180.000,6560,351,0,1,0 +rust,root-help,119,2203.761,1057.000,1057.000,6668,349,0,1,0 +go,root-help,119,5981.867,2623.000,5246.000,13180,750,0,68,0 +go,root-help,120,6329.120,4170.000,4170.000,13432,814,0,70,0 +rust,root-help,120,2476.603,0.000,2388.000,6480,353,0,1,0 +rust,root-help,121,3895.849,1917.000,1917.000,6724,348,0,1,0 +go,root-help,121,8849.324,5118.000,6398.000,13544,851,0,79,3 +go,root-help,122,7463.632,7622.000,2177.000,13428,818,0,84,1 +rust,root-help,122,2148.987,0.000,2020.000,6576,350,0,1,0 +rust,root-help,123,5580.644,2245.000,2245.000,6484,349,0,1,1 +go,root-help,123,10243.974,6459.000,6459.000,13404,820,0,92,0 +go,root-help,124,6202.682,1198.000,5990.000,13404,828,0,76,0 +rust,root-help,124,2544.159,1234.000,1234.000,6624,349,0,1,0 +rust,root-help,125,4737.621,1019.000,3059.000,6600,350,0,1,1 +go,root-help,125,6096.793,0.000,8272.000,13436,814,0,58,0 +go,root-help,126,7030.127,3119.000,7279.000,13148,754,0,61,1 +rust,root-help,126,2310.090,1112.000,1112.000,6752,350,0,1,0 +rust,root-help,127,2433.862,2316.000,0.000,6656,350,0,1,0 +go,root-help,127,4812.170,2046.000,4092.000,13440,805,0,59,0 +go,root-help,128,5353.788,3953.000,2635.000,13340,813,0,52,0 +rust,root-help,128,2381.815,1157.000,1157.000,6724,351,0,1,0 +rust,root-help,129,1987.144,0.000,1878.000,6284,358,0,1,0 +go,root-help,129,4509.292,2467.000,3701.000,13564,848,0,61,0 +go,root-help,130,6017.044,2983.000,3978.000,13132,772,0,73,1 +rust,root-help,130,2049.901,976.000,976.000,6656,349,0,1,0 +rust,root-help,131,1886.674,1797.000,0.000,6572,350,0,1,0 +go,root-help,131,5023.999,4774.000,1909.000,13304,822,0,62,0 +go,root-help,132,5270.942,3790.000,2526.000,13248,794,0,62,0 +rust,root-help,132,3059.989,996.000,1992.000,6684,350,0,1,0 +rust,root-help,133,2458.187,789.000,1579.000,6272,355,0,1,0 +go,root-help,133,5300.297,3393.000,3393.000,13312,797,0,59,0 +go,root-help,134,5322.710,2560.000,4267.000,13548,834,0,67,0 +rust,root-help,134,2635.260,0.000,2590.000,6684,350,0,1,0 +rust,root-help,135,2266.908,0.000,2220.000,6676,350,0,1,0 +go,root-help,135,5024.600,2527.000,3791.000,13292,793,0,64,0 +go,root-help,136,4232.091,1733.000,3466.000,13148,789,0,58,0 +rust,root-help,136,1681.670,0.000,1617.000,6516,349,0,1,0 +rust,root-help,137,1573.525,760.000,760.000,6600,349,0,1,0 +go,root-help,137,6455.969,3144.000,5241.000,13432,829,0,86,0 +go,root-help,138,6171.184,2282.000,5324.000,13416,816,0,79,0 +rust,root-help,138,2729.247,1048.000,1048.000,6608,351,0,1,1 +rust,root-help,139,2797.244,0.000,2751.000,6748,350,0,1,0 +go,root-help,139,4310.818,4214.000,1404.000,13180,764,0,60,0 +go,root-help,140,5708.524,1415.000,5660.000,13164,753,0,68,0 +rust,root-help,140,1827.904,0.000,1776.000,6680,349,0,1,0 +rust,root-help,141,1564.430,0.000,1499.000,6732,351,0,1,0 +go,root-help,141,4837.127,3171.000,3171.000,13560,848,0,71,0 +go,root-help,142,5544.536,888.000,6219.000,13096,775,0,62,0 +rust,root-help,142,3879.859,957.000,2871.000,6704,350,0,1,0 +rust,root-help,143,1949.714,0.000,1882.000,6652,351,0,1,0 +go,root-help,143,4844.671,2126.000,4252.000,13404,817,0,65,0 +go,root-help,144,4346.626,4143.000,1381.000,13404,809,0,55,0 +rust,root-help,144,3564.045,0.000,3503.000,6780,352,0,1,0 +rust,root-help,145,3660.107,0.000,3592.000,6604,350,0,1,0 +go,root-help,145,9425.717,6438.000,5365.000,13300,793,0,89,0 +go,root-help,146,7200.417,4491.000,4491.000,13436,806,0,87,0 +rust,root-help,146,2866.545,1834.000,917.000,6688,350,0,1,1 +rust,root-help,147,2694.583,0.000,2613.000,6368,350,0,1,0 +go,root-help,147,8064.820,4508.000,5635.000,13504,838,0,90,0 +go,root-help,148,5989.152,5322.000,2661.000,13404,836,0,76,0 +rust,root-help,148,2413.564,1159.000,1159.000,6600,349,0,1,0 +rust,root-help,149,3034.881,0.000,2926.000,6720,351,0,1,1 +go,root-help,149,6711.938,2173.000,6521.000,13568,828,0,66,0 +go,root-help,150,8313.900,6612.000,4408.000,13348,800,0,79,1 +rust,root-help,150,3526.084,1725.000,1725.000,6640,349,0,1,0 +rust,root-help,151,2909.927,946.000,1893.000,6596,351,0,1,0 +go,root-help,151,5898.270,2080.000,6240.000,13212,804,0,71,0 +go,root-help,152,7502.805,4895.000,4895.000,13220,766,0,60,0 +rust,root-help,152,2450.594,784.000,1568.000,6556,349,0,1,0 +rust,root-help,153,3581.930,2319.000,1159.000,6232,357,0,1,0 +go,root-help,153,9579.797,2642.000,9687.000,13476,792,0,83,1 +go,root-help,154,9436.347,5926.000,5926.000,13184,747,0,84,0 +rust,root-help,154,3465.080,0.000,3358.000,6656,350,0,1,0 +rust,root-help,155,2728.916,0.000,2607.000,6648,351,0,1,0 +go,root-help,155,5292.652,4484.000,2242.000,13436,791,0,61,0 +go,root-help,156,6125.368,3750.000,3750.000,13404,821,0,76,0 +rust,root-help,156,2739.686,889.000,1778.000,6728,348,0,1,0 +rust,root-help,157,2356.558,1119.000,1119.000,6684,351,0,1,0 +go,root-help,157,6002.887,3994.000,3994.000,13436,824,0,69,0 +go,root-help,158,5098.368,2217.000,4434.000,13404,808,0,64,0 +rust,root-help,158,2551.824,0.000,1996.000,6848,350,0,1,1 +rust,root-help,159,2581.109,0.000,2487.000,6672,350,0,1,0 +go,root-help,159,5096.224,2001.000,5004.000,13436,822,0,62,0 +go,root-help,160,9312.183,6052.000,4842.000,13104,782,0,82,1 +rust,root-help,160,3464.980,0.000,3366.000,6664,350,0,1,0 +rust,root-help,161,3943.698,952.000,2856.000,6656,350,0,1,0 +go,root-help,161,6192.154,4449.000,4449.000,13180,776,0,72,1 +go,root-help,162,8562.274,2665.000,7997.000,13600,856,0,67,2 +rust,root-help,162,2506.970,1197.000,1197.000,6684,350,0,1,1 +rust,root-help,163,3610.072,1160.000,2321.000,6540,349,0,1,0 +go,root-help,163,7574.120,5872.000,2936.000,13476,829,0,72,6 +go,root-help,164,5422.477,2813.000,3750.000,13216,768,0,67,5 +rust,root-help,164,2131.924,1031.000,1031.000,6652,350,0,1,0 +rust,root-help,165,2428.371,0.000,2360.000,6540,349,0,1,0 +go,root-help,165,4824.163,3511.000,2341.000,13216,748,0,63,4 +go,root-help,166,4847.567,1272.000,5089.000,13404,804,0,63,5 +rust,root-help,166,2893.937,1151.000,1151.000,6664,350,0,1,1 +rust,root-help,167,2199.382,0.000,1956.000,6556,349,0,1,0 +go,root-help,167,6171.514,4387.000,2925.000,13404,819,0,67,4 +go,root-help,168,6102.374,5623.000,2249.000,13428,805,0,67,3 +rust,root-help,168,2318.395,0.000,2220.000,6408,355,0,1,1 +rust,root-help,169,3310.210,0.000,3204.000,6616,351,0,1,1 +go,root-help,169,4710.228,1551.000,4654.000,13436,805,0,62,0 +go,root-help,170,5501.105,4065.000,3049.000,13148,758,0,69,2 +rust,root-help,170,2473.307,1198.000,1198.000,6592,350,0,1,0 +rust,root-help,171,1663.153,0.000,1586.000,6556,349,0,1,0 +go,root-help,171,5043.204,957.000,5746.000,13436,829,0,74,0 +go,root-help,172,5537.995,1973.000,4934.000,13440,810,0,73,0 +rust,root-help,172,1878.870,0.000,1773.000,6720,348,0,1,0 +rust,root-help,173,1538.670,0.000,1438.000,6552,350,0,1,1 +go,root-help,173,5093.179,1799.000,4497.000,13404,794,0,57,0 +go,root-help,174,6158.961,1322.000,6612.000,13304,798,0,71,0 +rust,root-help,174,2275.004,895.000,895.000,6668,349,0,1,1 +rust,root-help,175,1883.949,0.000,1766.000,6560,350,0,1,0 +go,root-help,175,5853.657,1127.000,6763.000,13604,844,0,76,0 +go,root-help,176,7113.834,2562.000,6406.000,13564,840,0,83,0 +rust,root-help,176,2673.883,1300.000,1300.000,6724,350,0,1,0 +rust,root-help,177,2544.972,0.000,2475.000,6724,349,0,1,0 +go,root-help,177,5773.968,1087.000,6526.000,13416,813,0,63,0 +go,root-help,178,5325.464,3839.000,2879.000,13404,818,0,65,0 +rust,root-help,178,2250.468,0.000,2160.000,6588,350,0,1,1 +rust,root-help,179,2411.770,770.000,1540.000,6632,351,0,1,0 +go,root-help,179,5016.524,0.000,6393.000,13180,764,0,68,1 +go,root-help,180,6119.155,3851.000,3851.000,13404,831,0,78,1 +rust,root-help,180,2979.346,0.000,2365.000,6564,350,0,1,1 +rust,root-help,181,2353.782,1133.000,1133.000,6784,352,0,1,0 +go,root-help,181,4947.725,3422.000,3422.000,13604,864,0,74,0 +go,root-help,182,5873.274,2954.000,3939.000,13404,821,0,72,1 +rust,root-help,182,2122.888,0.000,2062.000,6500,350,0,1,0 +rust,root-help,183,2299.650,1118.000,1118.000,6792,349,0,1,1 +go,root-help,183,4884.636,2069.000,4139.000,13420,812,0,68,0 +go,root-help,184,4350.252,2368.000,3158.000,13228,788,0,58,0 +rust,root-help,184,2382.676,1164.000,1164.000,6596,350,0,1,0 +rust,root-help,185,1966.104,948.000,948.000,6592,350,0,1,0 +go,root-help,185,4762.808,2334.000,3502.000,13476,808,0,69,0 +go,root-help,186,4604.310,4303.000,1434.000,13436,808,0,67,1 +rust,root-help,186,2523.380,1210.000,1210.000,6604,350,0,1,0 +rust,root-help,187,1933.582,0.000,1834.000,6712,350,0,1,0 +go,root-help,187,4645.737,2424.000,3636.000,13292,815,0,62,1 +go,root-help,188,5696.282,1229.000,6149.000,13180,772,0,72,0 +rust,root-help,188,2535.043,814.000,1629.000,6596,350,0,1,0 +rust,root-help,189,2324.868,0.000,2215.000,6580,349,0,1,0 +go,root-help,189,5995.644,4843.000,2905.000,13176,759,0,67,1 +go,root-help,190,5641.909,6440.000,1073.000,13300,793,0,61,0 +rust,root-help,190,2833.704,0.000,2147.000,6388,356,0,1,1 +rust,root-help,191,2239.869,1067.000,1067.000,6576,350,0,1,0 +go,root-help,191,6231.947,2126.000,5317.000,13180,782,0,83,1 +go,root-help,192,6161.977,1282.000,6414.000,13340,817,0,72,0 +rust,root-help,192,2659.285,869.000,1738.000,6816,351,0,1,0 +rust,root-help,193,2090.146,1021.000,1021.000,6616,351,0,1,0 +go,root-help,193,5794.545,3166.000,4221.000,13164,757,0,79,0 +go,root-help,194,4614.108,2927.000,2927.000,13420,809,0,67,0 +rust,root-help,194,2442.549,0.000,2377.000,6640,348,0,1,0 +rust,root-help,195,2823.172,2714.000,0.000,6700,350,0,1,0 +go,root-help,195,4573.783,3031.000,3031.000,13288,788,0,68,0 +go,root-help,196,5695.140,3631.000,3631.000,13288,790,0,73,0 +rust,root-help,196,2183.603,0.000,2064.000,6856,351,0,1,1 +rust,root-help,197,2228.977,714.000,1429.000,6676,350,0,1,1 +go,root-help,197,4560.287,3531.000,2354.000,13432,818,0,63,0 +go,root-help,198,6630.616,4659.000,3494.000,13404,803,0,84,0 +rust,root-help,198,2780.945,0.000,2709.000,6664,349,0,1,0 +rust,root-help,199,2845.286,1372.000,1372.000,6720,350,0,1,0 +go,root-help,199,5083.890,2795.000,3727.000,13404,843,0,70,0 +go,root-help,200,5084.122,1306.000,5226.000,13404,800,0,63,0 +rust,root-help,200,3884.598,0.000,3822.000,6580,351,0,0,1 +rust,root-help,201,1977.666,0.000,1883.000,6620,350,0,1,0 +go,root-help,201,4868.257,1234.000,4937.000,13236,789,0,51,2 +go,root-help,202,10478.786,4831.000,8455.000,13436,832,0,93,0 +rust,root-help,202,4253.692,2095.000,2095.000,6556,349,0,1,0 +rust,root-help,203,3760.244,917.000,2753.000,6572,351,0,1,0 +go,root-help,203,7692.542,4187.000,5234.000,13348,817,0,93,0 +go,root-help,204,8920.147,5719.000,5719.000,13480,848,0,89,0 +rust,root-help,204,3943.228,2525.000,1262.000,6708,350,0,1,0 +rust,root-help,205,3010.587,0.000,2840.000,6608,350,0,1,1 +go,root-help,205,9417.902,5133.000,6159.000,13340,833,0,97,2 +go,root-help,206,9669.646,3032.000,9098.000,13404,804,0,81,0 +rust,root-help,206,2919.776,0.000,2839.000,6704,351,0,1,0 +rust,root-help,207,2727.534,1311.000,1311.000,6504,352,0,1,0 +go,root-help,207,6916.243,2999.000,5999.000,13340,826,0,78,0 +go,root-help,208,6562.479,2457.000,6142.000,13228,828,0,81,0 +rust,root-help,208,3743.303,898.000,2694.000,6520,350,0,1,1 +rust,root-help,209,2479.428,0.000,2328.000,6684,350,0,1,0 +go,root-help,209,9586.489,3499.000,7874.000,13500,830,0,98,1 +go,root-help,210,5870.579,1757.000,6150.000,13436,820,0,66,0 +rust,root-help,210,3717.183,1186.000,2373.000,6600,350,0,1,1 +rust,root-help,211,3908.563,2486.000,1243.000,6696,350,0,1,0 +go,root-help,211,8473.268,4597.000,5746.000,13436,817,0,82,0 +go,root-help,212,8189.125,5763.000,4802.000,13288,807,0,78,0 +rust,root-help,212,3378.287,0.000,3250.000,6552,350,0,1,0 +rust,root-help,213,3595.716,3453.000,0.000,6548,350,0,1,0 +go,root-help,213,5822.679,5087.000,2543.000,13128,758,0,66,0 +go,root-help,214,8540.684,4733.000,5916.000,13180,754,0,91,0 +rust,root-help,214,2249.065,1081.000,1081.000,6764,350,0,1,0 +rust,root-help,215,2112.850,0.000,1999.000,6732,351,0,1,1 +go,root-help,215,8786.577,6090.000,4872.000,13532,846,0,70,0 +go,root-help,216,7629.954,4914.000,4914.000,13344,805,0,70,0 +rust,root-help,216,3680.455,982.000,1964.000,6692,349,0,1,1 +rust,root-help,217,3400.268,2210.000,1105.000,6644,349,0,1,0 +go,root-help,217,6005.302,3310.000,4413.000,13436,809,0,65,0 +go,root-help,218,7149.902,5517.000,3310.000,13600,837,0,80,0 +rust,root-help,218,2884.670,0.000,2752.000,6748,348,0,1,0 +rust,root-help,219,3240.968,0.000,3113.000,6668,351,0,1,0 +go,root-help,219,8124.724,9684.000,0.000,13472,832,0,87,2 +go,root-help,220,5655.856,4958.000,2479.000,13176,767,0,67,0 +rust,root-help,220,2904.908,0.000,2760.000,6860,353,0,1,0 +rust,root-help,221,3274.040,1054.000,2108.000,6532,348,0,1,1 +go,root-help,221,8662.865,3593.000,6289.000,13340,804,0,81,2 +go,root-help,222,7541.899,4825.000,4825.000,13148,775,0,78,1 +rust,root-help,222,3082.401,0.000,2977.000,6632,348,0,1,1 +rust,root-help,223,3301.302,0.000,2567.000,6464,353,0,1,1 +go,root-help,223,8041.057,3223.000,6447.000,13348,807,0,59,0 +go,root-help,224,6393.811,5367.000,2683.000,13472,822,0,72,0 +rust,root-help,224,4069.826,981.000,2944.000,6620,349,0,1,0 +rust,root-help,225,2225.400,1056.000,1056.000,6708,350,0,1,0 +go,root-help,225,9276.939,5406.000,6487.000,13432,804,0,80,0 +go,root-help,226,5775.089,3807.000,3807.000,13432,810,0,70,0 +rust,root-help,226,3775.693,1207.000,2415.000,6684,352,0,1,0 +rust,root-help,227,3394.797,0.000,3288.000,6680,349,0,1,0 +go,root-help,227,6207.322,1413.000,7068.000,13304,783,0,64,0 +go,root-help,228,9140.932,5233.000,6280.000,13560,834,0,83,1 +rust,root-help,228,3450.482,0.000,3337.000,6280,351,0,1,0 +rust,root-help,229,3643.956,1123.000,2247.000,6672,350,0,1,0 +go,root-help,229,6959.114,3595.000,4793.000,13244,810,0,69,0 +go,root-help,230,10026.426,3683.000,8594.000,13404,815,0,91,0 +rust,root-help,230,2734.306,0.000,2606.000,6332,355,0,1,0 +rust,root-help,231,2674.845,1719.000,859.000,6636,349,0,1,0 +go,root-help,231,5934.980,2228.000,5571.000,13432,810,0,70,0 +go,root-help,232,6585.924,2086.000,6259.000,13436,807,0,78,0 +rust,root-help,232,3010.767,0.000,2927.000,6540,348,0,1,0 +rust,root-help,233,2812.924,0.000,2716.000,6696,350,0,1,0 +go,root-help,233,6628.724,3550.000,5325.000,13436,820,0,76,0 +go,root-help,234,6011.654,2425.000,5659.000,13180,755,0,71,0 +rust,root-help,234,2798.096,1342.000,1342.000,6724,349,0,1,0 +rust,root-help,235,2060.451,0.000,2001.000,6564,349,0,1,0 +go,root-help,235,6050.276,3617.000,4823.000,13148,776,0,68,2 +go,root-help,236,8722.136,4795.000,5994.000,13468,824,0,86,0 +rust,root-help,236,3417.701,1094.000,2189.000,6580,349,0,1,0 +rust,root-help,237,2518.953,1197.000,1197.000,6684,350,0,1,0 +go,root-help,237,9995.457,3826.000,8608.000,13564,836,0,88,1 +go,root-help,238,8696.918,4916.000,6145.000,13276,779,0,86,0 +rust,root-help,238,3842.319,0.000,3706.000,6664,350,0,1,1 +rust,root-help,239,2607.969,1221.000,1221.000,6680,350,0,1,0 +go,root-help,239,8832.973,4962.000,6202.000,13396,800,0,81,0 +go,root-help,240,8728.118,3375.000,6751.000,13468,827,0,77,2 +rust,root-help,240,3841.707,1847.000,1847.000,6472,349,0,1,1 +rust,root-help,241,3132.095,0.000,2980.000,6684,349,0,0,0 +go,root-help,241,8708.881,2397.000,8391.000,13184,747,0,93,0 +go,root-help,242,7246.052,2396.000,7189.000,13500,837,0,74,0 +rust,root-help,242,2371.725,0.000,2307.000,6588,351,0,1,0 +rust,root-help,243,3050.902,994.000,1988.000,6728,349,0,1,0 +go,root-help,243,8884.782,3712.000,7424.000,13440,815,0,92,0 +go,root-help,244,9026.858,7154.000,4088.000,13164,773,0,94,0 +rust,root-help,244,2203.801,2104.000,0.000,6612,350,0,1,0 +rust,root-help,245,3770.293,911.000,2735.000,6676,349,0,1,0 +go,root-help,245,8396.915,5625.000,5625.000,13220,766,0,89,0 +go,root-help,246,5640.485,1161.000,6966.000,13440,824,0,67,1 +rust,root-help,246,2892.604,938.000,1876.000,6572,350,0,1,0 +rust,root-help,247,2535.444,1219.000,1219.000,6744,350,0,1,0 +go,root-help,247,9493.184,4596.000,6435.000,13476,816,0,83,2 +go,root-help,248,6310.455,3670.000,4587.000,13564,819,0,69,0 +rust,root-help,248,2532.247,1220.000,1220.000,6748,350,0,1,0 +rust,root-help,249,2484.036,0.000,2392.000,6732,349,0,1,1 +go,root-help,249,7980.052,3765.000,6589.000,13308,805,0,90,1 +go,root-help,250,7247.065,1220.000,8542.000,12968,773,0,78,0 +rust,root-help,250,3159.475,0.000,3040.000,6664,350,0,1,1 +rust,root-help,251,2462.807,1150.000,1150.000,6592,350,0,1,1 +go,root-help,251,7714.774,6239.000,4159.000,13084,771,0,86,0 +go,root-help,252,7768.925,3816.000,6360.000,13176,774,0,80,1 +rust,root-help,252,2974.558,0.000,2859.000,6556,350,0,1,0 +rust,root-help,253,3243.624,1045.000,2091.000,6672,353,0,1,0 +go,root-help,253,4756.255,1688.000,5065.000,13344,809,0,64,0 +go,root-help,254,5513.888,4494.000,2996.000,13472,833,0,65,1 +rust,root-help,254,3680.304,0.000,3604.000,6852,351,0,1,0 +rust,root-help,255,3510.976,0.000,3429.000,6692,351,0,1,0 +go,root-help,255,8000.230,5626.000,4501.000,13392,814,0,82,2 +go,root-help,256,8199.463,5402.000,4322.000,13436,819,0,83,0 +rust,root-help,256,3686.315,2348.000,1174.000,6628,350,0,1,1 +rust,root-help,257,1904.979,914.000,914.000,6684,350,0,1,0 +go,root-help,257,4877.031,2103.000,4207.000,13436,815,0,54,0 +go,root-help,258,5547.052,4237.000,3178.000,13276,819,0,71,0 +rust,root-help,258,3755.115,981.000,1962.000,6716,349,0,1,1 +rust,root-help,259,2250.497,0.000,2081.000,6600,350,0,1,0 +go,root-help,259,5235.485,4404.000,3303.000,13476,811,0,72,4 +go,root-help,260,5728.502,5081.000,2540.000,13476,797,0,67,2 +rust,root-help,260,3459.690,1131.000,2263.000,6684,349,0,1,0 +rust,root-help,261,2729.528,1081.000,1081.000,6724,351,0,1,1 +go,root-help,261,9423.574,6470.000,5391.000,13348,809,0,80,0 +go,root-help,262,5101.383,2137.000,4275.000,13184,769,0,64,0 +rust,root-help,262,2353.342,0.000,2243.000,6796,351,0,1,0 +rust,root-help,263,1948.971,0.000,1838.000,6240,355,0,1,0 +go,root-help,263,7087.356,3816.000,6361.000,13308,804,0,75,0 +go,root-help,264,6482.789,5346.000,2673.000,13368,830,0,80,1 +rust,root-help,264,1859.815,0.000,1771.000,6560,352,0,1,0 +rust,root-help,265,2435.264,771.000,1543.000,6412,351,0,1,0 +go,root-help,265,7844.658,5180.000,5180.000,13348,792,0,74,0 +go,root-help,266,7890.795,7077.000,2830.000,13108,784,0,80,0 +rust,root-help,266,3198.328,774.000,2323.000,6560,349,0,1,1 +rust,root-help,267,2431.307,0.000,2335.000,6684,349,0,1,0 +go,root-help,267,5019.571,2507.000,4179.000,13160,764,0,66,0 +go,root-help,268,5329.031,4482.000,2241.000,13308,805,0,65,0 +rust,root-help,268,2338.954,0.000,2257.000,6680,351,0,1,0 +rust,root-help,269,1688.573,0.000,1617.000,6732,351,0,1,0 +go,root-help,269,5095.353,6381.000,0.000,13432,794,0,61,0 +go,root-help,270,4810.557,1504.000,4513.000,13180,774,0,52,0 +rust,root-help,270,3064.107,0.000,2990.000,6684,350,0,1,1 +rust,root-help,271,1865.625,0.000,1766.000,6720,352,0,1,0 +go,root-help,271,4991.698,3231.000,3231.000,13532,848,0,56,0 +go,root-help,272,4698.587,1169.000,4678.000,13532,822,0,67,0 +rust,root-help,272,2002.973,970.000,970.000,6664,350,0,1,0 +rust,root-help,273,2675.937,1093.000,1093.000,6664,352,0,1,1 +go,root-help,273,6288.944,1708.000,5980.000,13488,841,0,60,0 +go,root-help,274,5236.789,2093.000,4187.000,13164,768,0,59,2 +rust,root-help,274,2917.091,0.000,2846.000,6652,349,0,1,0 +rust,root-help,275,2001.269,0.000,1928.000,6604,349,0,1,0 +go,root-help,275,6802.981,5135.000,3081.000,13468,829,0,69,0 +go,root-help,276,5414.062,986.000,5920.000,13404,807,0,63,0 +rust,root-help,276,2365.223,1148.000,1148.000,6424,350,0,1,0 +rust,root-help,277,2406.360,0.000,2347.000,6472,350,0,1,0 +go,root-help,277,5802.060,2795.000,4659.000,13564,836,0,63,0 +go,root-help,278,7862.491,2982.000,6958.000,13216,772,0,86,0 +rust,root-help,278,2770.696,902.000,1804.000,6732,351,0,1,0 +rust,root-help,279,1732.655,0.000,1633.000,6624,350,0,1,0 +go,root-help,279,6867.281,3753.000,4691.000,13560,819,0,76,2 +go,root-help,280,5551.649,2033.000,5083.000,13244,807,0,66,0 +rust,root-help,280,2121.135,0.000,2052.000,6572,350,0,1,1 +rust,root-help,281,3046.234,0.000,2969.000,6476,349,0,1,0 +go,root-help,281,5356.022,2705.000,4057.000,13436,801,0,59,0 +go,root-help,282,4323.242,1399.000,4199.000,13276,780,0,53,0 +rust,root-help,282,3765.514,924.000,2773.000,6716,349,0,1,0 +rust,root-help,283,3510.285,0.000,3437.000,6548,349,0,1,0 +go,root-help,283,5461.992,3340.000,3340.000,13148,753,0,57,1 +go,root-help,284,4786.832,2948.000,2948.000,13220,746,0,59,0 +rust,root-help,284,2227.955,0.000,2138.000,6608,351,0,1,0 +rust,root-help,285,1900.731,0.000,1813.000,6592,350,0,1,0 +go,root-help,285,5168.289,4260.000,2130.000,13276,798,0,65,0 +go,root-help,286,4893.783,1759.000,4397.000,13404,816,0,66,0 +rust,root-help,286,2005.428,967.000,967.000,6344,350,0,1,1 +rust,root-help,287,2417.522,1177.000,1177.000,6736,349,0,1,0 +go,root-help,287,4440.412,2185.000,3278.000,13404,797,0,56,0 +go,root-help,288,5462.362,2195.000,4390.000,13164,753,0,67,0 +rust,root-help,288,2010.417,965.000,965.000,6656,351,0,1,0 +rust,root-help,289,2180.657,0.000,2121.000,6800,350,0,1,0 +go,root-help,289,4684.590,1762.000,4405.000,13472,822,0,70,1 +go,root-help,290,5148.262,1178.000,4715.000,13428,838,0,66,0 +rust,root-help,290,1917.061,926.000,926.000,6352,356,0,1,1 +rust,root-help,291,1609.243,0.000,1551.000,6752,349,0,1,0 +go,root-help,291,4378.917,2704.000,2704.000,13180,761,0,61,0 +go,root-help,292,4653.202,4595.000,1148.000,13392,819,0,70,1 +rust,root-help,292,2044.831,993.000,993.000,6732,351,0,1,0 +rust,root-help,293,1997.362,968.000,968.000,6640,350,0,1,0 +go,root-help,293,4532.485,2318.000,3477.000,13436,818,0,61,0 +go,root-help,294,4523.428,3427.000,2284.000,13404,798,0,64,0 +rust,root-help,294,2308.787,0.000,2244.000,6552,352,0,1,0 +rust,root-help,295,1660.559,795.000,795.000,6548,350,0,1,0 +go,root-help,295,5223.112,1979.000,3959.000,13476,811,0,67,2 +go,root-help,296,5491.677,2138.000,4277.000,13308,813,0,64,1 +rust,root-help,296,2303.777,0.000,2238.000,6744,350,0,1,0 +rust,root-help,297,1822.103,882.000,882.000,6608,350,0,1,0 +go,root-help,297,4566.608,2787.000,2787.000,13084,750,0,68,0 +go,root-help,298,4512.477,0.000,5490.000,13308,792,0,63,0 +rust,root-help,298,1901.212,1836.000,0.000,6600,350,0,1,0 +rust,root-help,299,1927.921,931.000,931.000,6408,352,0,1,0 +go,root-help,299,4844.710,1225.000,4902.000,13404,804,0,64,0 +go,episodes-create-help,0,5623.895,2031.000,5079.000,13340,810,0,74,0 +rust,episodes-create-help,0,2365.784,0.000,2292.000,6716,353,0,1,0 +rust,episodes-create-help,1,2795.121,1792.000,896.000,6792,351,0,1,0 +go,episodes-create-help,1,6439.628,1976.000,5929.000,13336,829,0,78,0 +go,episodes-create-help,2,6046.890,2404.000,4809.000,13420,826,0,67,1 +rust,episodes-create-help,2,2269.463,0.000,2194.000,6752,353,0,1,0 +rust,episodes-create-help,3,1682.310,0.000,1619.000,6632,350,0,1,0 +go,episodes-create-help,3,5037.795,2754.000,3672.000,13276,786,0,68,0 +go,episodes-create-help,4,5787.182,1824.000,5473.000,13436,804,0,76,0 +rust,episodes-create-help,4,2131.934,1002.000,1002.000,6748,351,0,1,0 +rust,episodes-create-help,5,2044.992,964.000,964.000,6728,351,0,1,0 +go,episodes-create-help,5,5808.863,2452.000,4904.000,13148,750,0,77,0 +go,episodes-create-help,6,5166.787,3999.000,2666.000,13568,816,0,68,0 +rust,episodes-create-help,6,3617.436,0.000,3502.000,6744,352,0,1,0 +rust,episodes-create-help,7,2288.209,0.000,2181.000,6724,350,0,1,0 +go,episodes-create-help,7,8714.200,5059.000,6070.000,13476,793,0,75,1 +go,episodes-create-help,8,6087.466,3449.000,4599.000,13404,790,0,68,0 +rust,episodes-create-help,8,3121.444,0.000,3003.000,6712,350,0,1,1 +rust,episodes-create-help,9,3276.566,1564.000,1564.000,6764,349,0,1,0 +go,episodes-create-help,9,8182.934,5651.000,4521.000,13476,813,0,83,0 +go,episodes-create-help,10,5826.065,4489.000,3367.000,13020,774,0,71,0 +rust,episodes-create-help,10,3099.774,1000.000,2001.000,6644,350,0,1,0 +rust,episodes-create-help,11,4248.591,2061.000,2061.000,6816,351,0,1,0 +go,episodes-create-help,11,5069.213,1599.000,5596.000,13340,791,0,67,0 +go,episodes-create-help,12,8857.069,2744.000,8234.000,13544,836,0,85,0 +rust,episodes-create-help,12,2967.977,956.000,1913.000,6780,351,0,1,1 +rust,episodes-create-help,13,2399.218,0.000,2311.000,6748,352,0,1,0 +go,episodes-create-help,13,5697.724,2500.000,5000.000,13340,812,0,69,0 +go,episodes-create-help,14,6161.464,5830.000,2332.000,13440,823,0,77,2 +rust,episodes-create-help,14,3244.445,0.000,3144.000,6876,353,0,0,1 +rust,episodes-create-help,15,2836.238,1348.000,1348.000,6408,357,0,1,1 +go,episodes-create-help,15,5863.206,5054.000,2527.000,13308,784,0,69,0 +go,episodes-create-help,16,4583.951,836.000,5021.000,13176,762,0,59,0 +rust,episodes-create-help,16,1871.085,888.000,888.000,6680,354,0,1,1 +rust,episodes-create-help,17,2417.010,1170.000,1170.000,6628,350,0,1,0 +go,episodes-create-help,17,4748.571,3688.000,2459.000,13396,806,0,66,0 +go,episodes-create-help,18,4920.965,2546.000,3819.000,13356,821,0,62,4 +rust,episodes-create-help,18,2374.251,0.000,1903.000,6624,352,0,1,1 +rust,episodes-create-help,19,3968.747,973.000,2919.000,6652,352,0,1,0 +go,episodes-create-help,19,6099.158,0.000,7899.000,13148,759,0,81,0 +go,episodes-create-help,20,4872.755,3893.000,2595.000,13532,790,0,66,0 +rust,episodes-create-help,20,2141.904,1031.000,1031.000,6728,351,0,1,0 +rust,episodes-create-help,21,2358.611,1136.000,1136.000,6600,350,0,1,0 +go,episodes-create-help,21,4374.239,1370.000,4111.000,13180,750,0,61,0 +go,episodes-create-help,22,4961.020,2328.000,3881.000,13428,820,0,60,0 +rust,episodes-create-help,22,1986.532,0.000,1899.000,6504,358,0,1,0 +rust,episodes-create-help,23,2225.851,0.000,2180.000,6852,351,0,1,0 +go,episodes-create-help,23,4437.246,2400.000,3200.000,13476,823,0,67,0 +go,episodes-create-help,24,4542.815,4249.000,1416.000,13604,826,0,61,2 +rust,episodes-create-help,24,1928.552,937.000,937.000,6684,355,0,1,0 +rust,episodes-create-help,25,1685.776,0.000,1630.000,6716,352,0,1,0 +go,episodes-create-help,25,5057.702,3071.000,3071.000,13352,792,0,57,0 +go,episodes-create-help,26,5344.109,4045.000,2697.000,13548,838,0,74,0 +rust,episodes-create-help,26,2341.890,1130.000,1130.000,6832,352,0,1,0 +rust,episodes-create-help,27,1996.822,971.000,971.000,6812,353,0,1,0 +go,episodes-create-help,27,4393.113,3445.000,2296.000,13292,781,0,57,0 +go,episodes-create-help,28,7681.261,1075.000,8604.000,13300,785,0,89,0 +rust,episodes-create-help,28,2066.442,0.000,2025.000,6736,351,0,1,0 +rust,episodes-create-help,29,3696.745,897.000,2692.000,6536,354,0,1,0 +go,episodes-create-help,29,8360.226,4681.000,5852.000,13432,817,0,82,2 +go,episodes-create-help,30,8506.109,3179.000,7419.000,13420,821,0,90,0 +rust,episodes-create-help,30,3497.541,2258.000,1129.000,6644,351,0,1,0 +rust,episodes-create-help,31,3654.356,886.000,2658.000,6688,351,0,1,0 +go,episodes-create-help,31,7980.724,4066.000,6099.000,13568,809,0,84,1 +go,episodes-create-help,32,9454.390,4593.000,6890.000,13404,839,0,87,0 +rust,episodes-create-help,32,3213.878,0.000,3105.000,6800,355,0,1,0 +rust,episodes-create-help,33,2532.798,1225.000,1225.000,6776,352,0,1,0 +go,episodes-create-help,33,9752.621,4044.000,10111.000,13308,818,0,84,0 +go,episodes-create-help,34,6083.499,1171.000,7031.000,13432,828,0,65,1 +rust,episodes-create-help,34,4193.268,1028.000,3085.000,6856,353,0,1,0 +rust,episodes-create-help,35,2618.348,839.000,1678.000,6768,350,0,1,0 +go,episodes-create-help,35,6565.114,3245.000,5680.000,13176,749,0,67,2 +go,episodes-create-help,36,6189.027,2227.000,5569.000,13404,801,0,73,0 +rust,episodes-create-help,36,2806.753,0.000,2739.000,6700,350,0,1,0 +rust,episodes-create-help,37,2579.707,0.000,2503.000,6672,350,0,1,0 +go,episodes-create-help,37,6634.574,4818.000,3613.000,13340,806,0,69,0 +go,episodes-create-help,38,7909.629,5998.000,4498.000,13176,768,0,74,0 +rust,episodes-create-help,38,3878.287,0.000,3805.000,6496,358,0,1,0 +rust,episodes-create-help,39,2572.783,0.000,2469.000,6640,352,0,1,0 +go,episodes-create-help,39,7291.598,4850.000,4850.000,13628,862,0,86,0 +go,episodes-create-help,40,6894.272,5871.000,2935.000,13356,828,0,74,1 +rust,episodes-create-help,40,2536.534,0.000,2424.000,6720,350,0,1,0 +rust,episodes-create-help,41,4488.452,1095.000,3286.000,6760,349,0,1,0 +go,episodes-create-help,41,6907.988,7891.000,1127.000,13404,838,0,73,0 +go,episodes-create-help,42,9119.331,4542.000,6813.000,13344,809,0,91,1 +rust,episodes-create-help,42,3400.779,0.000,3326.000,6776,351,0,1,0 +rust,episodes-create-help,43,2830.538,0.000,2745.000,6800,350,0,1,0 +go,episodes-create-help,43,9089.847,4209.000,7365.000,13420,815,0,92,0 +go,episodes-create-help,44,6839.248,2846.000,5693.000,13568,835,0,68,0 +rust,episodes-create-help,44,3051.132,0.000,2910.000,6776,352,0,1,1 +rust,episodes-create-help,45,3319.496,1074.000,2148.000,6688,351,0,1,0 +go,episodes-create-help,45,6125.448,2011.000,6033.000,13488,791,0,79,0 +go,episodes-create-help,46,5597.966,3560.000,3560.000,13180,757,0,65,0 +rust,episodes-create-help,46,2599.284,0.000,2417.000,6624,354,0,1,0 +rust,episodes-create-help,47,2094.635,995.000,995.000,6788,353,0,1,0 +go,episodes-create-help,47,5459.837,2676.000,4014.000,13584,843,0,64,0 +go,episodes-create-help,48,7109.076,2984.000,5969.000,13148,748,0,77,0 +rust,episodes-create-help,48,2135.612,1017.000,1017.000,6792,350,0,1,1 +rust,episodes-create-help,49,3330.136,811.000,2434.000,6740,351,0,1,0 +go,episodes-create-help,49,4736.537,3622.000,2414.000,13420,810,0,68,0 +go,episodes-create-help,50,4306.319,1174.000,4697.000,13560,849,0,59,0 +rust,episodes-create-help,50,2243.805,0.000,2184.000,6704,352,0,1,0 +rust,episodes-create-help,51,1815.630,883.000,883.000,6720,350,0,1,0 +go,episodes-create-help,51,4647.611,2270.000,3405.000,13304,819,0,60,0 +go,episodes-create-help,52,5217.582,0.000,7196.000,13564,857,0,52,0 +rust,episodes-create-help,52,2726.082,1312.000,1312.000,6688,351,0,1,0 +rust,episodes-create-help,53,2719.940,862.000,1724.000,6420,357,0,1,0 +go,episodes-create-help,53,5069.364,1881.000,4703.000,13180,774,0,66,0 +go,episodes-create-help,54,4274.591,5619.000,0.000,13220,757,0,64,0 +rust,episodes-create-help,54,4612.294,2661.000,0.000,6788,353,0,1,0 +rust,episodes-create-help,55,2647.263,0.000,2503.000,6724,351,0,1,1 +go,episodes-create-help,55,5537.834,4207.000,2804.000,13248,762,0,71,0 +go,episodes-create-help,56,4783.147,0.000,5753.000,13184,759,0,57,1 +rust,episodes-create-help,56,2778.700,0.000,2695.000,6728,351,0,1,0 +rust,episodes-create-help,57,2045.513,0.000,1983.000,6772,352,0,1,0 +go,episodes-create-help,57,5139.566,2870.000,3827.000,13276,794,0,72,0 +go,episodes-create-help,58,4492.911,4082.000,1633.000,13476,792,0,64,0 +rust,episodes-create-help,58,1815.461,1758.000,0.000,6800,353,0,1,0 +rust,episodes-create-help,59,2158.545,0.000,2050.000,6476,359,0,1,0 +go,episodes-create-help,59,5148.642,3256.000,3256.000,13556,834,0,66,1 +go,episodes-create-help,60,4888.474,4764.000,1588.000,13532,840,0,66,0 +rust,episodes-create-help,60,2186.299,1062.000,1062.000,6672,350,0,1,0 +rust,episodes-create-help,61,1842.771,892.000,892.000,6684,355,0,1,0 +go,episodes-create-help,61,5612.434,3605.000,3605.000,13476,813,0,73,0 +go,episodes-create-help,62,5003.189,1643.000,4929.000,13476,833,0,65,1 +rust,episodes-create-help,62,1896.784,919.000,919.000,6792,352,0,1,0 +rust,episodes-create-help,63,2493.695,800.000,1600.000,6880,353,0,1,1 +go,episodes-create-help,63,5004.081,915.000,5493.000,13532,814,0,63,0 +go,episodes-create-help,64,5293.995,2798.000,3731.000,13292,762,0,61,0 +rust,episodes-create-help,64,2069.277,2014.000,0.000,6868,352,0,1,0 +rust,episodes-create-help,65,2330.398,1121.000,1121.000,6812,352,0,1,0 +go,episodes-create-help,65,4726.600,1814.000,4537.000,13436,802,0,61,0 +go,episodes-create-help,66,5462.171,999.000,5997.000,13464,838,0,68,1 +rust,episodes-create-help,66,2416.860,0.000,2317.000,6692,352,0,1,0 +rust,episodes-create-help,67,2138.016,0.000,2049.000,6696,351,0,1,0 +go,episodes-create-help,67,4828.190,1570.000,4710.000,13492,796,0,62,0 +go,episodes-create-help,68,5217.912,2719.000,4079.000,13436,829,0,68,0 +rust,episodes-create-help,68,2585.066,2443.000,0.000,6772,352,0,1,0 +rust,episodes-create-help,69,2555.672,806.000,1612.000,6800,351,0,1,0 +go,episodes-create-help,69,4655.927,0.000,5835.000,13292,794,0,59,0 +go,episodes-create-help,70,7037.191,3242.000,4322.000,13308,795,0,62,0 +rust,episodes-create-help,70,2434.793,771.000,1543.000,6864,352,0,1,0 +rust,episodes-create-help,71,3051.293,965.000,1930.000,6640,351,0,1,0 +go,episodes-create-help,71,5449.057,2066.000,5167.000,13436,822,0,68,0 +go,episodes-create-help,72,5826.766,1268.000,6341.000,13420,831,0,76,0 +rust,episodes-create-help,72,2428.371,784.000,1569.000,6600,351,0,1,0 +rust,episodes-create-help,73,2256.560,1065.000,1065.000,6712,352,0,1,0 +go,episodes-create-help,73,5508.298,2290.000,4580.000,13404,808,0,68,0 +go,episodes-create-help,74,5293.296,3432.000,3432.000,13436,827,0,74,0 +rust,episodes-create-help,74,2065.691,1981.000,0.000,6760,352,0,1,1 +rust,episodes-create-help,75,2304.198,0.000,2231.000,6732,351,0,1,0 +go,episodes-create-help,75,4854.691,2605.000,3474.000,13340,832,0,71,0 +go,episodes-create-help,76,4952.674,969.000,5819.000,13416,832,0,71,0 +rust,episodes-create-help,76,2277.679,0.000,2200.000,6704,350,0,1,1 +rust,episodes-create-help,77,1840.918,890.000,890.000,6872,352,0,1,0 +go,episodes-create-help,77,4825.306,2646.000,3970.000,13404,827,0,73,0 +go,episodes-create-help,78,4992.138,931.000,5587.000,13148,777,0,74,0 +rust,episodes-create-help,78,1951.517,0.000,1890.000,6648,351,0,1,0 +rust,episodes-create-help,79,2023.982,0.000,1931.000,6672,350,0,1,1 +go,episodes-create-help,79,5721.108,628.000,6909.000,13440,824,0,61,1 +go,episodes-create-help,80,5297.111,1093.000,5465.000,13300,740,0,64,0 +rust,episodes-create-help,80,3651.079,0.000,3539.000,6488,356,0,1,0 +rust,episodes-create-help,81,2684.753,0.000,2552.000,6684,351,0,1,1 +go,episodes-create-help,81,5339.171,3907.000,2930.000,13436,813,0,57,0 +go,episodes-create-help,82,5318.802,2006.000,5016.000,13340,821,0,69,1 +rust,episodes-create-help,82,2101.358,1011.000,1011.000,6748,350,0,1,0 +rust,episodes-create-help,83,2076.652,0.000,1985.000,6628,353,0,1,0 +go,episodes-create-help,83,5650.285,2872.000,4308.000,13548,855,0,73,0 +go,episodes-create-help,84,5751.345,2411.000,4822.000,13256,770,0,73,0 +rust,episodes-create-help,84,2574.136,0.000,2445.000,6692,354,0,1,0 +rust,episodes-create-help,85,1981.012,0.000,1841.000,6656,351,0,1,0 +go,episodes-create-help,85,5511.213,4015.000,3212.000,13436,808,0,74,0 +go,episodes-create-help,86,4984.595,1049.000,5246.000,13164,774,0,64,2 +rust,episodes-create-help,86,2143.077,0.000,2041.000,6712,350,0,1,0 +rust,episodes-create-help,87,3437.799,0.000,3312.000,6868,353,0,1,1 +go,episodes-create-help,87,6609.757,4128.000,3096.000,13180,759,0,57,0 +go,episodes-create-help,88,5497.097,1150.000,5751.000,13340,827,0,72,0 +rust,episodes-create-help,88,2306.092,1086.000,1086.000,6684,354,0,1,0 +rust,episodes-create-help,89,2241.801,1080.000,1080.000,6856,351,0,1,0 +go,episodes-create-help,89,5107.736,3097.000,3872.000,13288,790,0,68,0 +go,episodes-create-help,90,8602.813,3905.000,6835.000,13304,794,0,85,0 +rust,episodes-create-help,90,2409.977,0.000,2322.000,6724,350,0,1,1 +rust,episodes-create-help,91,1926.810,918.000,918.000,6760,349,0,1,0 +go,episodes-create-help,91,9292.898,7026.000,3513.000,13532,822,0,91,1 +go,episodes-create-help,92,5757.066,3369.000,4493.000,13172,771,0,74,0 +rust,episodes-create-help,92,3621.593,1757.000,1757.000,6360,358,0,1,1 +rust,episodes-create-help,93,1837.823,1728.000,0.000,6792,352,0,1,0 +go,episodes-create-help,93,4639.025,3683.000,2455.000,13176,753,0,50,0 +go,episodes-create-help,94,5116.984,3640.000,3640.000,13416,800,0,70,0 +rust,episodes-create-help,94,3315.789,1075.000,2151.000,6620,351,0,1,1 +rust,episodes-create-help,95,2435.666,0.000,2350.000,6736,351,0,1,0 +go,episodes-create-help,95,5224.725,1720.000,5160.000,13288,804,0,64,0 +go,episodes-create-help,96,7559.883,1288.000,6440.000,13180,756,0,60,0 +rust,episodes-create-help,96,2716.173,1747.000,873.000,6752,351,0,1,0 +rust,episodes-create-help,97,2674.263,864.000,1729.000,6876,351,0,1,0 +go,episodes-create-help,97,4975.217,1341.000,5367.000,13172,772,0,67,0 +go,episodes-create-help,98,5008.750,2821.000,4232.000,13164,777,0,61,2 +rust,episodes-create-help,98,2413.365,1161.000,1161.000,6800,351,0,1,0 +rust,episodes-create-help,99,3693.779,0.000,3610.000,6832,349,0,1,0 +go,episodes-create-help,99,4388.204,1477.000,4433.000,13308,797,0,58,0 +go,episodes-create-help,100,4771.032,2299.000,3449.000,13348,794,0,66,0 +rust,episodes-create-help,100,2472.646,0.000,2049.000,6880,352,0,1,1 +rust,episodes-create-help,101,2357.809,1134.000,1134.000,6672,355,0,1,0 +go,episodes-create-help,101,4993.290,2520.000,3780.000,13436,853,0,77,0 +go,episodes-create-help,102,4512.408,4255.000,1418.000,13496,831,0,69,0 +rust,episodes-create-help,102,1701.387,0.000,1649.000,6768,350,0,1,0 +rust,episodes-create-help,103,1498.375,0.000,1450.000,6940,352,0,1,0 +go,episodes-create-help,103,4643.654,2307.000,3461.000,13180,759,0,63,0 +go,episodes-create-help,104,4362.395,1078.000,4313.000,13468,836,0,62,0 +rust,episodes-create-help,104,2571.831,1252.000,1252.000,6656,351,0,1,0 +rust,episodes-create-help,105,1967.747,950.000,950.000,6804,350,0,1,0 +go,episodes-create-help,105,5375.840,1900.000,4750.000,13420,820,0,65,1 +go,episodes-create-help,106,5137.671,1061.000,5308.000,13216,747,0,61,0 +rust,episodes-create-help,106,1874.121,0.000,1817.000,6888,353,0,1,1 +rust,episodes-create-help,107,1495.249,0.000,1450.000,6504,356,0,1,0 +go,episodes-create-help,107,4050.129,5277.000,0.000,13292,793,0,55,0 +go,episodes-create-help,108,4059.707,1310.000,3932.000,13600,850,0,59,0 +rust,episodes-create-help,108,1889.470,0.000,1840.000,6872,352,0,1,0 +rust,episodes-create-help,109,1818.736,0.000,1753.000,6744,355,0,1,0 +go,episodes-create-help,109,4300.941,2729.000,2729.000,13340,829,0,65,0 +go,episodes-create-help,110,4847.226,1056.000,5280.000,13532,845,0,63,0 +rust,episodes-create-help,110,1806.012,0.000,1741.000,6720,350,0,1,0 +rust,episodes-create-help,111,2199.942,0.000,2121.000,6852,352,0,1,0 +go,episodes-create-help,111,6495.292,1634.000,6538.000,13344,809,0,69,0 +go,episodes-create-help,112,6096.203,2195.000,5489.000,13404,808,0,72,0 +rust,episodes-create-help,112,1729.419,0.000,1670.000,6796,351,0,1,0 +rust,episodes-create-help,113,2304.630,1115.000,1115.000,6752,350,0,1,0 +go,episodes-create-help,113,6865.288,2435.000,6087.000,13180,767,0,79,0 +go,episodes-create-help,114,5750.503,4483.000,2989.000,13532,791,0,67,0 +rust,episodes-create-help,114,2282.788,1100.000,1100.000,6668,352,0,1,0 +rust,episodes-create-help,115,2420.076,0.000,2368.000,6816,350,0,1,0 +go,episodes-create-help,115,5352.917,2738.000,4107.000,13164,745,0,58,0 +go,episodes-create-help,116,5771.212,4259.000,3194.000,13436,806,0,69,0 +rust,episodes-create-help,116,2345.546,0.000,2264.000,6808,350,0,1,0 +rust,episodes-create-help,117,3946.705,0.000,3871.000,6640,351,0,1,0 +go,episodes-create-help,117,5532.303,5495.000,1373.000,13164,756,0,68,0 +go,episodes-create-help,118,5298.814,1132.000,5661.000,13404,800,0,67,0 +rust,episodes-create-help,118,2042.989,0.000,1962.000,6876,350,0,1,0 +rust,episodes-create-help,119,3490.327,2243.000,1121.000,6832,352,0,1,0 +go,episodes-create-help,119,5420.523,2226.000,4453.000,13556,807,0,69,0 +go,episodes-create-help,120,5161.577,1284.000,5137.000,13436,808,0,68,0 +rust,episodes-create-help,120,2332.732,1130.000,1130.000,6872,351,0,1,1 +rust,episodes-create-help,121,2080.038,1992.000,0.000,6856,352,0,1,0 +go,episodes-create-help,121,5836.424,3326.000,3326.000,13176,774,0,69,1 +go,episodes-create-help,122,4823.873,3482.000,2611.000,13420,821,0,61,0 +rust,episodes-create-help,122,2165.148,2072.000,0.000,6672,350,0,1,0 +rust,episodes-create-help,123,2126.435,1019.000,1019.000,6736,350,0,1,0 +go,episodes-create-help,123,5904.803,2119.000,5299.000,13436,815,0,73,0 +go,episodes-create-help,124,5762.936,5080.000,2032.000,13212,807,0,70,0 +rust,episodes-create-help,124,3542.034,1154.000,2309.000,6876,352,0,1,0 +rust,episodes-create-help,125,3128.307,1472.000,1472.000,6760,353,0,1,0 +go,episodes-create-help,125,5218.203,0.000,7100.000,13420,817,0,65,0 +go,episodes-create-help,126,5259.090,3427.000,3427.000,13428,819,0,67,0 +rust,episodes-create-help,126,2340.597,1139.000,1139.000,6780,351,0,1,0 +rust,episodes-create-help,127,1947.619,1855.000,0.000,6740,352,0,1,0 +go,episodes-create-help,127,4583.001,2521.000,3361.000,13112,775,0,67,0 +go,episodes-create-help,128,4644.354,1672.000,4181.000,13276,814,0,67,0 +rust,episodes-create-help,128,1737.574,0.000,1685.000,6688,351,0,1,0 +rust,episodes-create-help,129,1912.814,930.000,930.000,6600,353,0,1,0 +go,episodes-create-help,129,5593.889,3931.000,2948.000,13476,823,0,70,1 +go,episodes-create-help,130,4896.779,1781.000,4454.000,13440,811,0,73,0 +rust,episodes-create-help,130,2072.563,0.000,1965.000,6700,351,0,1,0 +rust,episodes-create-help,131,2603.762,1240.000,1240.000,6796,351,0,1,0 +go,episodes-create-help,131,5254.491,2637.000,3956.000,13432,830,0,68,0 +go,episodes-create-help,132,5041.442,5014.000,1253.000,13404,815,0,78,0 +rust,episodes-create-help,132,2164.606,0.000,2089.000,6680,355,0,1,0 +rust,episodes-create-help,133,2128.731,832.000,832.000,6676,351,0,1,1 +go,episodes-create-help,133,4768.007,4199.000,2099.000,13532,848,0,67,1 +go,episodes-create-help,134,4581.417,1149.000,4596.000,13476,806,0,62,0 +rust,episodes-create-help,134,2356.847,1134.000,1134.000,6844,350,0,1,0 +rust,episodes-create-help,135,1865.446,888.000,888.000,6944,352,0,1,0 +go,episodes-create-help,135,4763.910,2041.000,4083.000,13176,766,0,57,0 +go,episodes-create-help,136,5417.949,1183.000,5918.000,13216,772,0,67,0 +rust,episodes-create-help,136,2279.743,1112.000,1112.000,6796,349,0,1,0 +rust,episodes-create-help,137,2441.827,0.000,2375.000,6744,351,0,1,0 +go,episodes-create-help,137,4623.486,2897.000,2897.000,13432,804,0,64,0 +go,episodes-create-help,138,4840.262,0.000,6068.000,13352,825,0,69,1 +rust,episodes-create-help,138,2850.676,0.000,2288.000,6820,352,0,1,1 +rust,episodes-create-help,139,2370.132,1136.000,1136.000,6664,351,0,1,0 +go,episodes-create-help,139,4461.282,1902.000,3804.000,13428,804,0,67,0 +go,episodes-create-help,140,4343.410,3378.000,2252.000,13404,809,0,62,0 +rust,episodes-create-help,140,2789.169,0.000,2724.000,6404,356,0,1,0 +rust,episodes-create-help,141,2031.717,0.000,1961.000,6716,351,0,1,0 +go,episodes-create-help,141,4494.754,0.000,5583.000,13276,796,0,66,0 +go,episodes-create-help,142,5648.032,2782.000,4636.000,13432,818,0,64,3 +rust,episodes-create-help,142,3833.692,1871.000,1871.000,6816,353,0,1,1 +rust,episodes-create-help,143,2725.692,0.000,2625.000,6816,352,0,1,0 +go,episodes-create-help,143,7852.552,2483.000,7450.000,13532,856,0,85,0 +go,episodes-create-help,144,9191.547,6535.000,4668.000,13568,839,0,93,0 +rust,episodes-create-help,144,3483.115,0.000,3389.000,6824,352,0,1,0 +rust,episodes-create-help,145,3046.322,0.000,2961.000,6556,351,0,1,0 +go,episodes-create-help,145,5668.971,2602.000,5205.000,13564,843,0,71,0 +go,episodes-create-help,146,7781.008,4036.000,6054.000,13164,747,0,72,0 +rust,episodes-create-help,146,3494.655,846.000,2538.000,6728,356,0,1,1 +rust,episodes-create-help,147,2136.273,0.000,2029.000,6616,351,0,1,0 +go,episodes-create-help,147,8674.236,5234.000,5234.000,13428,810,0,93,0 +go,episodes-create-help,148,6826.566,4404.000,4404.000,13432,806,0,77,0 +rust,episodes-create-help,148,3560.038,872.000,2617.000,6688,353,0,1,0 +rust,episodes-create-help,149,2171.430,1029.000,1029.000,6344,358,0,1,0 +go,episodes-create-help,149,8997.363,5277.000,6332.000,13176,766,0,83,0 +go,episodes-create-help,150,6945.919,3341.000,5569.000,13176,762,0,65,0 +rust,episodes-create-help,150,2218.428,0.000,2148.000,6840,350,0,1,1 +rust,episodes-create-help,151,2148.976,1034.000,1034.000,6876,354,0,1,0 +go,episodes-create-help,151,7661.563,5221.000,4177.000,13432,810,0,82,0 +go,episodes-create-help,152,5194.559,1882.000,4705.000,13556,836,0,57,0 +rust,episodes-create-help,152,2193.961,0.000,2066.000,6692,350,0,1,0 +rust,episodes-create-help,153,2655.199,0.000,2533.000,6716,353,0,1,0 +go,episodes-create-help,153,4206.573,2785.000,2785.000,13164,758,0,55,0 +go,episodes-create-help,154,4907.620,1182.000,4728.000,13180,746,0,58,2 +rust,episodes-create-help,154,1939.544,0.000,1861.000,6504,358,0,1,1 +rust,episodes-create-help,155,1741.091,0.000,1672.000,6688,355,0,1,0 +go,episodes-create-help,155,7227.558,4047.000,5059.000,13564,836,0,73,0 +go,episodes-create-help,156,6610.459,4306.000,4306.000,13308,787,0,66,0 +rust,episodes-create-help,156,2724.498,0.000,2661.000,6652,351,0,1,0 +rust,episodes-create-help,157,2543.579,1231.000,1231.000,6340,358,0,1,0 +go,episodes-create-help,157,7017.583,2939.000,5879.000,13160,749,0,67,2 +go,episodes-create-help,158,9144.379,4967.000,6209.000,13604,843,0,86,0 +rust,episodes-create-help,158,2807.864,0.000,2682.000,6532,354,0,1,0 +rust,episodes-create-help,159,2009.856,0.000,1921.000,6832,353,0,1,0 +go,episodes-create-help,159,7138.732,4552.000,3414.000,13436,803,0,78,0 +go,episodes-create-help,160,4851.724,1187.000,4750.000,13548,835,0,66,0 +rust,episodes-create-help,160,4114.200,2008.000,2008.000,6492,357,0,1,1 +rust,episodes-create-help,161,2355.676,0.000,2267.000,6608,351,0,1,0 +go,episodes-create-help,161,10026.026,6378.000,5315.000,13420,797,0,101,0 +go,episodes-create-help,162,9565.751,3568.000,4757.000,13564,836,0,76,0 +rust,episodes-create-help,162,2287.897,737.000,1474.000,6744,351,0,1,0 +rust,episodes-create-help,163,2665.587,0.000,2544.000,6724,350,0,1,0 +go,episodes-create-help,163,8039.032,1237.000,9897.000,13400,819,0,72,1 +go,episodes-create-help,164,7482.517,1907.000,8581.000,13544,857,0,57,2 +rust,episodes-create-help,164,3182.769,1017.000,2035.000,6480,357,0,1,0 +rust,episodes-create-help,165,2751.348,1753.000,876.000,6792,351,0,1,0 +go,episodes-create-help,165,6155.704,4698.000,2819.000,13420,820,0,64,1 +go,episodes-create-help,166,5521.743,962.000,5777.000,13216,750,0,68,0 +rust,episodes-create-help,166,2529.251,1222.000,1222.000,6876,350,0,1,1 +rust,episodes-create-help,167,2288.590,0.000,2207.000,6836,351,0,1,0 +go,episodes-create-help,167,5526.362,3344.000,3344.000,13176,770,0,72,0 +go,episodes-create-help,168,7211.608,4124.000,5774.000,13212,775,0,77,1 +rust,episodes-create-help,168,2454.882,789.000,1578.000,6804,351,0,1,1 +rust,episodes-create-help,169,2561.343,0.000,2474.000,6720,350,0,1,0 +go,episodes-create-help,169,6052.731,2910.000,4851.000,13564,831,0,68,0 +go,episodes-create-help,170,5911.144,1204.000,6022.000,13440,808,0,64,0 +rust,episodes-create-help,170,2546.004,1637.000,818.000,6792,350,0,1,1 +rust,episodes-create-help,171,2297.756,0.000,2199.000,6872,353,0,1,1 +go,episodes-create-help,171,6188.075,3994.000,3994.000,13428,778,0,68,0 +go,episodes-create-help,172,4935.241,1548.000,4646.000,13160,760,0,69,0 +rust,episodes-create-help,172,2059.518,988.000,988.000,6668,355,0,1,0 +rust,episodes-create-help,173,1974.229,946.000,946.000,6844,353,0,1,0 +go,episodes-create-help,173,5217.001,2428.000,4047.000,13404,845,0,73,0 +go,episodes-create-help,174,4793.254,5991.000,0.000,13440,807,0,65,0 +rust,episodes-create-help,174,1946.026,0.000,1884.000,6872,351,0,1,0 +rust,episodes-create-help,175,1718.398,0.000,1663.000,6708,351,0,1,0 +go,episodes-create-help,175,4262.448,1388.000,4164.000,13472,826,0,66,0 +go,episodes-create-help,176,4965.478,1060.000,5304.000,13172,778,0,68,0 +rust,episodes-create-help,176,2041.645,0.000,1974.000,6408,356,0,1,0 +rust,episodes-create-help,177,1787.868,0.000,1727.000,6736,351,0,1,0 +go,episodes-create-help,177,4684.773,1757.000,4393.000,13600,819,0,58,1 +go,episodes-create-help,178,4521.824,3363.000,2242.000,13548,839,0,67,0 +rust,episodes-create-help,178,1923.073,934.000,934.000,6688,352,0,1,0 +rust,episodes-create-help,179,1796.875,0.000,1732.000,6828,351,0,1,0 +go,episodes-create-help,179,4625.620,3410.000,2273.000,13436,802,0,69,0 +go,episodes-create-help,180,4687.105,2958.000,2958.000,13400,836,0,67,0 +rust,episodes-create-help,180,1857.510,1787.000,0.000,6728,352,0,1,0 +rust,episodes-create-help,181,2202.058,0.000,2126.000,6716,350,0,1,0 +go,episodes-create-help,181,4559.035,1660.000,4151.000,13420,808,0,61,0 +go,episodes-create-help,182,5475.637,2233.000,4466.000,13416,805,0,72,0 +rust,episodes-create-help,182,1866.096,909.000,909.000,6896,352,0,1,0 +rust,episodes-create-help,183,1590.438,1542.000,0.000,6720,351,0,1,0 +go,episodes-create-help,183,3908.033,2643.000,2643.000,13396,827,0,63,0 +go,episodes-create-help,184,4011.816,2146.000,3219.000,13432,813,0,62,1 +rust,episodes-create-help,184,1805.031,872.000,872.000,6556,351,0,1,1 +rust,episodes-create-help,185,2173.854,1064.000,1064.000,6712,351,0,1,0 +go,episodes-create-help,185,4188.849,1280.000,3840.000,13472,806,0,64,1 +go,episodes-create-help,186,4493.863,2294.000,3441.000,13432,818,0,62,1 +rust,episodes-create-help,186,4218.344,2082.000,2082.000,6824,349,0,1,0 +rust,episodes-create-help,187,1941.267,0.000,1896.000,6824,352,0,1,0 +go,episodes-create-help,187,3973.895,2496.000,2496.000,13432,799,0,64,0 +go,episodes-create-help,188,3929.342,3867.000,966.000,13148,775,0,63,0 +rust,episodes-create-help,188,1666.952,0.000,1620.000,6776,351,0,1,0 +rust,episodes-create-help,189,1568.386,1516.000,0.000,6408,356,0,1,0 +go,episodes-create-help,189,4205.349,2453.000,2453.000,13548,834,0,55,3 +go,episodes-create-help,190,3878.597,2916.000,1944.000,13304,793,0,55,0 +rust,episodes-create-help,190,3588.971,0.000,3536.000,6876,352,0,1,0 +rust,episodes-create-help,191,1880.904,904.000,904.000,6704,352,0,1,0 +go,episodes-create-help,191,8034.043,2728.000,7276.000,13232,833,0,94,3 +go,episodes-create-help,192,9029.635,5202.000,5202.000,13420,842,0,95,0 +rust,episodes-create-help,192,3338.602,2167.000,1083.000,6732,351,0,1,0 +rust,episodes-create-help,193,1632.588,1549.000,0.000,6876,352,0,1,0 +go,episodes-create-help,193,7940.617,4647.000,4647.000,13428,810,0,84,0 +go,episodes-create-help,194,8338.685,6935.000,3467.000,13180,766,0,89,0 +rust,episodes-create-help,194,3016.067,0.000,2891.000,6856,350,0,1,1 +rust,episodes-create-help,195,3861.234,939.000,2819.000,6632,351,0,1,1 +go,episodes-create-help,195,6212.781,2731.000,5463.000,13392,802,0,70,0 +go,episodes-create-help,196,6406.395,1821.000,6376.000,13340,808,0,70,0 +rust,episodes-create-help,196,2619.370,1270.000,1270.000,6880,351,0,1,0 +rust,episodes-create-help,197,3698.128,1808.000,1808.000,6816,353,0,1,0 +go,episodes-create-help,197,6448.003,2501.000,6254.000,13184,771,0,74,0 +go,episodes-create-help,198,8192.911,4010.000,7018.000,12988,773,0,78,0 +rust,episodes-create-help,198,3617.056,1180.000,2360.000,6716,351,0,1,0 +rust,episodes-create-help,199,2874.460,0.000,2767.000,6712,352,0,1,0 +go,episodes-create-help,199,8156.272,4648.000,5810.000,13148,775,0,75,2 +go,episodes-create-help,200,6248.891,2362.000,5905.000,13288,783,0,60,0 +rust,episodes-create-help,200,2441.205,0.000,2339.000,6784,352,0,1,0 +rust,episodes-create-help,201,4022.186,2932.000,977.000,6784,351,0,1,0 +go,episodes-create-help,201,9300.652,6740.000,5616.000,13548,858,0,87,0 +go,episodes-create-help,202,6768.977,4509.000,4509.000,12972,752,0,79,0 +rust,episodes-create-help,202,4059.597,1992.000,1992.000,6652,351,0,1,0 +rust,episodes-create-help,203,4419.903,934.000,2804.000,6804,352,0,1,1 +go,episodes-create-help,203,7848.895,4888.000,4888.000,13564,830,0,71,0 +go,episodes-create-help,204,5961.130,2256.000,5641.000,13488,824,0,72,2 +rust,episodes-create-help,204,2209.222,0.000,2124.000,6868,351,0,1,0 +rust,episodes-create-help,205,3669.342,2389.000,1194.000,6408,356,0,1,0 +go,episodes-create-help,205,5848.929,2974.000,3966.000,13364,802,0,64,0 +go,episodes-create-help,206,5785.429,1816.000,5448.000,13248,811,0,70,0 +rust,episodes-create-help,206,2012.962,956.000,956.000,6944,353,0,1,0 +rust,episodes-create-help,207,3345.755,0.000,2741.000,6732,353,0,1,1 +go,episodes-create-help,207,4800.980,4603.000,1534.000,13428,827,0,68,0 +go,episodes-create-help,208,7732.777,5355.000,4284.000,13148,766,0,76,0 +rust,episodes-create-help,208,3656.538,881.000,2645.000,6572,350,0,1,0 +rust,episodes-create-help,209,3772.948,1192.000,2384.000,6612,351,0,1,1 +go,episodes-create-help,209,8446.469,5143.000,5143.000,13564,825,0,86,1 +go,episodes-create-help,210,8952.559,2931.000,7816.000,13436,835,0,96,0 +rust,episodes-create-help,210,2568.045,0.000,2031.000,6876,351,0,1,1 +rust,episodes-create-help,211,3919.653,946.000,2838.000,6728,352,0,1,0 +go,episodes-create-help,211,9559.019,2371.000,8300.000,13432,814,0,92,2 +go,episodes-create-help,212,7505.581,1857.000,7431.000,13600,857,0,80,0 +rust,episodes-create-help,212,3723.355,1213.000,2427.000,6740,351,0,1,0 +rust,episodes-create-help,213,3385.610,1084.000,2169.000,6736,350,0,1,1 +go,episodes-create-help,213,7160.813,3353.000,5588.000,13544,838,0,75,0 +go,episodes-create-help,214,5586.234,1060.000,6362.000,13436,819,0,71,0 +rust,episodes-create-help,214,2485.350,1188.000,1188.000,6704,352,0,1,0 +rust,episodes-create-help,215,2139.879,1030.000,1030.000,6720,351,0,1,0 +go,episodes-create-help,215,7664.920,3751.000,5627.000,13336,809,0,88,1 +go,episodes-create-help,216,4612.405,857.000,5144.000,13164,754,0,63,0 +rust,episodes-create-help,216,1790.814,0.000,1745.000,6712,350,0,1,0 +rust,episodes-create-help,217,2317.624,747.000,1495.000,6696,351,0,1,0 +go,episodes-create-help,217,4241.829,2875.000,2875.000,13404,799,0,59,0 +go,episodes-create-help,218,4910.005,2987.000,2987.000,13560,838,0,64,0 +rust,episodes-create-help,218,2381.685,0.000,1916.000,6744,350,0,1,1 +rust,episodes-create-help,219,2455.544,0.000,2318.000,6660,351,0,1,1 +go,episodes-create-help,219,4666.076,3121.000,3121.000,13164,745,0,51,0 +go,episodes-create-help,220,5498.420,3025.000,4034.000,13304,816,0,66,0 +rust,episodes-create-help,220,2232.274,0.000,2143.000,6888,352,0,1,1 +rust,episodes-create-help,221,2431.438,0.000,1934.000,6748,350,0,1,1 +go,episodes-create-help,221,4479.805,4103.000,1641.000,13224,755,0,64,0 +go,episodes-create-help,222,6063.871,3708.000,3708.000,13420,819,0,74,0 +rust,episodes-create-help,222,3489.586,0.000,3402.000,6732,352,0,1,1 +rust,episodes-create-help,223,3112.978,2009.000,1004.000,6884,352,0,1,0 +go,episodes-create-help,223,7495.812,4909.000,4909.000,13436,827,0,90,0 +go,episodes-create-help,224,7497.594,5901.000,3540.000,13420,804,0,75,0 +rust,episodes-create-help,224,2444.333,1159.000,1159.000,6796,349,0,1,0 +rust,episodes-create-help,225,2571.992,0.000,2410.000,6492,357,0,1,0 +go,episodes-create-help,225,6581.274,3462.000,5193.000,13468,842,0,72,0 +go,episodes-create-help,226,5509.561,1222.000,6110.000,13564,836,0,68,0 +rust,episodes-create-help,226,4522.766,0.000,4398.000,6728,355,0,1,1 +rust,episodes-create-help,227,2184.133,0.000,2093.000,6600,350,0,1,0 +go,episodes-create-help,227,8473.129,4496.000,5620.000,13532,855,0,91,1 +go,episodes-create-help,228,8090.169,1020.000,9185.000,13484,834,0,83,1 +rust,episodes-create-help,228,2032.488,0.000,1948.000,6812,351,0,1,0 +rust,episodes-create-help,229,2006.690,0.000,1884.000,6616,351,0,1,0 +go,episodes-create-help,229,4911.267,1355.000,5421.000,13432,831,0,65,0 +go,episodes-create-help,230,9197.679,6957.000,3478.000,13432,812,0,84,3 +rust,episodes-create-help,230,3864.521,1252.000,2504.000,6668,353,0,1,0 +rust,episodes-create-help,231,3779.190,1215.000,2431.000,6688,352,0,1,1 +go,episodes-create-help,231,7680.609,983.000,8855.000,13172,761,0,73,0 +go,episodes-create-help,232,8432.581,5921.000,4737.000,13184,753,0,85,0 +rust,episodes-create-help,232,3465.961,1127.000,2255.000,6896,352,0,1,0 +rust,episodes-create-help,233,2868.427,1862.000,931.000,6700,351,0,1,0 +go,episodes-create-help,233,5729.924,1858.000,5574.000,13184,768,0,66,0 +go,episodes-create-help,234,6265.872,2096.000,4193.000,13532,806,0,60,0 +rust,episodes-create-help,234,2798.206,882.000,1765.000,6860,352,0,1,1 +rust,episodes-create-help,235,2662.361,1269.000,1269.000,6724,352,0,1,0 +go,episodes-create-help,235,5361.103,4016.000,2677.000,13404,821,0,67,0 +go,episodes-create-help,236,5289.197,2506.000,3759.000,13276,789,0,72,1 +rust,episodes-create-help,236,2186.447,0.000,2101.000,6800,351,0,1,0 +rust,episodes-create-help,237,2186.768,1046.000,1046.000,6840,350,0,1,0 +go,episodes-create-help,237,4360.231,1608.000,4021.000,13440,806,0,63,0 +go,episodes-create-help,238,4526.745,4252.000,1417.000,13420,821,0,68,0 +rust,episodes-create-help,238,2105.515,1013.000,1013.000,6792,350,0,1,1 +rust,episodes-create-help,239,2523.441,0.000,2440.000,6720,350,0,1,0 +go,episodes-create-help,239,5233.120,5275.000,1318.000,13548,831,0,74,2 +go,episodes-create-help,240,4902.310,3087.000,3087.000,13432,796,0,59,0 +rust,episodes-create-help,240,2055.972,0.000,2001.000,6496,356,0,1,0 +rust,episodes-create-help,241,1910.279,0.000,1844.000,6864,352,0,1,0 +go,episodes-create-help,241,4322.540,1351.000,4055.000,13340,819,0,67,0 +go,episodes-create-help,242,4688.197,3622.000,2414.000,13220,768,0,68,0 +rust,episodes-create-help,242,2299.640,623.000,1246.000,6936,353,0,1,1 +rust,episodes-create-help,243,2388.437,0.000,2328.000,6756,351,0,1,0 +go,episodes-create-help,243,4537.274,2945.000,2945.000,13600,854,0,53,1 +go,episodes-create-help,244,4426.887,939.000,4699.000,13416,824,0,64,0 +rust,episodes-create-help,244,2081.470,0.000,2009.000,6500,357,0,1,1 +rust,episodes-create-help,245,2425.907,0.000,2371.000,6668,352,0,1,0 +go,episodes-create-help,245,6988.689,2759.000,4139.000,13404,803,0,90,1 +go,episodes-create-help,246,5708.495,2561.000,3842.000,13548,847,0,66,1 +rust,episodes-create-help,246,2581.530,0.000,2525.000,6744,352,0,1,0 +rust,episodes-create-help,247,2809.979,0.000,2741.000,6792,352,0,1,1 +go,episodes-create-help,247,4718.424,0.000,5955.000,13492,803,0,67,1 +go,episodes-create-help,248,4787.164,925.000,5555.000,13404,812,0,64,0 +rust,episodes-create-help,248,2508.442,0.000,2410.000,6804,350,0,1,1 +rust,episodes-create-help,249,2380.083,1129.000,1129.000,6668,352,0,1,0 +go,episodes-create-help,249,6104.739,4323.000,3458.000,13308,809,0,69,0 +go,episodes-create-help,250,4413.902,2412.000,3217.000,13084,763,0,56,0 +rust,episodes-create-help,250,1948.181,0.000,1872.000,6792,352,0,1,0 +rust,episodes-create-help,251,1713.840,1645.000,0.000,6728,352,0,1,0 +go,episodes-create-help,251,5353.567,3421.000,3421.000,13556,835,0,80,0 +go,episodes-create-help,252,7716.006,5657.000,3771.000,13420,815,0,86,0 +rust,episodes-create-help,252,2842.701,0.000,2735.000,6756,351,0,1,1 +rust,episodes-create-help,253,3849.181,932.000,2798.000,6664,350,0,1,1 +go,episodes-create-help,253,6637.880,2801.000,5602.000,13340,820,0,70,0 +go,episodes-create-help,254,5366.442,3873.000,2904.000,13276,813,0,65,0 +rust,episodes-create-help,254,2485.138,795.000,1590.000,6528,351,0,1,1 +rust,episodes-create-help,255,3264.783,1053.000,2107.000,6488,356,0,1,0 +go,episodes-create-help,255,9883.639,5431.000,6517.000,13536,808,0,92,1 +go,episodes-create-help,256,6887.809,2270.000,5677.000,13420,808,0,73,2 +rust,episodes-create-help,256,3674.514,0.000,3604.000,6668,351,0,1,0 +rust,episodes-create-help,257,3627.524,2657.000,885.000,6856,352,0,1,0 +go,episodes-create-help,257,9184.775,6897.000,4598.000,13416,824,0,91,0 +go,episodes-create-help,258,5786.280,3284.000,4105.000,13436,827,0,67,0 +rust,episodes-create-help,258,2129.320,1024.000,1024.000,6808,351,0,1,0 +rust,episodes-create-help,259,2062.386,988.000,988.000,6700,351,0,1,0 +go,episodes-create-help,259,4422.028,943.000,4718.000,13392,795,0,64,0 +go,episodes-create-help,260,6709.665,2624.000,5248.000,13564,830,0,78,0 +rust,episodes-create-help,260,1905.170,911.000,911.000,6684,351,0,1,0 +rust,episodes-create-help,261,3236.359,0.000,3151.000,6808,354,0,1,0 +go,episodes-create-help,261,5426.645,2839.000,4259.000,13276,800,0,66,0 +go,episodes-create-help,262,5316.797,5536.000,1107.000,13440,795,0,61,0 +rust,episodes-create-help,262,2933.922,945.000,1890.000,6816,350,0,1,1 +rust,episodes-create-help,263,2059.831,0.000,1984.000,6832,352,0,1,0 +go,episodes-create-help,263,5836.164,2491.000,4983.000,13292,809,0,65,0 +go,episodes-create-help,264,6242.217,5164.000,2582.000,13416,810,0,79,0 +rust,episodes-create-help,264,2667.893,1284.000,1284.000,6816,351,0,1,0 +rust,episodes-create-help,265,1502.272,0.000,1443.000,6916,352,0,1,0 +go,episodes-create-help,265,5190.440,3953.000,2635.000,13236,749,0,66,0 +go,episodes-create-help,266,6421.243,3408.000,4260.000,13240,795,0,80,0 +rust,episodes-create-help,266,3376.675,824.000,2474.000,6632,351,0,1,0 +rust,episodes-create-help,267,1916.068,0.000,1854.000,6752,351,0,1,0 +go,episodes-create-help,267,7751.311,6777.000,2259.000,13172,762,0,79,1 +go,episodes-create-help,268,10719.990,6722.000,6722.000,13420,806,0,92,0 +rust,episodes-create-help,268,2901.761,0.000,2775.000,6828,351,0,1,0 +rust,episodes-create-help,269,2088.904,0.000,1929.000,6876,351,0,1,0 +go,episodes-create-help,269,6969.633,3392.000,5654.000,13432,820,0,84,0 +go,episodes-create-help,270,8699.924,7605.000,3802.000,13480,818,0,88,0 +rust,episodes-create-help,270,3511.648,1133.000,2267.000,6776,351,0,1,1 +rust,episodes-create-help,271,3619.120,0.000,3455.000,6676,352,0,1,0 +go,episodes-create-help,271,6030.280,3006.000,5011.000,13416,822,0,63,0 +go,episodes-create-help,272,8174.397,2016.000,8064.000,13436,819,0,76,0 +rust,episodes-create-help,272,3045.502,980.000,1960.000,6544,351,0,1,1 +rust,episodes-create-help,273,3168.302,1010.000,2021.000,6344,358,0,1,0 +go,episodes-create-help,273,6705.397,5824.000,2912.000,13148,765,0,64,0 +go,episodes-create-help,274,7990.561,3316.000,6632.000,13244,828,0,90,0 +rust,episodes-create-help,274,3113.249,0.000,3068.000,6828,351,0,1,0 +rust,episodes-create-help,275,4038.156,1659.000,1659.000,6668,349,0,1,1 +go,episodes-create-help,275,7986.994,4237.000,5649.000,13404,805,0,79,0 +go,episodes-create-help,276,9602.470,7613.000,4350.000,13368,788,0,95,0 +rust,episodes-create-help,276,2489.498,774.000,1548.000,6700,350,0,1,0 +rust,episodes-create-help,277,4216.501,0.000,3988.000,6732,351,0,1,1 +go,episodes-create-help,277,7192.241,6159.000,3695.000,13604,860,0,75,1 +go,episodes-create-help,278,5529.499,1065.000,6391.000,13416,826,0,68,0 +rust,episodes-create-help,278,2681.738,0.000,2596.000,6684,351,0,1,0 +rust,episodes-create-help,279,2365.413,0.000,2248.000,6476,359,0,1,0 +go,episodes-create-help,279,9374.390,4695.000,7043.000,13548,832,0,87,0 +go,episodes-create-help,280,9703.961,8223.000,3524.000,13440,800,0,81,0 +rust,episodes-create-help,280,2903.845,950.000,1900.000,6792,351,0,1,0 +rust,episodes-create-help,281,3522.477,1152.000,2305.000,6776,351,0,1,0 +go,episodes-create-help,281,5240.525,0.000,7050.000,13340,807,0,66,0 +go,episodes-create-help,282,5032.144,1371.000,5486.000,13600,840,0,66,0 +rust,episodes-create-help,282,2765.796,0.000,2686.000,6848,352,0,1,0 +rust,episodes-create-help,283,2880.903,0.000,2811.000,6736,352,0,1,0 +go,episodes-create-help,283,6019.680,1917.000,5753.000,13532,851,0,66,0 +go,episodes-create-help,284,6747.257,3376.000,5065.000,13404,814,0,68,1 +rust,episodes-create-help,284,2914.375,1904.000,952.000,6892,351,0,1,0 +rust,episodes-create-help,285,2687.830,0.000,2597.000,6496,356,0,1,1 +go,episodes-create-help,285,5022.276,5097.000,1274.000,13340,812,0,64,0 +go,episodes-create-help,286,5403.711,2400.000,4801.000,13340,827,0,71,0 +rust,episodes-create-help,286,2039.602,1950.000,0.000,6712,352,0,1,0 +rust,episodes-create-help,287,2963.989,0.000,2835.000,6792,352,0,1,1 +go,episodes-create-help,287,7638.610,4028.000,6042.000,13436,818,0,81,2 +go,episodes-create-help,288,7301.667,3184.000,6369.000,13148,749,0,74,0 +rust,episodes-create-help,288,3007.561,0.000,2905.000,6900,352,0,1,0 +rust,episodes-create-help,289,1863.120,0.000,1724.000,6788,352,0,1,0 +go,episodes-create-help,289,5632.232,2042.000,5106.000,13544,851,0,62,0 +go,episodes-create-help,290,4597.457,1484.000,4454.000,13304,772,0,54,0 +rust,episodes-create-help,290,1900.200,919.000,919.000,6780,351,0,1,0 +rust,episodes-create-help,291,3459.370,842.000,2527.000,6812,350,0,1,1 +go,episodes-create-help,291,6025.139,3160.000,4214.000,13472,862,0,69,0 +go,episodes-create-help,292,7717.499,3690.000,6151.000,13180,769,0,78,0 +rust,episodes-create-help,292,3400.900,2179.000,1089.000,6620,351,0,1,1 +rust,episodes-create-help,293,2873.328,919.000,1839.000,6776,352,0,1,0 +go,episodes-create-help,293,7217.078,0.000,8653.000,13548,827,0,91,0 +go,episodes-create-help,294,7297.178,7831.000,1118.000,13432,829,0,74,0 +rust,episodes-create-help,294,2564.858,820.000,1640.000,6648,351,0,1,0 +rust,episodes-create-help,295,2924.354,1868.000,934.000,6404,358,0,1,0 +go,episodes-create-help,295,5165.334,3404.000,3404.000,13180,769,0,66,0 +go,episodes-create-help,296,5317.089,3923.000,2615.000,13276,788,0,59,0 +rust,episodes-create-help,296,1986.882,950.000,950.000,6684,351,0,1,0 +rust,episodes-create-help,297,4006.016,0.000,3876.000,6708,350,0,1,0 +go,episodes-create-help,297,6062.730,4559.000,3039.000,13532,815,0,71,0 +go,episodes-create-help,298,6046.809,3607.000,4809.000,13312,803,0,69,0 +rust,episodes-create-help,298,3721.962,1201.000,2402.000,6692,351,0,1,1 +rust,episodes-create-help,299,2588.543,0.000,2503.000,6772,351,0,1,0 +go,episodes-create-help,299,5370.369,3019.000,4025.000,13436,798,0,67,0 diff --git a/bench/results/linux-amd64.json b/bench/results/linux-amd64.json new file mode 100644 index 0000000..c9ed665 --- /dev/null +++ b/bench/results/linux-amd64.json @@ -0,0 +1,322 @@ +[ + { + "implementation": "go", + "command": "root-help", + "samples": 300, + "cpu_percent": 126.77180301202564, + "metrics": { + "wall_us": { + "mean": 6341.147223333333, + "median": 6004.094499999999, + "p95": 9276.939, + "min": 3921.427, + "max": 10478.786, + "stddev": 1533.139337293172 + }, + "user_us": { + "mean": 3449.3766666666666, + "median": 3227.0, + "p95": 6381.0, + "min": 0.0, + "max": 9684.0, + "stddev": 1675.2967343495368 + }, + "system_us": { + "mean": 4589.41, + "median": 4537.0, + "p95": 7356.0, + "min": 0.0, + "max": 9687.0, + "stddev": 1764.9137491390336 + }, + "peak_rss_kib": { + "mean": 13359.8, + "median": 13404.0, + "p95": 13564.0, + "min": 12968.0, + "max": 13604.0, + "stddev": 136.66001609834532 + }, + "minor_faults": { + "mean": 804.0433333333333, + "median": 807.0, + "p95": 843.0, + "min": 746.0, + "max": 864.0, + "stddev": 26.731656431196992 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 71.17666666666666, + "median": 70.0, + "p95": 91.0, + "min": 45.0, + "max": 98.0, + "stddev": 10.672649884426807 + }, + "involuntary_context_switches": { + "mean": 0.47, + "median": 0.0, + "p95": 2.0, + "min": 0.0, + "max": 6.0, + "stddev": 0.9393792276462862 + }, + "total_cpu_us": { + "mean": 8038.786666666667, + "median": 7715.0, + "p95": 11438.0, + "min": 4873.0, + "max": 13286.0, + "stddev": 1906.8806223311994 + } + } + }, + { + "implementation": "rust", + "command": "root-help", + "samples": 300, + "cpu_percent": 95.45116754863707, + "metrics": { + "wall_us": { + "mean": 2690.85935, + "median": 2544.5655, + "p95": 3842.319, + "min": 1387.266, + "max": 5580.644, + "stddev": 693.7030734145224 + }, + "user_us": { + "mean": 638.1666666666666, + "median": 765.0, + "p95": 2095.0, + "min": 0.0, + "max": 3453.0, + "stddev": 718.4236903728112 + }, + "system_us": { + "mean": 1930.29, + "median": 1930.5, + "p95": 3337.0, + "min": 0.0, + "max": 3835.0, + "stddev": 797.9393497628752 + }, + "peak_rss_kib": { + "mean": 6620.133333333333, + "median": 6640.0, + "p95": 6792.0, + "min": 6228.0, + "max": 6860.0, + "stddev": 117.87126687855508 + }, + "minor_faults": { + "mean": 350.24666666666667, + "median": 350.0, + "p95": 353.0, + "min": 348.0, + "max": 358.0, + "stddev": 1.5510713143573451 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 0.9933333333333333, + "median": 1.0, + "p95": 1.0, + "min": 0.0, + "max": 1.0, + "stddev": 0.08137703743822469 + }, + "involuntary_context_switches": { + "mean": 0.20333333333333334, + "median": 0.0, + "p95": 1.0, + "min": 0.0, + "max": 1.0, + "stddev": 0.402478432824529 + }, + "total_cpu_us": { + "mean": 2568.4566666666665, + "median": 2394.0, + "p95": 3711.0, + "min": 1349.0, + "max": 4490.0, + "stddev": 660.8478756785777 + } + } + }, + { + "implementation": "go", + "command": "episodes-create-help", + "samples": 300, + "cpu_percent": 126.4117530395956, + "metrics": { + "wall_us": { + "mean": 6037.204993333334, + "median": 5562.034, + "p95": 9144.379, + "min": 3878.597, + "max": 10719.99, + "stddev": 1492.1623623526116 + }, + "user_us": { + "mean": 3111.5333333333333, + "median": 2913.0, + "p95": 5998.0, + "min": 0.0, + "max": 8223.0, + "stddev": 1667.6253682673723 + }, + "system_us": { + "mean": 4520.203333333333, + "median": 4515.0, + "p95": 7365.0, + "min": 0.0, + "max": 10111.0, + "stddev": 1715.937425623544 + }, + "peak_rss_kib": { + "mean": 13375.946666666667, + "median": 13416.0, + "p95": 13564.0, + "min": 12972.0, + "max": 13628.0, + "stddev": 136.32782971776362 + }, + "minor_faults": { + "mean": 805.6666666666666, + "median": 809.0, + "p95": 849.0, + "min": 740.0, + "max": 862.0, + "stddev": 28.671627477738724 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 70.58333333333333, + "median": 68.0, + "p95": 91.0, + "min": 50.0, + "max": 101.0, + "stddev": 9.655208726669535 + }, + "involuntary_context_switches": { + "mean": 0.28, + "median": 0.0, + "p95": 2.0, + "min": 0.0, + "max": 4.0, + "stddev": 0.649307323229917 + }, + "total_cpu_us": { + "mean": 7631.736666666667, + "median": 7116.0, + "p95": 11176.0, + "min": 4833.0, + "max": 14155.0, + "stddev": 1808.948906774932 + } + } + }, + { + "implementation": "rust", + "command": "episodes-create-help", + "samples": 300, + "cpu_percent": 95.63121746636804, + "metrics": { + "wall_us": { + "mean": 2613.5294166666667, + "median": 2435.2295000000004, + "p95": 3919.653, + "min": 1495.249, + "max": 4612.294, + "stddev": 686.079782003456 + }, + "user_us": { + "mean": 664.0133333333333, + "median": 808.5, + "p95": 1992.0, + "min": 0.0, + "max": 2932.0, + "stddev": 705.4151967616109 + }, + "system_us": { + "mean": 1835.3366666666666, + "median": 1901.5, + "p95": 3286.0, + "min": 0.0, + "max": 4398.0, + "stddev": 837.4302856490337 + }, + "peak_rss_kib": { + "mean": 6725.626666666667, + "median": 6734.0, + "p95": 6876.0, + "min": 6340.0, + "max": 6944.0, + "stddev": 118.40999657498892 + }, + "minor_faults": { + "mean": 351.9033333333333, + "median": 351.0, + "p95": 357.0, + "min": 349.0, + "max": 359.0, + "stddev": 2.010138193148808 + }, + "major_faults": { + "mean": 0.0, + "median": 0.0, + "p95": 0.0, + "min": 0.0, + "max": 0.0, + "stddev": 0.0 + }, + "voluntary_context_switches": { + "mean": 0.9966666666666667, + "median": 1.0, + "p95": 1.0, + "min": 0.0, + "max": 1.0, + "stddev": 0.057638721552635275 + }, + "involuntary_context_switches": { + "mean": 0.22, + "median": 0.0, + "p95": 1.0, + "min": 0.0, + "max": 1.0, + "stddev": 0.4142463035441596 + }, + "total_cpu_us": { + "mean": 2499.35, + "median": 2322.0, + "p95": 3756.0, + "min": 1443.0, + "max": 4398.0, + "stddev": 661.338567477607 + } + } + } +] diff --git a/bench/startup.c b/bench/startup.c index 006dd52..ae214fd 100644 --- a/bench/startup.c +++ b/bench/startup.c @@ -1,4 +1,4 @@ -// Linux startup benchmark: no shell, HTTP, JSON, config, or terminal rendering. +// Linux startup benchmark: no shell, HTTP, JSON response decoding, or terminal rendering. // Build: cc -O2 -Wall -Wextra -Werror bench/startup.c -o /tmp/cli-startup // Run: taskset -c CPU /tmp/cli-startup /absolute/go-cli /absolute/rust-cli > samples.csv #define _GNU_SOURCE diff --git a/bench/summarize.py b/bench/summarize.py new file mode 100644 index 0000000..4e2372f --- /dev/null +++ b/bench/summarize.py @@ -0,0 +1,41 @@ +"""Summarize startup.c CSV output using only the Python standard library.""" +import csv +import json +import math +import statistics +import sys +from collections import defaultdict + + +def distribution(values): + values = sorted(values) + return { + "mean": statistics.mean(values), + "median": statistics.median(values), + "p95": values[math.ceil(len(values) * 0.95) - 1], + "min": values[0], + "max": values[-1], + "stddev": statistics.pstdev(values), + } + + +groups = defaultdict(list) +with open(sys.argv[1], newline="", encoding="utf-8") as source: + for row in csv.DictReader(source): + groups[(row["implementation"], row["command"])].append(row) +results = [] +for (implementation, command), rows in groups.items(): + metrics = { + field: distribution([float(row[field]) for row in rows]) + for field in rows[0] + if field not in ("implementation", "command", "sample") + } + cpu = [float(row["user_us"]) + float(row["system_us"]) for row in rows] + metrics["total_cpu_us"] = distribution(cpu) + # Ratio of totals avoids averaging unstable percentages for short processes. + cpu_percent = 100 * sum(cpu) / sum(float(row["wall_us"]) for row in rows) + results.append({ + "implementation": implementation, "command": command, + "samples": len(rows), "cpu_percent": cpu_percent, "metrics": metrics, + }) +print(json.dumps(results, indent=2)) From caaec943677ce8fc4b32713b12389213d8445b7f Mon Sep 17 00:00:00 2001 From: adelnizamutdinov Date: Thu, 24 Sep 2026 15:41:52 +0000 Subject: [PATCH 3/6] Use native Moon CI selection and publish reports --- .github/workflows/ci.yaml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a99ff3c..d5d2bc9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,6 +12,7 @@ jobs: - uses: actions/checkout@v7 with: fetch-depth: 0 + filter: blob:none - uses: actions/setup-node@v6 with: node-version: "26" @@ -34,4 +35,23 @@ jobs: ~/.pkgx key: rust-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml', 'prepare-ffmpeg.ts') }} - run: nub i --frozen-lockfile - - run: moon run cli:check + - name: Check affected tasks + id: ci + run: moon ci + - name: Report Moon CI results + if: "!cancelled() && steps.ci.outcome != 'skipped'" + uses: moonrepo/run-report-action@v1 + with: + access-token: ${{ secrets.GITHUB_TOKEN }} + skip-comment: true + - name: Upload Moon reports + if: always() + uses: actions/upload-artifact@v7 + with: + name: moon-reports-${{ github.run_attempt }} + path: | + .moon/cache/ciReport.json + .moon/cache/runReport.json + if-no-files-found: ignore + include-hidden-files: true + retention-days: 14 From 324cf1bc047d5f6f694f0c5a2a832611768be948 Mon Sep 17 00:00:00 2001 From: adelnizamutdinov Date: Thu, 24 Sep 2026 15:56:20 +0000 Subject: [PATCH 4/6] Replace help timings with complete YouTube import benchmarks --- README.md | 6 +- bench/README.md | 148 ++-- bench/measure.c | 52 ++ bench/results/binaries.json | 14 - bench/results/environment.json | 20 +- bench/results/linux-amd64-cpu7.csv | 1201 --------------------------- bench/results/linux-amd64-cpu7.json | 322 ------- bench/results/linux-amd64.csv | 1201 --------------------------- bench/results/linux-amd64.json | 322 ------- bench/results/youtube-import.json | 420 ++++++++++ bench/startup.c | 68 -- bench/summarize.py | 88 +- moon.yml | 10 + 13 files changed, 612 insertions(+), 3260 deletions(-) create mode 100644 bench/measure.c delete mode 100644 bench/results/binaries.json delete mode 100644 bench/results/linux-amd64-cpu7.csv delete mode 100644 bench/results/linux-amd64-cpu7.json delete mode 100644 bench/results/linux-amd64.csv delete mode 100644 bench/results/linux-amd64.json create mode 100644 bench/results/youtube-import.json delete mode 100644 bench/startup.c diff --git a/README.md b/README.md index b8fdf5d..f314d22 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,6 @@ used in E2E tests. Tests supply an isolated YAML config with their dynamic servi origins and `print_trace_ids: true`; no test-only executable or Cargo feature is required. -The [startup and footprint report](bench/README.md) compares this implementation -against Go with statically linked go-astiav and kkdai/youtube, with raw wall-time, -CPU, RSS, and binary-size measurements. +The [YouTube import benchmark](bench/README.md) compares this implementation +against Go with statically linked go-astiav and kkdai/youtube, measuring the full +import's wall time, CPU time, CPU utilization, peak RAM, and binary size. diff --git a/bench/README.md b/bench/README.md index 09b8943..5dd7eff 100644 --- a/bench/README.md +++ b/bench/README.md @@ -1,101 +1,69 @@ -# CLI startup and footprint comparison +# YouTube import benchmark -Measured 2026-09-24 on Linux x86-64. These are measurements of two complete CLI -implementations, not a general claim about Go versus Rust. +One command, from process launch through successful publication: -The Go baseline is [3d0f569](https://github.com/listenbox/cli/commit/3d0f56927a9fc0ae8a97d1cb64d209505ea2ae14) -from [the go-astiav PR](https://github.com/listenbox/cli/pull/7), including -**go-astiav v0.43.0 and kkdai/youtube v2.10.6**. It statically links FFmpeg; -this comparison does not use the earlier CLI with FFmpeg subprocesses. -The Rust executable is [5bd42d5](https://github.com/listenbox/cli/commit/5bd42d509f33ae08bb6ef5cd21546cf3943bdaca), -including **ffmpeg-the-third 6.0.0, YouTube.js 18.0.0 and rquickjs 0.11.0**. - -## Normal CPU affinity (all 8 vCPUs) - -| Command | Build | Wall p50 / p95 (µs) | Mean user / system CPU (µs) | Mean total CPU (µs) | CPU utilization | Peak RSS p50 / max (KiB) | -|---|---|---:|---:|---:|---:|---:| -| `--help` | Go | 6,004 / 9,277 | 3,449 / 4,589 | 8,039 | 126.8% | 13,404 / 13,604 | -| `--help` | Rust | 2,545 / 3,842 | 638 / 1,930 | 2,568 | 95.5% | 6,640 / 6,860 | -| `episodes create --help` | Go | 5,562 / 9,144 | 3,112 / 4,520 | 7,632 | 126.4% | 13,416 / 13,628 | -| `episodes create --help` | Rust | 2,435 / 3,920 | 664 / 1,835 | 2,499 | 95.6% | 6,734 / 6,944 | - -Rust's root-help median wall time is 57.6% lower, -mean total CPU time is 68.0% lower, -and median peak RSS is 50.5% lower. - -## Binary size +```sh +listenbox import --slug 'https://www.youtube.com/watch?v=w' +``` -| Artifact | Go + go-astiav + kkdai/youtube | Rust + ffmpeg-the-third + YouTube.js/rquickjs | +| Metric | Go | Rust | |---|---:|---:| -| Stripped executable | 22,639,472 bytes (21.59 MiB) | 20,525,240 bytes (19.57 MiB) | -| Executable compressed with gzip level 9 | 8,728,187 bytes | 8,141,703 bytes | - -The Rust executable is 9.3% smaller uncompressed. -Both include their media and extraction implementations even though help does not -initialize or execute them. Neither needs FFmpeg, FFprobe, Node, or Bun executables -at runtime. FFmpeg is static; platform C libraries are dynamic. The Go binding -also requires avdevice, avfilter, and swscale libraries at build time. Rust only -requests its used FFmpeg libraries. Codec/source selections otherwise match. - -## Controlled single-CPU run (CPU 7) - -| Command | Build | Wall p50 / p95 (µs) | Mean user / system CPU (µs) | Mean total CPU (µs) | CPU utilization | Peak RSS p50 / max (KiB) | -|---|---|---:|---:|---:|---:|---:| -| `--help` | Go | 4,622 / 5,913 | 1,953 / 2,462 | 4,415 | 93.5% | 13,176 / 13,276 | -| `--help` | Rust | 1,960 / 2,629 | 563 / 1,283 | 1,846 | 92.8% | 6,632 / 6,940 | -| `episodes create --help` | Go | 4,344 / 5,545 | 1,937 / 2,202 | 4,140 | 93.6% | 13,300 / 13,348 | -| `episodes create --help` | Rust | 1,849 / 2,520 | 480 / 1,242 | 1,722 | 91.8% | 6,758 / 6,988 | - -The same process affinity applies to each child. The normal-affinity run above -retains each runtime's ordinary CPU discovery and thread behavior. - -## Method - -- AMD EPYC-Rome KVM guest, 8 vCPUs; Linux 7.0.0-29-generic. Full environment, - compiler versions, optimization flags and dependencies: [environment.json](results/environment.json). -- Go: CGO enabled, `go build -trimpath -ldflags="-s -w"`; Rust: - `cargo build --locked --release`, opt-level 3, thin LTO, symbols stripped. - Both use checksum-pinned FFmpeg 9.0.2, compiled with GCC 15.2.0. -- [`startup.c`](startup.c) directly uses `posix_spawn`, `CLOCK_MONOTONIC`, and - `wait4`. Wall time covers spawn through process exit, including loading, - runtime initialization, argument parsing, and help generation. It includes the - common spawn/wait overhead; it is not a timestamp inside `main`. -- No HTTP requests, JSON response decoding, media processing, or terminal - rendering. Both programs run identical argument vectors; stdout/stderr and stdin - use `/dev/null`. The Go implementation also decodes its embedded YAML defaults - on the help path; Rust has typed compiled defaults and exits from Clap help. -- 30 discarded warmups then 300 samples per binary per command, alternating Go - and Rust order each iteration. Two commands and two affinity settings yield - 2,400 measured processes. Page/file caches are warm; no cache flushing. -- CPU time is per-child user plus system time from `wait4`, excluding the harness. - CPU utilization is total CPU time divided by total wall time, so a process - using multiple threads can exceed 100%. For these short processes, individual - user/system accounting samples can be zero; their means and combined CPU time - are more useful than separate medians. Values are reported in microseconds, - not interpreted as nanosecond measurement precision. -- RAM is per-process maximum resident set size (`ru_maxrss` on Linux), not heap - allocation, virtual address space, or media-import peak memory. RSS includes - resident shared pages; it is not proportional set size. -- Benchmarks ran without concurrent builds or test suites. Local service - containers remained running. This is one virtual host/run, not a hardware-wide - guarantee, a cold-disk benchmark, or evidence of media/network throughput. -- Raw samples: [normal affinity](results/linux-amd64.csv), - [CPU 7](results/linux-amd64-cpu7.csv). Corresponding JSON summaries contain - means, medians, p95, ranges, standard deviations, page faults and context - switches. [`binaries.json`](results/binaries.json) records exact commits, - SHA-256 hashes, file lengths, and deterministic gzip sizes. +| Wall time, including startup | 331 ms | 445 ms | +| CPU time (user + system) | 198 ms | 285 ms | +| CPU utilization | 58.4% | 64.7% | +| Peak RAM (RSS) | 31.2 MiB | 35.0 MiB | +| Binary size | 21.6 MiB | 19.6 MiB | + +Measured on 2026-09-24. Each runtime figure is the median of 20 imports after four +warmups per build. On this fixture, Rust takes longer and uses more CPU time and +RAM; its executable is smaller. + +## What runs + +The repository's YouTube web mock serves a two-second, 1080p AVC video (1.50 MiB) +and separate Opus audio (43 KiB). Both CLIs run their actual extraction library, +download both streams, transcode Opus to AAC with bundled FFmpeg, prepare MP4/HLS, +upload to the local API/storage, and publish the episode. Each import gets a fresh +video ID and show. The driver rejects failed imports, unpublished video, and runs +that did not download the Opus fixture. + +Go is [3d0f569](https://github.com/listenbox/cli/commit/3d0f56927a9fc0ae8a97d1cb64d209505ea2ae14): +**go-astiav 0.43.0 + kkdai/youtube 2.10.6**, from [PR #7](https://github.com/listenbox/cli/pull/7). +Rust is [caaec94](https://github.com/listenbox/cli/commit/caaec943677ce8fc4b32713b12389213d8445b7f): +**ffmpeg-the-third 6.0.0 + YouTube.js 18.0.0 through rquickjs 0.11.0**. +Both are optimized, stripped release executables with statically linked FFmpeg +9.0.2. No external FFmpeg or JavaScript process runs during the import. + +## Measurement + +Linux x86-64, AMD EPYC-Rome VM, eight vCPUs with normal affinity. Go 1.27.1; +Rust 1.98.1. Builds and fixture setup finish before timing. The E2E driver repeats +Go/Rust/Rust/Go twelve times, discarding the first two groups as warmups. File +caches are warm; no builds or other test suites run alongside the samples. + +[`measure.c`](measure.c) measures the CLI child from `posix_spawn` to process exit +using a monotonic clock and `wait4`. Wall time includes startup, local HTTP waits, +and the complete import. CPU time combines user and system time across CLI +threads. CPU utilization is CPU time divided by wall time, expressed as a +percentage (100% means one fully occupied CPU). RAM is peak resident memory. +The supervisor and backend services' CPU/RAM are excluded. There is no separate +startup-only claim: startup is included in the one command's wall time. + +These results describe this short, deterministic local workload, not live YouTube +latency, signature challenges, long-video memory growth, or languages in general. +[Raw samples, binary hashes and summary](results/youtube-import.json) · +[Build and host details](results/environment.json). ## Reproduce -Build the two commits in separate checkouts with their Moon package tasks and -required tools installed. In the Rust checkout: +In a separate checkout of Go commit `3d0f569`, run `moon run cli:build`. +Then, from the parent Listenbox workspace with this Rust CLI submodule: ```sh -cc -O2 -Wall -Wextra -Werror bench/startup.c -o /tmp/cli-startup -env -u GOMAXPROCS -u GOGC -u GOMEMLIMIT -u RUST_LOG \ - /tmp/cli-startup /absolute/go/dist/listenbox /absolute/rust/dist/release/listenbox > samples.csv -python3 bench/summarize.py samples.csv -# Repeat with `taskset -c 7 /tmp/cli-startup ...` for the controlled affinity run. +LISTENBOX_BENCHMARK_GO=/absolute/go-checkout/dist/listenbox moon run api:benchmark-cli ``` -The summarizer uses the Python standard library; p95 uses nearest rank. +That target builds the Rust release executable and measurement wrapper, starts +the canonical E2E environment, measures imports, and summarizes them with +[`summarize.py`](summarize.py). Results are written to +`apps/api/.test-results/cli-import-summary.json`. No Go correctness suite is run. diff --git a/bench/measure.c b/bench/measure.c new file mode 100644 index 0000000..6d7c754 --- /dev/null +++ b/bench/measure.c @@ -0,0 +1,52 @@ +// Linux process accounting for one real CLI invocation. The E2E harness owns +// fixtures and cancellation; this child retains its supervisor's process group. +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +extern char **environ; + +static double milliseconds(struct timespec value) { + return value.tv_sec * 1000.0 + value.tv_nsec / 1000000.0; +} + +static double cpu_ms(struct timeval value) { + return value.tv_sec * 1000.0 + value.tv_usec / 1000.0; +} + +int main(int argc, char **argv) { + if (argc < 3) { + fprintf(stderr, "usage: %s METRICS_JSON BINARY [ARGS...]\n", argv[0]); + return 1; + } + struct timespec begin, end; + struct rusage usage; + pid_t child; + int status; + if (clock_gettime(CLOCK_MONOTONIC, &begin)) { perror("clock"); return 1; } + int error = posix_spawn(&child, argv[2], NULL, NULL, argv + 2, environ); + if (error) { fprintf(stderr, "spawn: %s\n", strerror(error)); return 1; } + while (wait4(child, &status, 0, &usage) < 0) { + if (errno != EINTR) { perror("wait4"); return 1; } + } + if (clock_gettime(CLOCK_MONOTONIC, &end)) { perror("clock"); return 1; } + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr, "CLI failed: wait status %d\n", status); + return 1; + } + FILE *output = fopen(argv[1], "wx"); + if (!output) { perror("metrics file"); return 1; } + double wall = milliseconds(end) - milliseconds(begin); + double cpu = cpu_ms(usage.ru_utime) + cpu_ms(usage.ru_stime); + int written = fprintf(output, + "{\"wall_ms\":%.6f,\"cpu_ms\":%.3f,\"cpu_percent\":%.6f,\"peak_rss_mib\":%.6f}\n", + wall, cpu, 100.0 * cpu / wall, usage.ru_maxrss / 1024.0); + int closed = fclose(output); + if (written < 0 || closed) { perror("write metrics"); return 1; } + return 0; +} diff --git a/bench/results/binaries.json b/bench/results/binaries.json deleted file mode 100644 index fd93ea8..0000000 --- a/bench/results/binaries.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "go": { - "commit": "3d0f56927a9fc0ae8a97d1cb64d209505ea2ae14", - "bytes": 22639472, - "gzip_9_bytes": 8728187, - "sha256": "b14d4f4fe699d2995d21f9da7211a36fab4f1a2c7c2c98c9aa7c1101ed6a9692" - }, - "rust": { - "commit": "5bd42d509f33ae08bb6ef5cd21546cf3943bdaca", - "bytes": 20525240, - "gzip_9_bytes": 8141703, - "sha256": "ffd778f8d1dc2ae07678d11ddeedcb1515c2f4f1e93e9b40c990342d135ea26b" - } -} diff --git a/bench/results/environment.json b/bench/results/environment.json index a67f87e..91f5e93 100644 --- a/bench/results/environment.json +++ b/bench/results/environment.json @@ -14,10 +14,7 @@ "rust_youtube": "youtubei.js 18.0.0 through rquickjs 0.11.0", "go_flags": "CGO_ENABLED=1 go build -trimpath -ldflags=\"-s -w\"", "rust_flags": "cargo build --locked --release; opt-level=3, strip=true, lto=thin", - "warmups_per_command_per_binary": 30, - "samples_per_command_per_binary": 300, "default_affinity": "0-7", - "controlled_affinity": "7", "unset_environment": [ "GOMAXPROCS", "GOGC", @@ -32,5 +29,20 @@ "libgcc_s.so.1", "libm.so.6", "libc.so.6" - ] + ], + "warmups_per_build": 4, + "samples_per_build": 20, + "workload": "One fresh YouTube video import: local Innertube responses, 1080p AVC + Opus download, AAC transcode, MP4/HLS packaging, upload and publication", + "measurement": "CLOCK_MONOTONIC and wait4 for the CLI child only; supervisor, fixture setup and backend CPU/RAM excluded", + "fixture_workspace_commit": "674cadc3bb543a5bf41bd5b59c7d46cf606c58e4", + "fixture_files": { + "apps/fixtures/e2e/youtube/dash-range/1080/manifest-stream0.mp4": { + "bytes": 1570121, + "sha256": "7b94dd83068e85136002f6eec994112368b9c3d077351f1ee2a8b5bca60c3b94" + }, + "apps/fixtures/e2e/youtube/audio/opus.webm": { + "bytes": 44326, + "sha256": "0a5eaec5205f2add1314c71362e1b0720a49eaec41a28a77d655dc53ff8a56ae" + } + } } diff --git a/bench/results/linux-amd64-cpu7.csv b/bench/results/linux-amd64-cpu7.csv deleted file mode 100644 index 024094a..0000000 --- a/bench/results/linux-amd64-cpu7.csv +++ /dev/null @@ -1,1201 +0,0 @@ -implementation,command,sample,wall_us,user_us,system_us,peak_rss_kib,minor_faults,major_faults,voluntary_context_switches,involuntary_context_switches -go,root-help,0,4182.707,1026.000,3080.000,13176,703,0,42,35 -rust,root-help,0,1754.055,853.000,853.000,6556,349,0,1,1 -rust,root-help,1,1347.562,0.000,1309.000,6516,351,0,1,1 -go,root-help,1,4022.837,909.000,2728.000,13180,692,0,38,34 -go,root-help,2,4252.489,3126.000,1042.000,13244,706,0,28,24 -rust,root-help,2,1749.566,846.000,846.000,6808,351,0,1,1 -rust,root-help,3,1336.540,0.000,1287.000,6488,349,0,1,2 -go,root-help,3,4074.614,2780.000,926.000,13128,701,0,41,34 -go,root-help,4,4515.684,775.000,3103.000,13176,701,0,39,34 -rust,root-help,4,1661.141,1610.000,0.000,6332,355,0,1,2 -rust,root-help,5,1849.915,0.000,1458.000,6852,351,0,1,2 -go,root-help,5,4020.773,1974.000,1974.000,13216,693,0,40,37 -go,root-help,6,3375.221,2214.000,1107.000,13180,690,0,22,17 -rust,root-help,6,1714.341,0.000,1664.000,6692,349,0,1,2 -rust,root-help,7,1571.062,1524.000,0.000,6756,350,0,1,1 -go,root-help,7,4240.857,2881.000,960.000,13176,692,0,42,37 -go,root-help,8,3839.864,3717.000,0.000,13216,713,0,36,32 -rust,root-help,8,2062.374,0.000,1643.000,6556,350,0,1,1 -rust,root-help,9,1852.139,0.000,1790.000,6600,351,0,1,1 -go,root-help,9,5723.162,1734.000,3469.000,13176,687,0,20,15 -go,root-help,10,4641.018,1693.000,2540.000,13220,705,0,22,20 -rust,root-help,10,1649.029,0.000,1604.000,6676,351,0,1,1 -rust,root-help,11,1964.080,1529.000,0.000,6792,352,0,1,1 -go,root-help,11,3740.097,2758.000,919.000,13164,691,0,38,37 -go,root-help,12,3736.409,918.000,2756.000,13240,692,0,34,28 -rust,root-help,12,1598.502,1557.000,0.000,6520,350,0,1,1 -rust,root-help,13,1299.673,0.000,1251.000,6496,349,0,1,2 -go,root-help,13,3743.743,3682.000,0.000,13160,707,0,41,34 -go,root-help,14,3777.317,2788.000,929.000,13220,694,0,40,36 -rust,root-help,14,1798.408,0.000,1701.000,6344,350,0,1,1 -rust,root-help,15,1205.684,1168.000,0.000,6484,349,0,1,1 -go,root-help,15,4162.900,3062.000,1020.000,13092,693,0,36,30 -go,root-help,16,4200.490,1016.000,3049.000,13148,691,0,22,17 -rust,root-help,16,1745.068,0.000,1695.000,6664,350,0,1,1 -rust,root-help,17,1436.098,0.000,1390.000,6660,349,0,1,1 -go,root-help,17,4536.373,936.000,2810.000,13176,691,0,38,35 -go,root-help,18,4622.895,3157.000,789.000,13176,687,0,42,36 -rust,root-help,18,1731.122,838.000,838.000,6628,349,0,1,2 -rust,root-help,19,1711.435,1652.000,0.000,6484,350,0,1,1 -go,root-help,19,5209.507,2800.000,1866.000,13176,702,0,44,41 -go,root-help,20,4232.661,2075.000,2075.000,13180,697,0,16,12 -rust,root-help,20,2514.974,0.000,2099.000,6792,351,0,1,2 -rust,root-help,21,2016.348,0.000,1911.000,6600,350,0,1,2 -go,root-help,21,4761.887,0.000,4637.000,13164,697,0,18,13 -go,root-help,22,4539.157,817.000,3269.000,13148,689,0,33,29 -rust,root-help,22,1608.142,0.000,1562.000,6668,351,0,1,1 -rust,root-help,23,1330.790,0.000,1288.000,6576,350,0,1,1 -go,root-help,23,4313.644,984.000,2953.000,13176,707,0,41,32 -go,root-help,24,5208.626,4090.000,1022.000,13164,701,0,45,41 -rust,root-help,24,2392.744,774.000,1548.000,6852,351,0,1,1 -rust,root-help,25,1712.998,0.000,1649.000,6612,349,0,1,1 -go,root-help,25,4541.011,0.000,4430.000,13180,688,0,16,12 -go,root-help,26,4645.056,2726.000,1817.000,13176,699,0,16,13 -rust,root-help,26,2625.503,1103.000,1103.000,6680,350,0,1,2 -rust,root-help,27,2000.960,958.000,958.000,6824,350,0,1,2 -go,root-help,27,4940.412,898.000,3593.000,13148,691,0,39,34 -go,root-help,28,5266.253,4101.000,1025.000,13176,688,0,18,12 -rust,root-help,28,2418.885,0.000,2331.000,6552,350,0,1,1 -rust,root-help,29,2225.560,0.000,2142.000,6560,349,0,1,1 -go,root-help,29,5601.052,2736.000,2736.000,13160,692,0,50,44 -go,root-help,30,4546.129,3324.000,1108.000,13148,688,0,36,30 -rust,root-help,30,1800.262,873.000,873.000,6716,350,0,1,1 -rust,root-help,31,2126.374,0.000,1756.000,6596,351,0,1,2 -go,root-help,31,4454.188,3482.000,870.000,13160,688,0,39,33 -go,root-help,32,4392.963,1076.000,3230.000,13172,686,0,41,34 -rust,root-help,32,2297.485,0.000,1892.000,6568,350,0,0,3 -rust,root-help,33,1578.315,0.000,1517.000,6784,351,0,1,2 -go,root-help,33,3868.479,1893.000,1893.000,13172,698,0,14,10 -go,root-help,34,3696.324,905.000,2715.000,13048,679,0,14,10 -rust,root-help,34,1849.183,1707.000,0.000,6660,350,0,1,1 -rust,root-help,35,1736.553,1684.000,0.000,6472,349,0,1,1 -go,root-help,35,4354.972,1595.000,2393.000,13164,705,0,41,36 -go,root-help,36,4343.821,1977.000,1977.000,13148,691,0,38,33 -rust,root-help,36,1724.840,839.000,839.000,6720,352,0,1,1 -rust,root-help,37,1235.701,1199.000,0.000,6780,352,0,1,1 -go,root-help,37,3901.470,3816.000,0.000,13172,691,0,26,20 -go,root-help,38,4153.864,3058.000,1019.000,13180,692,0,40,36 -rust,root-help,38,1731.733,0.000,1684.000,6732,349,0,1,1 -rust,root-help,39,1667.202,1287.000,0.000,6728,349,0,1,2 -go,root-help,39,4053.625,2716.000,905.000,13160,701,0,40,31 -go,root-help,40,3499.845,0.000,3421.000,13216,693,0,14,12 -rust,root-help,40,1672.673,0.000,1628.000,6596,350,0,1,1 -rust,root-help,41,1377.938,1336.000,0.000,6624,349,0,1,1 -go,root-help,41,4168.250,950.000,2852.000,13220,703,0,38,39 -go,root-help,42,4808.525,3191.000,797.000,13184,691,0,38,33 -rust,root-help,42,1784.873,862.000,862.000,6876,352,0,1,2 -rust,root-help,43,2153.597,0.000,1716.000,6588,351,0,1,3 -go,root-help,43,4259.001,2076.000,2076.000,13172,690,0,42,34 -go,root-help,44,4694.200,1692.000,2538.000,13176,687,0,41,32 -rust,root-help,44,1801.674,0.000,1748.000,6672,349,0,1,2 -rust,root-help,45,1758.604,0.000,1409.000,6608,350,0,1,2 -go,root-help,45,3671.839,0.000,3606.000,13216,715,0,38,35 -go,root-help,46,4391.710,1593.000,2389.000,13164,696,0,43,35 -rust,root-help,46,1933.372,941.000,941.000,6748,351,0,1,2 -rust,root-help,47,1351.588,0.000,1310.000,6472,349,0,1,1 -go,root-help,47,4571.408,896.000,3587.000,13084,682,0,41,35 -go,root-help,48,4785.070,2187.000,2187.000,13148,700,0,40,36 -rust,root-help,48,1822.965,1738.000,0.000,6628,350,0,1,1 -rust,root-help,49,1494.607,722.000,722.000,6644,351,0,1,1 -go,root-help,49,4135.289,928.000,2784.000,13180,684,0,15,13 -go,root-help,50,4240.236,0.000,3839.000,13180,688,0,15,12 -rust,root-help,50,1921.961,928.000,928.000,6696,350,0,1,2 -rust,root-help,51,1628.719,783.000,783.000,6612,350,0,1,1 -go,root-help,51,4597.356,1127.000,3382.000,13176,702,0,42,35 -go,root-help,52,4203.979,1647.000,2471.000,13148,700,0,40,36 -rust,root-help,52,1773.340,0.000,1724.000,6676,350,0,1,1 -rust,root-help,53,1488.986,722.000,722.000,6500,349,0,1,1 -go,root-help,53,4324.184,2122.000,2122.000,13164,699,0,20,13 -go,root-help,54,3932.939,1928.000,1928.000,13216,707,0,39,34 -rust,root-help,54,1538.149,747.000,747.000,6640,350,0,1,1 -rust,root-help,55,1526.037,0.000,1465.000,6492,349,0,1,2 -go,root-help,55,4437.175,870.000,3483.000,13164,691,0,47,38 -go,root-help,56,5173.129,940.000,3760.000,13164,692,0,42,37 -rust,root-help,56,1965.823,949.000,949.000,6736,351,0,1,1 -rust,root-help,57,2162.682,0.000,1731.000,6632,351,0,1,2 -go,root-help,57,4355.112,2126.000,2126.000,13148,687,0,18,11 -go,root-help,58,4520.353,2469.000,1646.000,13148,706,0,40,35 -rust,root-help,58,1895.620,1843.000,0.000,6684,349,0,1,1 -rust,root-help,59,1904.227,767.000,767.000,6656,350,0,1,2 -go,root-help,59,4112.025,1830.000,1830.000,13240,693,0,21,17 -go,root-help,60,4754.573,1719.000,2578.000,13148,686,0,42,38 -rust,root-help,60,1678.934,0.000,1631.000,6672,350,0,1,1 -rust,root-help,61,1307.706,0.000,1269.000,6152,355,0,1,1 -go,root-help,61,4396.630,996.000,2988.000,13148,707,0,43,36 -go,root-help,62,4989.754,1801.000,2702.000,13236,693,0,37,33 -rust,root-help,62,2242.313,1089.000,1089.000,6772,351,0,1,1 -rust,root-help,63,2164.466,1046.000,1046.000,6844,351,0,1,1 -go,root-help,63,5816.868,2626.000,2626.000,13164,688,0,46,44 -go,root-help,64,5362.225,1609.000,3219.000,13180,693,0,47,38 -rust,root-help,64,2441.897,0.000,2010.000,6564,350,0,1,2 -rust,root-help,65,1619.613,780.000,780.000,6512,350,0,1,2 -go,root-help,65,4076.818,1002.000,3008.000,13176,702,0,42,38 -go,root-help,66,3791.723,1849.000,1849.000,13148,688,0,15,11 -rust,root-help,66,1749.036,0.000,1694.000,6512,350,0,1,1 -rust,root-help,67,1410.079,0.000,1362.000,6788,350,0,1,1 -go,root-help,67,3632.185,0.000,3571.000,13180,702,0,41,34 -go,root-help,68,4620.591,0.000,4117.000,13180,692,0,47,32 -rust,root-help,68,1869.461,906.000,906.000,6600,350,0,1,2 -rust,root-help,69,1383.580,0.000,1341.000,6668,351,0,1,1 -go,root-help,69,4930.162,2686.000,1790.000,13176,694,0,37,34 -go,root-help,70,3887.003,2858.000,952.000,13176,690,0,37,32 -rust,root-help,70,2089.976,1014.000,1014.000,6636,350,0,1,1 -rust,root-help,71,2278.009,726.000,1453.000,6652,350,0,1,2 -go,root-help,71,5287.773,1799.000,2699.000,13128,701,0,31,26 -go,root-help,72,4546.231,3347.000,1115.000,13180,699,0,41,36 -rust,root-help,72,1801.264,1751.000,0.000,6668,351,0,1,1 -rust,root-help,73,1679.345,0.000,1579.000,6684,349,0,1,1 -go,root-help,73,5551.549,836.000,4180.000,13164,703,0,49,41 -go,root-help,74,5126.150,3015.000,2010.000,13224,692,0,16,13 -rust,root-help,74,1922.360,0.000,1865.000,6428,350,0,1,1 -rust,root-help,75,2538.738,0.000,2111.000,6608,350,0,1,2 -go,root-help,75,5569.875,842.000,4212.000,13080,686,0,45,44 -go,root-help,76,4583.599,3374.000,1124.000,13180,707,0,41,33 -rust,root-help,76,2453.439,1588.000,794.000,6344,350,0,1,1 -rust,root-help,77,2296.765,0.000,2196.000,6588,349,0,1,1 -go,root-help,77,5672.216,3397.000,1698.000,13164,692,0,43,43 -go,root-help,78,5774.408,1896.000,3792.000,13220,700,0,51,47 -rust,root-help,78,2210.822,1066.000,1066.000,6656,349,0,1,2 -rust,root-help,79,1978.106,1894.000,0.000,6544,350,0,1,1 -go,root-help,79,5255.343,1885.000,2828.000,13160,696,0,23,20 -go,root-help,80,5230.426,1745.000,2618.000,13180,687,0,15,13 -rust,root-help,80,2424.615,0.000,2341.000,6644,350,0,1,2 -rust,root-help,81,2296.945,1104.000,1104.000,6484,349,0,1,1 -go,root-help,81,5700.931,3388.000,1694.000,13148,686,0,15,13 -go,root-help,82,6304.204,1891.000,3783.000,13032,679,0,17,13 -rust,root-help,82,2685.113,0.000,2561.000,6540,350,0,1,2 -rust,root-help,83,2440.023,1171.000,1171.000,6236,356,0,1,1 -go,root-help,83,6599.870,1716.000,4292.000,13184,708,0,55,50 -go,root-help,84,5173.609,1014.000,4058.000,13176,690,0,20,14 -rust,root-help,84,2374.551,0.000,2308.000,6716,349,0,1,1 -rust,root-help,85,2141.383,0.000,2068.000,6628,349,0,1,1 -go,root-help,85,5228.271,3050.000,2033.000,13140,705,0,21,15 -go,root-help,86,5912.527,2668.000,2668.000,13160,692,0,31,27 -rust,root-help,86,2315.681,0.000,2251.000,6620,350,0,1,1 -rust,root-help,87,2648.376,1414.000,707.000,6468,350,0,1,2 -go,root-help,87,5177.447,2031.000,3046.000,13180,688,0,16,12 -go,root-help,88,6234.743,3811.000,1905.000,13148,691,0,50,42 -rust,root-help,88,2914.716,0.000,2429.000,6716,350,0,1,2 -rust,root-help,89,2354.673,752.000,1505.000,6688,350,0,1,2 -go,root-help,89,5419.951,0.000,5316.000,13148,701,0,16,12 -go,root-help,90,5720.347,3737.000,1868.000,13184,711,0,48,43 -rust,root-help,90,2581.730,1253.000,1253.000,6472,349,0,1,1 -rust,root-help,91,2366.055,0.000,2266.000,6632,348,0,1,2 -go,root-help,91,5951.942,3194.000,2129.000,13160,691,0,28,21 -go,root-help,92,5677.125,2226.000,3339.000,13216,701,0,43,39 -rust,root-help,92,2447.588,0.000,2379.000,6556,350,0,1,1 -rust,root-help,93,2074.978,997.000,997.000,6600,350,0,1,1 -go,root-help,93,5499.612,1944.000,2917.000,13180,688,0,16,13 -go,root-help,94,5538.024,1080.000,4322.000,13184,701,0,39,36 -rust,root-help,94,3014.834,823.000,1647.000,6716,350,0,1,2 -rust,root-help,95,2365.032,0.000,2272.000,6576,350,0,1,2 -go,root-help,95,5107.676,1516.000,3032.000,13180,707,0,41,37 -go,root-help,96,4127.955,1012.000,3037.000,13184,704,0,15,7 -rust,root-help,96,1750.989,0.000,1698.000,6724,350,0,1,1 -rust,root-help,97,1949.983,0.000,1550.000,6600,351,0,1,2 -go,root-help,97,3874.019,1898.000,1898.000,13148,700,0,39,35 -go,root-help,98,4551.671,2460.000,1640.000,13180,685,0,40,34 -rust,root-help,98,1881.305,1822.000,0.000,6536,351,0,1,2 -rust,root-help,99,1638.038,0.000,1575.000,6664,350,0,1,2 -go,root-help,99,3859.240,948.000,2845.000,13148,686,0,41,34 -go,root-help,100,4882.753,2628.000,1752.000,13176,698,0,25,20 -rust,root-help,100,1881.584,910.000,910.000,6464,350,0,1,2 -rust,root-help,101,1813.688,0.000,1749.000,6644,349,0,1,1 -go,root-help,101,4272.205,3122.000,1040.000,13084,692,0,42,37 -go,root-help,102,4741.900,856.000,3426.000,13164,687,0,38,36 -rust,root-help,102,1803.178,876.000,876.000,6516,350,0,1,1 -rust,root-help,103,1446.497,1399.000,0.000,6544,349,0,1,1 -go,root-help,103,3904.155,2878.000,959.000,13164,691,0,39,31 -go,root-help,104,4654.655,1664.000,2497.000,13164,691,0,41,36 -rust,root-help,104,1939.685,0.000,1884.000,6788,351,0,1,1 -rust,root-help,105,1960.313,0.000,1896.000,6572,350,0,1,1 -go,root-help,105,6314.492,3545.000,1772.000,13148,704,0,46,40 -go,root-help,106,6310.405,4538.000,907.000,13160,687,0,39,37 -rust,root-help,106,2031.747,975.000,975.000,6628,348,0,1,1 -rust,root-help,107,2715.612,0.000,2236.000,6684,350,0,1,2 -go,root-help,107,4831.205,2828.000,1885.000,13184,687,0,42,39 -go,root-help,108,4872.924,1908.000,2863.000,13184,696,0,34,32 -rust,root-help,108,2149.348,0.000,2060.000,6744,352,0,1,1 -rust,root-help,109,2325.259,918.000,918.000,6648,348,0,1,2 -go,root-help,109,4501.997,2637.000,1758.000,13176,690,0,36,34 -go,root-help,110,4418.121,2164.000,2164.000,13220,698,0,16,11 -rust,root-help,110,2389.158,0.000,2292.000,6296,357,0,1,2 -rust,root-help,111,2055.572,991.000,991.000,6664,351,0,1,1 -go,root-help,111,4512.237,0.000,4071.000,13176,691,0,18,15 -go,root-help,112,5119.577,2756.000,1837.000,13148,708,0,40,35 -rust,root-help,112,2275.295,0.000,2208.000,6640,350,0,1,1 -rust,root-help,113,2074.979,2007.000,0.000,6660,350,0,1,1 -go,root-help,113,4369.228,983.000,2950.000,13180,689,0,14,9 -go,root-help,114,4661.768,902.000,3608.000,13180,692,0,20,12 -rust,root-help,114,2064.528,1998.000,0.000,6508,350,0,1,1 -rust,root-help,115,2426.789,0.000,1944.000,6620,349,0,1,2 -go,root-help,115,5409.222,3203.000,1601.000,13216,690,0,44,40 -go,root-help,116,4694.960,2280.000,2280.000,13180,687,0,33,28 -rust,root-help,116,2112.169,0.000,2049.000,6792,351,0,1,1 -rust,root-help,117,1855.094,0.000,1771.000,6848,350,0,1,1 -go,root-help,117,4501.798,2016.000,2016.000,13184,695,0,14,9 -go,root-help,118,4549.948,798.000,3195.000,13020,681,0,15,11 -rust,root-help,118,1782.889,0.000,1722.000,6760,351,0,1,2 -rust,root-help,119,1684.365,0.000,1631.000,6576,350,0,1,1 -go,root-help,119,4480.917,872.000,3490.000,13164,697,0,18,14 -go,root-help,120,5401.408,2911.000,1941.000,13172,707,0,42,34 -rust,root-help,120,2100.185,0.000,2035.000,6604,350,0,1,1 -rust,root-help,121,2861.567,1555.000,777.000,6360,351,0,1,3 -go,root-help,121,5891.928,4411.000,882.000,13012,680,0,13,8 -go,root-help,122,6242.106,2827.000,2827.000,13148,704,0,30,24 -rust,root-help,122,3357.978,901.000,1802.000,6664,350,0,1,2 -rust,root-help,123,2518.842,0.000,2418.000,6800,351,0,1,2 -go,root-help,123,4921.325,3835.000,958.000,13180,693,0,14,10 -go,root-help,124,6173.798,2796.000,2796.000,13084,689,0,26,23 -rust,root-help,124,2381.584,0.000,2314.000,6672,351,0,1,1 -rust,root-help,125,2731.782,0.000,2649.000,6472,349,0,1,1 -go,root-help,125,4515.592,1760.000,2640.000,13220,691,0,13,8 -go,root-help,126,5158.170,2807.000,1871.000,13140,698,0,22,19 -rust,root-help,126,1837.103,0.000,1762.000,6668,350,0,1,2 -rust,root-help,127,1569.729,0.000,1504.000,6728,350,0,1,2 -go,root-help,127,3957.104,2587.000,1293.000,13176,695,0,40,37 -go,root-help,128,4826.237,2162.000,2162.000,13148,692,0,40,38 -rust,root-help,128,1925.637,930.000,930.000,6568,351,0,1,1 -rust,root-help,129,1678.573,0.000,1587.000,6700,350,0,1,1 -go,root-help,129,4159.555,939.000,2819.000,13160,692,0,41,34 -go,root-help,130,4000.335,0.000,3920.000,13084,701,0,13,8 -rust,root-help,130,2008.362,975.000,975.000,6648,348,0,1,1 -rust,root-help,131,2130.834,1696.000,0.000,6672,350,0,1,2 -go,root-help,131,4627.974,0.000,4483.000,13228,688,0,43,35 -go,root-help,132,4491.828,1101.000,3305.000,13148,687,0,15,11 -rust,root-help,132,2315.570,1271.000,635.000,6616,350,0,1,2 -rust,root-help,133,1612.990,1557.000,0.000,6568,350,0,1,2 -go,root-help,133,4634.446,0.000,4553.000,13164,696,0,37,33 -go,root-help,134,5176.334,3543.000,885.000,13180,688,0,17,12 -rust,root-help,134,1808.237,0.000,1716.000,6540,348,0,1,1 -rust,root-help,135,1554.010,0.000,1498.000,6620,349,0,1,1 -go,root-help,135,4983.632,1107.000,3322.000,13184,692,0,41,35 -go,root-help,136,4453.436,2191.000,2191.000,13180,691,0,41,34 -rust,root-help,136,1853.212,0.000,1802.000,6716,349,0,1,1 -rust,root-help,137,1448.601,0.000,1402.000,6628,350,0,1,1 -go,root-help,137,3842.469,942.000,2827.000,13176,693,0,34,28 -go,root-help,138,4138.065,3046.000,1015.000,13148,692,0,40,33 -rust,root-help,138,1835.447,0.000,1785.000,6412,352,0,1,1 -rust,root-help,139,2168.463,1733.000,0.000,6720,351,0,1,2 -go,root-help,139,5032.344,2737.000,1825.000,13160,698,0,37,36 -go,root-help,140,5255.743,3181.000,1590.000,13240,704,0,44,36 -rust,root-help,140,1851.248,896.000,896.000,6432,351,0,1,2 -rust,root-help,141,1783.211,0.000,1437.000,6344,350,0,1,2 -go,root-help,141,3940.673,2903.000,967.000,13164,701,0,39,35 -go,root-help,142,3950.251,969.000,2909.000,13164,691,0,36,37 -rust,root-help,142,1571.431,0.000,1515.000,6680,349,0,1,1 -rust,root-help,143,1294.352,0.000,1245.000,6632,351,0,1,1 -go,root-help,143,3623.557,2671.000,890.000,13180,703,0,39,29 -go,root-help,144,4840.142,2595.000,1730.000,13176,685,0,24,20 -rust,root-help,144,1962.717,0.000,1908.000,6516,350,0,1,1 -rust,root-help,145,2609.682,0.000,2148.000,6864,352,0,1,2 -go,root-help,145,4596.525,1675.000,2513.000,13176,686,0,40,32 -go,root-help,146,4265.124,963.000,2891.000,13216,692,0,27,23 -rust,root-help,146,1915.749,0.000,1856.000,6788,351,0,1,2 -rust,root-help,147,1830.410,736.000,736.000,6664,349,0,1,2 -go,root-help,147,4272.858,2091.000,2091.000,13164,708,0,45,36 -go,root-help,148,4844.551,2613.000,1742.000,13148,691,0,41,37 -rust,root-help,148,2042.708,0.000,1978.000,6572,350,0,1,2 -rust,root-help,149,1940.566,0.000,1855.000,6560,352,0,1,2 -go,root-help,149,4756.917,1735.000,2603.000,13176,688,0,39,38 -go,root-help,150,4917.618,2669.000,1779.000,13216,703,0,41,37 -rust,root-help,150,1932.059,938.000,938.000,6808,350,0,1,1 -rust,root-help,151,1577.334,0.000,1526.000,6856,350,0,1,1 -go,root-help,151,4438.198,2381.000,1587.000,13128,701,0,22,17 -go,root-help,152,4311.129,1041.000,3123.000,12956,690,0,16,14 -rust,root-help,152,2141.724,1037.000,1037.000,6520,350,0,1,1 -rust,root-help,153,1970.190,950.000,950.000,6744,350,0,1,1 -go,root-help,153,4766.656,2805.000,1870.000,13180,692,0,45,38 -go,root-help,154,5964.536,2498.000,2498.000,13148,694,0,23,18 -rust,root-help,154,2234.958,0.000,2150.000,6724,349,0,1,2 -rust,root-help,155,2216.845,0.000,2092.000,6696,351,0,1,2 -go,root-help,155,5416.787,1636.000,3272.000,13164,691,0,45,40 -go,root-help,156,5327.929,1782.000,2673.000,13180,682,0,38,34 -rust,root-help,156,1963.159,0.000,1893.000,6604,350,0,1,2 -rust,root-help,157,1916.331,0.000,1839.000,6636,349,0,1,2 -go,root-help,157,4717.703,3701.000,925.000,13176,690,0,43,37 -go,root-help,158,5275.560,2849.000,1899.000,13176,692,0,43,36 -rust,root-help,158,2864.301,1133.000,1133.000,6704,349,0,1,2 -rust,root-help,159,2206.456,1055.000,1055.000,6732,352,0,1,2 -go,root-help,159,4614.881,1796.000,2695.000,13084,703,0,16,13 -go,root-help,160,5374.277,1934.000,2901.000,13184,704,0,44,36 -rust,root-help,160,2860.844,0.000,2346.000,6664,350,0,1,2 -rust,root-help,161,2139.610,1032.000,1032.000,6664,350,0,1,2 -go,root-help,161,4943.487,971.000,3885.000,13240,689,0,17,13 -go,root-help,162,5330.975,0.000,5132.000,13224,689,0,16,12 -rust,root-help,162,2232.554,2167.000,0.000,6448,351,0,1,1 -rust,root-help,163,2548.668,1033.000,1033.000,6672,350,0,1,2 -go,root-help,163,4333.471,2544.000,1696.000,13164,692,0,40,34 -go,root-help,164,4162.231,3066.000,1022.000,13232,703,0,39,34 -rust,root-help,164,2269.523,0.000,2190.000,6544,351,0,1,1 -rust,root-help,165,1876.365,910.000,910.000,6544,348,0,1,1 -go,root-help,165,4914.874,1929.000,2894.000,13220,706,0,46,40 -go,root-help,166,5165.705,2016.000,3025.000,13148,692,0,43,37 -rust,root-help,166,2095.826,1019.000,1019.000,6844,350,0,1,1 -rust,root-help,167,1946.568,0.000,1884.000,6664,351,0,1,1 -go,root-help,167,5015.592,1686.000,2530.000,13216,704,0,15,12 -go,root-help,168,4596.355,831.000,3324.000,13180,691,0,40,35 -rust,root-help,168,1884.730,0.000,1820.000,6828,352,0,1,2 -rust,root-help,169,1721.193,1655.000,0.000,6736,350,0,1,1 -go,root-help,169,5757.887,1044.000,4179.000,13176,689,0,43,40 -go,root-help,170,4634.617,1803.000,2705.000,13084,688,0,41,35 -rust,root-help,170,2045.362,1989.000,0.000,6684,350,0,1,1 -rust,root-help,171,1677.552,809.000,809.000,6592,349,0,1,1 -go,root-help,171,4576.578,1023.000,3069.000,13176,685,0,40,38 -go,root-help,172,4422.218,2579.000,1719.000,13176,702,0,17,12 -rust,root-help,172,2084.226,1008.000,1008.000,6656,350,0,1,2 -rust,root-help,173,2145.181,0.000,1751.000,6548,350,0,1,2 -go,root-help,173,4347.538,1060.000,3182.000,13208,705,0,17,12 -go,root-help,174,5019.048,1796.000,2694.000,13164,696,0,42,37 -rust,root-help,174,1987.194,0.000,1922.000,6596,351,0,1,1 -rust,root-help,175,1678.133,0.000,1623.000,6720,350,0,1,1 -go,root-help,175,5115.821,2792.000,1861.000,13176,695,0,46,40 -go,root-help,176,5074.945,1997.000,2996.000,13128,701,0,49,46 -rust,root-help,176,2212.147,0.000,2141.000,6712,350,0,1,1 -rust,root-help,177,2175.317,0.000,1954.000,6628,348,0,1,1 -go,root-help,177,4435.363,1083.000,3250.000,13176,697,0,14,10 -go,root-help,178,6004.740,1815.000,3630.000,13148,686,0,46,42 -rust,root-help,178,2629.261,713.000,1427.000,6568,350,0,1,2 -rust,root-help,179,2005.958,966.000,966.000,6720,349,0,1,2 -go,root-help,179,5037.143,905.000,3623.000,13220,696,0,14,11 -go,root-help,180,5176.034,3034.000,2023.000,13172,705,0,25,23 -rust,root-help,180,2361.196,2289.000,0.000,6720,350,0,1,1 -rust,root-help,181,2613.801,717.000,1435.000,6656,351,0,1,2 -go,root-help,181,4970.778,1796.000,2694.000,13164,705,0,30,23 -go,root-help,182,4843.039,2198.000,2198.000,13164,683,0,15,12 -rust,root-help,182,1673.293,0.000,1613.000,6588,351,0,1,2 -rust,root-help,183,1410.530,0.000,1368.000,6736,350,0,1,1 -go,root-help,183,4680.764,3381.000,845.000,13148,701,0,35,33 -go,root-help,184,4292.143,967.000,2902.000,13220,695,0,38,32 -rust,root-help,184,1689.755,819.000,819.000,6408,352,0,1,2 -rust,root-help,185,1517.040,1456.000,0.000,6432,350,0,1,1 -go,root-help,185,3842.338,1879.000,1879.000,13084,687,0,16,12 -go,root-help,186,3959.940,2903.000,967.000,13160,687,0,38,35 -rust,root-help,186,1841.700,895.000,895.000,6600,351,0,1,1 -rust,root-help,187,1453.821,0.000,1396.000,6600,348,0,1,1 -go,root-help,187,4267.015,771.000,3087.000,13184,700,0,40,34 -go,root-help,188,4546.952,2223.000,2223.000,13148,707,0,42,36 -rust,root-help,188,2292.597,1080.000,1080.000,6660,351,0,1,1 -rust,root-help,189,1904.248,0.000,1823.000,6596,351,0,1,1 -go,root-help,189,5614.298,1711.000,3423.000,13216,692,0,48,41 -go,root-help,190,6058.873,2745.000,2745.000,13180,686,0,39,36 -rust,root-help,190,2083.223,0.000,2014.000,6752,350,0,1,1 -rust,root-help,191,2456.565,0.000,1950.000,6620,350,0,1,2 -go,root-help,191,5364.899,1047.000,4191.000,13148,699,0,44,41 -go,root-help,192,5420.042,1961.000,2942.000,13032,681,0,17,13 -rust,root-help,192,2195.084,0.000,2110.000,6728,351,0,1,2 -rust,root-help,193,1928.964,0.000,1852.000,6716,350,0,1,2 -go,root-help,193,4558.253,2227.000,2227.000,13180,686,0,13,7 -go,root-help,194,4614.980,2719.000,1812.000,13148,702,0,20,13 -rust,root-help,194,2255.749,1094.000,1094.000,6556,350,0,1,1 -rust,root-help,195,2096.227,1012.000,1012.000,6460,350,0,1,1 -go,root-help,195,5212.221,2037.000,3056.000,13172,698,0,13,7 -go,root-help,196,5386.319,1761.000,3523.000,13084,703,0,47,40 -rust,root-help,196,2316.943,1127.000,1127.000,6748,349,0,1,1 -rust,root-help,197,2102.831,0.000,2033.000,6664,350,0,1,1 -go,root-help,197,5603.086,1667.000,3335.000,13172,704,0,32,27 -go,root-help,198,5000.213,1960.000,2940.000,13180,688,0,20,13 -rust,root-help,198,2548.076,1237.000,1237.000,6664,349,0,1,1 -rust,root-help,199,2626.144,1069.000,1069.000,6576,349,0,1,2 -go,root-help,199,5117.545,1993.000,2990.000,13084,689,0,27,25 -go,root-help,200,4866.993,1911.000,2867.000,13048,679,0,16,11 -rust,root-help,200,2785.252,690.000,1381.000,6548,350,0,1,2 -rust,root-help,201,2033.691,976.000,976.000,6664,350,0,1,2 -go,root-help,201,4335.815,2118.000,2118.000,13148,701,0,15,12 -go,root-help,202,4111.143,1005.000,3015.000,13180,683,0,40,33 -rust,root-help,202,2217.627,593.000,1187.000,6664,349,0,1,2 -rust,root-help,203,1348.884,0.000,1306.000,6504,349,0,1,1 -go,root-help,203,4124.960,2019.000,2019.000,13164,703,0,13,8 -go,root-help,204,4176.085,4077.000,0.000,13180,689,0,14,8 -rust,root-help,204,2126.054,0.000,2062.000,6488,350,0,1,1 -rust,root-help,205,1770.166,1709.000,0.000,6864,352,0,1,1 -go,root-help,205,4791.371,0.000,4339.000,13160,690,0,41,37 -go,root-help,206,5113.237,4604.000,0.000,13180,692,0,26,21 -rust,root-help,206,2129.400,0.000,2052.000,6668,351,0,1,1 -rust,root-help,207,1819.899,0.000,1755.000,6656,350,0,1,1 -go,root-help,207,4750.524,2584.000,1722.000,13180,692,0,40,37 -go,root-help,208,4991.437,1799.000,2699.000,13180,686,0,22,17 -rust,root-help,208,1959.381,948.000,948.000,6600,349,0,1,2 -rust,root-help,209,1874.201,1809.000,0.000,6624,350,0,1,1 -go,root-help,209,5361.522,2899.000,1932.000,13164,696,0,47,41 -go,root-help,210,4792.644,863.000,3453.000,13216,697,0,14,9 -rust,root-help,210,2307.144,0.000,2231.000,6864,350,0,1,2 -rust,root-help,211,2049.981,989.000,989.000,6600,351,0,1,1 -go,root-help,211,4821.759,870.000,3482.000,13184,687,0,16,13 -go,root-help,212,5417.447,807.000,4039.000,13164,690,0,30,27 -rust,root-help,212,2349.565,1132.000,1132.000,6548,349,0,1,2 -rust,root-help,213,1963.568,0.000,1883.000,6672,350,0,1,1 -go,root-help,213,4630.640,1813.000,2719.000,13148,693,0,16,12 -go,root-help,214,5380.237,2944.000,1962.000,13180,687,0,41,36 -rust,root-help,214,1952.939,948.000,948.000,6676,350,0,1,1 -rust,root-help,215,2140.571,0.000,2068.000,6480,349,0,1,1 -go,root-help,215,5499.642,2862.000,1908.000,13084,692,0,42,41 -go,root-help,216,5389.875,1057.000,4231.000,13084,692,0,50,43 -rust,root-help,216,2417.932,2291.000,0.000,6668,350,0,1,1 -rust,root-help,217,2145.501,0.000,2060.000,6940,351,0,1,1 -go,root-help,217,5265.852,2810.000,1873.000,13176,706,0,20,15 -go,root-help,218,5443.396,4257.000,1064.000,13160,703,0,48,41 -rust,root-help,218,2728.817,736.000,1472.000,6716,349,0,1,2 -rust,root-help,219,2143.106,0.000,2055.000,6712,350,0,1,1 -go,root-help,219,6362.523,1654.000,4137.000,13176,698,0,53,48 -go,root-help,220,6992.305,2550.000,3401.000,13176,704,0,54,48 -rust,root-help,220,2383.157,2287.000,0.000,6708,350,0,1,2 -rust,root-help,221,2224.248,1065.000,1065.000,6472,349,0,1,2 -go,root-help,221,4828.070,1892.000,2838.000,13220,703,0,46,40 -go,root-help,222,6193.134,1733.000,3467.000,13172,681,0,19,12 -rust,root-help,222,1983.177,959.000,959.000,6876,351,0,1,2 -rust,root-help,223,1719.811,0.000,1651.000,6888,351,0,1,2 -go,root-help,223,4694.288,1842.000,2764.000,13148,686,0,44,40 -go,root-help,224,5276.722,2846.000,1897.000,13172,688,0,37,34 -rust,root-help,224,2148.075,906.000,906.000,6644,349,0,1,2 -rust,root-help,225,1437.290,1387.000,0.000,6648,348,0,1,2 -go,root-help,225,4222.283,1036.000,3110.000,13180,691,0,42,40 -go,root-help,226,4584.273,1121.000,3363.000,13172,691,0,40,36 -rust,root-help,226,2372.757,0.000,1936.000,6600,351,0,1,2 -rust,root-help,227,1634.391,0.000,1577.000,6720,350,0,1,2 -go,root-help,227,4521.093,2220.000,2220.000,13148,709,0,45,38 -go,root-help,228,4639.486,2729.000,1819.000,13220,702,0,42,36 -rust,root-help,228,1978.757,964.000,964.000,6588,351,0,1,1 -rust,root-help,229,1684.414,1628.000,0.000,6496,351,0,1,1 -go,root-help,229,5476.689,2300.000,2300.000,13180,691,0,42,39 -go,root-help,230,5795.057,4363.000,872.000,13148,691,0,44,39 -rust,root-help,230,2678.262,1083.000,1083.000,6552,350,0,1,2 -rust,root-help,231,1967.166,942.000,942.000,6536,351,0,1,2 -go,root-help,231,4744.755,2779.000,1853.000,13176,688,0,30,26 -go,root-help,232,4810.337,0.000,4726.000,13148,707,0,41,36 -rust,root-help,232,1963.309,0.000,1911.000,6612,350,0,1,1 -rust,root-help,233,2323.826,0.000,1901.000,6604,350,0,1,2 -go,root-help,233,4995.424,3603.000,900.000,13180,705,0,42,37 -go,root-help,234,4794.998,1875.000,2813.000,13216,682,0,35,32 -rust,root-help,234,2237.153,940.000,940.000,6504,350,0,1,2 -rust,root-help,235,1794.050,0.000,1731.000,6836,350,0,1,1 -go,root-help,235,4917.428,890.000,3561.000,13180,704,0,45,38 -go,root-help,236,5309.165,968.000,3872.000,13144,698,0,36,33 -rust,root-help,236,1801.795,0.000,1736.000,6620,349,0,1,1 -rust,root-help,237,1596.909,0.000,1528.000,6672,351,0,1,2 -go,root-help,237,3743.233,0.000,3665.000,13176,703,0,38,29 -go,root-help,238,4677.958,0.000,4214.000,13220,704,0,41,39 -rust,root-help,238,1901.071,918.000,918.000,6476,349,0,1,1 -rust,root-help,239,1638.017,793.000,793.000,6668,353,0,1,1 -go,root-help,239,4189.571,2057.000,2057.000,13164,691,0,20,12 -go,root-help,240,5073.010,2744.000,1829.000,13140,706,0,44,39 -rust,root-help,240,1830.649,0.000,1769.000,6600,351,0,1,2 -rust,root-help,241,1530.396,741.000,741.000,6408,353,0,1,1 -go,root-help,241,3848.600,2507.000,1253.000,13164,700,0,14,8 -go,root-help,242,4927.938,2691.000,1794.000,13176,687,0,35,33 -rust,root-help,242,2208.229,0.000,1848.000,6688,349,0,1,2 -rust,root-help,243,2744.376,0.000,2203.000,6584,350,0,1,2 -go,root-help,243,4201.894,0.000,4094.000,13180,684,0,39,31 -go,root-help,244,4498.120,1005.000,3015.000,13176,697,0,40,33 -rust,root-help,244,1764.526,0.000,1699.000,6600,351,0,1,2 -rust,root-help,245,1674.795,799.000,799.000,6744,349,0,0,3 -go,root-help,245,4415.367,1079.000,3238.000,13164,700,0,15,11 -go,root-help,246,4100.924,2013.000,2013.000,13176,692,0,14,7 -rust,root-help,246,1997.181,0.000,1944.000,6528,349,0,1,1 -rust,root-help,247,2161.240,887.000,887.000,6588,350,0,1,2 -go,root-help,247,4326.077,1690.000,2536.000,13172,691,0,42,38 -go,root-help,248,5434.660,1966.000,2950.000,13184,705,0,45,39 -rust,root-help,248,2173.935,1054.000,1054.000,6508,350,0,1,1 -rust,root-help,249,2058.947,0.000,1986.000,6412,356,0,1,1 -go,root-help,249,5973.422,3051.000,2034.000,13180,691,0,47,45 -go,root-help,250,5354.709,0.000,5254.000,13184,700,0,46,39 -rust,root-help,250,2255.578,916.000,916.000,6808,351,0,1,2 -rust,root-help,251,1542.689,1482.000,0.000,6368,351,0,1,2 -go,root-help,251,4344.593,1954.000,1954.000,13084,691,0,38,36 -go,root-help,252,4057.592,1982.000,1982.000,13176,703,0,38,32 -rust,root-help,252,2187.390,885.000,885.000,6528,349,0,1,2 -rust,root-help,253,1572.454,0.000,1515.000,6208,356,0,1,2 -go,root-help,253,4572.361,1044.000,3133.000,13220,692,0,43,39 -go,root-help,254,4180.826,0.000,4089.000,13220,706,0,38,35 -rust,root-help,254,2316.792,898.000,898.000,6568,350,0,1,2 -rust,root-help,255,1558.418,0.000,1502.000,6604,350,0,1,1 -go,root-help,255,4608.418,1672.000,2508.000,13220,692,0,40,34 -go,root-help,256,4621.873,2081.000,2081.000,13140,700,0,38,35 -rust,root-help,256,1794.190,0.000,1731.000,6684,351,0,1,2 -rust,root-help,257,2569.227,0.000,2046.000,6236,356,0,1,2 -go,root-help,257,3831.099,937.000,2811.000,13180,687,0,14,9 -go,root-help,258,4313.284,1941.000,1941.000,13148,698,0,26,22 -rust,root-help,258,2092.461,0.000,2019.000,6560,350,0,1,1 -rust,root-help,259,1648.787,0.000,1589.000,6632,348,0,1,1 -go,root-help,259,4000.505,1812.000,1812.000,13164,687,0,16,10 -go,root-help,260,3937.687,1278.000,2556.000,13176,698,0,15,9 -rust,root-help,260,1919.406,922.000,922.000,6612,351,0,1,2 -rust,root-help,261,1733.285,0.000,1678.000,6720,350,0,1,1 -go,root-help,261,4144.235,3038.000,1012.000,13084,695,0,36,34 -go,root-help,262,4509.001,3316.000,1105.000,13148,711,0,43,38 -rust,root-help,262,2289.962,0.000,1882.000,6536,349,0,1,2 -rust,root-help,263,1571.632,0.000,1514.000,6520,350,0,1,2 -go,root-help,263,4372.494,2132.000,2132.000,13036,680,0,38,33 -go,root-help,264,4107.807,2016.000,2016.000,13220,706,0,41,37 -rust,root-help,264,1802.296,0.000,1752.000,6236,356,0,1,1 -rust,root-help,265,1948.492,0.000,1564.000,6624,348,0,1,1 -go,root-help,265,4855.852,873.000,3493.000,13180,692,0,39,34 -go,root-help,266,4352.457,853.000,3413.000,13164,688,0,16,9 -rust,root-help,266,1680.618,1631.000,0.000,6524,350,0,1,1 -rust,root-help,267,1512.793,0.000,1455.000,6684,351,0,1,1 -go,root-help,267,3952.624,1792.000,1792.000,13180,701,0,40,32 -go,root-help,268,3920.025,964.000,2893.000,13220,703,0,41,35 -rust,root-help,268,1665.068,1621.000,0.000,6680,350,0,1,1 -rust,root-help,269,1369.894,0.000,1320.000,6644,349,0,1,1 -go,root-help,269,4176.947,1898.000,1898.000,13220,694,0,41,37 -go,root-help,270,4170.915,2039.000,2039.000,13148,704,0,26,25 -rust,root-help,270,1754.145,848.000,848.000,6684,350,0,1,1 -rust,root-help,271,1406.613,0.000,1356.000,6608,349,0,1,1 -go,root-help,271,4088.571,3011.000,1003.000,13180,705,0,40,37 -go,root-help,272,5124.608,1697.000,2545.000,13044,695,0,18,13 -rust,root-help,272,2181.388,1055.000,1055.000,6668,349,0,1,1 -rust,root-help,273,2279.502,943.000,943.000,6608,350,0,1,2 -go,root-help,273,4469.286,3269.000,1089.000,13216,709,0,33,29 -go,root-help,274,4825.906,2205.000,2205.000,13180,707,0,42,37 -rust,root-help,274,2025.896,0.000,1973.000,6212,356,0,1,1 -rust,root-help,275,1502.092,0.000,1455.000,6604,350,0,1,1 -go,root-help,275,4218.916,3089.000,1029.000,13176,697,0,41,34 -go,root-help,276,4497.529,1762.000,2644.000,13184,691,0,39,33 -rust,root-help,276,2567.655,2065.000,0.000,6712,349,0,1,2 -rust,root-help,277,2031.326,0.000,1953.000,6676,350,0,1,1 -go,root-help,277,4789.077,1725.000,2588.000,13148,688,0,15,11 -go,root-help,278,4258.641,968.000,2905.000,13164,691,0,40,33 -rust,root-help,278,1776.668,0.000,1724.000,6720,350,0,1,2 -rust,root-help,279,1660.459,804.000,804.000,6812,351,0,1,1 -go,root-help,279,4419.132,3019.000,1006.000,13148,703,0,15,12 -go,root-help,280,4725.898,2775.000,1850.000,13148,687,0,44,37 -rust,root-help,280,2118.890,0.000,2052.000,6816,351,0,1,1 -rust,root-help,281,2233.976,2132.000,0.000,6664,350,0,1,1 -go,root-help,281,5264.119,1576.000,3152.000,13148,691,0,43,37 -go,root-help,282,4771.192,2333.000,2333.000,13084,692,0,32,28 -rust,root-help,282,2335.617,1496.000,748.000,6732,350,0,1,1 -rust,root-help,283,1679.896,0.000,1615.000,6596,349,0,1,1 -go,root-help,283,4903.702,2169.000,2169.000,13176,691,0,43,37 -go,root-help,284,5192.946,780.000,3903.000,13148,692,0,44,38 -rust,root-help,284,2092.461,0.000,2010.000,6624,350,0,1,2 -rust,root-help,285,2008.053,0.000,1932.000,6212,356,0,1,1 -go,root-help,285,4912.549,1759.000,2639.000,13160,697,0,27,26 -go,root-help,286,4210.811,1026.000,3079.000,13148,688,0,15,11 -rust,root-help,286,2250.388,1086.000,1086.000,6720,350,0,1,2 -rust,root-help,287,1663.134,1607.000,0.000,6664,350,0,1,1 -go,root-help,287,4553.815,1932.000,1932.000,13276,693,0,40,33 -go,root-help,288,4282.616,838.000,3353.000,13148,691,0,40,34 -rust,root-help,288,2012.070,1947.000,0.000,6596,351,0,1,1 -rust,root-help,289,1479.119,1431.000,0.000,6484,350,0,1,1 -go,root-help,289,3582.310,876.000,2630.000,13164,698,0,16,11 -go,root-help,290,3985.877,3896.000,0.000,13148,709,0,41,35 -rust,root-help,290,2627.517,0.000,2153.000,6760,348,0,1,2 -rust,root-help,291,1897.744,908.000,908.000,6712,350,0,1,2 -go,root-help,291,5005.143,912.000,3648.000,13084,702,0,27,26 -go,root-help,292,4301.651,832.000,3329.000,13164,704,0,40,36 -rust,root-help,292,1839.517,0.000,1773.000,6628,350,0,1,1 -rust,root-help,293,1955.684,0.000,1891.000,6800,350,0,1,1 -go,root-help,293,4207.635,3077.000,1025.000,13148,701,0,15,9 -go,root-help,294,4638.354,1667.000,2501.000,13244,701,0,41,36 -rust,root-help,294,1867.278,0.000,1805.000,6624,350,0,1,2 -rust,root-help,295,1935.887,772.000,772.000,6792,350,0,1,2 -go,root-help,295,4441.102,2176.000,2176.000,13176,710,0,18,13 -go,root-help,296,4218.906,2072.000,2072.000,13216,702,0,40,35 -rust,root-help,296,2115.173,1021.000,1021.000,6688,350,0,1,1 -rust,root-help,297,2753.442,748.000,1496.000,6516,350,0,1,2 -go,root-help,297,4471.049,1740.000,2610.000,13084,692,0,39,33 -go,root-help,298,4062.081,998.000,2995.000,13180,700,0,15,11 -rust,root-help,298,1657.984,1609.000,0.000,6724,350,0,1,1 -rust,root-help,299,1646.003,794.000,794.000,6640,350,0,1,2 -go,root-help,299,4294.549,3107.000,1035.000,13148,692,0,40,33 -go,episodes-create-help,0,5954.396,2669.000,2669.000,13312,698,0,20,14 -rust,episodes-create-help,0,2741.410,1312.000,1312.000,6576,352,0,1,2 -rust,episodes-create-help,1,2395.540,0.000,2300.000,6796,351,0,1,1 -go,episodes-create-help,1,5665.865,922.000,4612.000,13308,713,0,21,14 -go,episodes-create-help,2,5473.584,1075.000,4301.000,13300,689,0,22,16 -rust,episodes-create-help,2,3116.085,637.000,1913.000,6700,351,0,1,3 -rust,episodes-create-help,3,2301.864,2216.000,0.000,6684,352,0,1,2 -go,episodes-create-help,3,5510.533,970.000,3881.000,13276,688,0,21,13 -go,episodes-create-help,4,4955.510,971.000,3887.000,13304,692,0,44,40 -rust,episodes-create-help,4,2600.615,840.000,1680.000,6880,353,0,1,1 -rust,episodes-create-help,5,1978.586,0.000,1900.000,6720,351,0,1,1 -go,episodes-create-help,5,4658.942,2550.000,1700.000,13292,707,0,44,39 -go,episodes-create-help,6,5268.828,3096.000,2064.000,13312,693,0,45,42 -rust,episodes-create-help,6,2364.472,1148.000,1148.000,6728,352,0,1,1 -rust,episodes-create-help,7,2391.082,1285.000,642.000,6876,353,0,1,2 -go,episodes-create-help,7,4048.145,907.000,2721.000,13348,695,0,17,10 -go,episodes-create-help,8,4002.179,896.000,2688.000,13292,693,0,19,18 -rust,episodes-create-help,8,1722.686,0.000,1674.000,6836,352,0,1,2 -rust,episodes-create-help,9,1698.923,0.000,1326.000,6684,352,0,1,2 -go,episodes-create-help,9,4360.953,1712.000,2568.000,13348,706,0,44,41 -go,episodes-create-help,10,4261.626,1925.000,1925.000,13288,692,0,39,33 -rust,episodes-create-help,10,1607.308,0.000,1539.000,6736,350,0,1,2 -rust,episodes-create-help,11,1258.734,0.000,1220.000,6828,351,0,1,1 -go,episodes-create-help,11,3499.374,2296.000,1148.000,13304,704,0,32,27 -go,episodes-create-help,12,4634.887,1476.000,2214.000,13304,700,0,34,32 -rust,episodes-create-help,12,1587.892,0.000,1541.000,6720,351,0,0,2 -rust,episodes-create-help,13,1268.202,0.000,1226.000,6716,352,0,1,1 -go,episodes-create-help,13,3421.168,2242.000,1121.000,13304,686,0,34,28 -go,episodes-create-help,14,3982.792,904.000,2712.000,13288,702,0,38,25 -rust,episodes-create-help,14,1619.002,1571.000,0.000,6772,351,0,1,2 -rust,episodes-create-help,15,1676.259,672.000,672.000,6816,352,0,1,2 -go,episodes-create-help,15,3477.183,1139.000,2278.000,13300,707,0,31,26 -go,episodes-create-help,16,3413.612,1122.000,2244.000,13276,700,0,36,29 -rust,episodes-create-help,16,2137.927,0.000,1729.000,6612,350,0,1,2 -rust,episodes-create-help,17,1411.772,1367.000,0.000,6772,351,0,1,1 -go,episodes-create-help,17,4166.307,941.000,2823.000,13312,687,0,37,32 -go,episodes-create-help,18,4158.242,2790.000,930.000,13288,708,0,34,29 -rust,episodes-create-help,18,1948.269,0.000,1609.000,6664,351,0,1,1 -rust,episodes-create-help,19,1278.622,618.000,618.000,6704,351,0,1,2 -go,episodes-create-help,19,3682.097,1115.000,2231.000,13268,697,0,33,28 -go,episodes-create-help,20,4551.330,895.000,3581.000,13344,699,0,18,11 -rust,episodes-create-help,20,2053.147,0.000,1719.000,6820,350,0,1,2 -rust,episodes-create-help,21,1309.420,0.000,1265.000,6788,351,0,1,2 -go,episodes-create-help,21,4263.129,1943.000,1943.000,13308,699,0,40,33 -go,episodes-create-help,22,3919.042,0.000,3823.000,13292,694,0,39,33 -rust,episodes-create-help,22,2005.577,0.000,1674.000,6824,352,0,1,2 -rust,episodes-create-help,23,1373.650,0.000,1328.000,6708,352,0,1,2 -go,episodes-create-help,23,3542.255,2328.000,1164.000,13212,701,0,40,31 -go,episodes-create-help,24,4286.703,0.000,4206.000,13344,709,0,36,33 -rust,episodes-create-help,24,1728.097,1685.000,0.000,6832,351,0,1,1 -rust,episodes-create-help,25,1293.982,601.000,601.000,6624,350,0,1,1 -go,episodes-create-help,25,3787.145,3406.000,0.000,13288,693,0,39,27 -go,episodes-create-help,26,3536.524,1066.000,2132.000,13312,693,0,28,21 -rust,episodes-create-help,26,1448.130,0.000,1410.000,6500,356,0,1,1 -rust,episodes-create-help,27,1306.343,0.000,1264.000,6496,357,0,1,2 -go,episodes-create-help,27,3957.766,903.000,2709.000,13308,693,0,40,34 -go,episodes-create-help,28,3543.587,2142.000,1071.000,13276,704,0,18,15 -rust,episodes-create-help,28,2495.699,1934.000,0.000,6744,351,0,1,2 -rust,episodes-create-help,29,1649.810,0.000,1604.000,6940,351,0,1,2 -go,episodes-create-help,29,4007.299,913.000,2740.000,13212,690,0,37,33 -go,episodes-create-help,30,3663.913,2382.000,1191.000,13304,703,0,38,33 -rust,episodes-create-help,30,1694.503,825.000,825.000,6832,350,0,1,1 -rust,episodes-create-help,31,1748.154,0.000,1395.000,6888,351,0,1,2 -go,episodes-create-help,31,3632.334,3575.000,0.000,13308,693,0,38,35 -go,episodes-create-help,32,3589.813,872.000,2617.000,13308,685,0,39,33 -rust,episodes-create-help,32,1493.395,1454.000,0.000,6700,352,0,1,1 -rust,episodes-create-help,33,1427.170,1378.000,0.000,6864,353,0,1,1 -go,episodes-create-help,33,3634.338,1192.000,2385.000,13308,705,0,15,12 -go,episodes-create-help,34,3429.434,777.000,2333.000,13292,693,0,35,28 -rust,episodes-create-help,34,1401.974,0.000,1366.000,6876,351,0,1,1 -rust,episodes-create-help,35,1649.919,0.000,1327.000,6888,352,0,1,2 -go,episodes-create-help,35,3746.448,2754.000,918.000,13212,707,0,42,37 -go,episodes-create-help,36,3846.778,1157.000,2314.000,13292,689,0,15,9 -rust,episodes-create-help,36,1428.984,0.000,1389.000,6784,351,0,1,2 -rust,episodes-create-help,37,1171.471,0.000,1138.000,6532,355,0,1,1 -go,episodes-create-help,37,3791.563,1863.000,1863.000,13332,710,0,36,27 -go,episodes-create-help,38,4040.210,2715.000,905.000,13276,698,0,37,31 -rust,episodes-create-help,38,1797.336,744.000,744.000,6892,352,0,1,2 -rust,episodes-create-help,39,1241.141,0.000,1202.000,6804,351,0,1,2 -go,episodes-create-help,39,3380.541,1107.000,2215.000,13276,692,0,33,28 -go,episodes-create-help,40,3609.631,1774.000,1774.000,13292,706,0,36,28 -rust,episodes-create-help,40,1571.202,767.000,767.000,6496,356,0,1,1 -rust,episodes-create-help,41,1619.543,0.000,1249.000,6712,352,0,1,2 -go,episodes-create-help,41,3785.081,855.000,2567.000,13308,689,0,16,12 -go,episodes-create-help,42,5632.271,2740.000,2740.000,13236,698,0,31,27 -rust,episodes-create-help,42,2434.814,0.000,2362.000,6860,352,0,1,1 -rust,episodes-create-help,43,1722.766,0.000,1661.000,6492,356,0,1,1 -go,episodes-create-help,43,5825.223,2418.000,2418.000,13288,704,0,44,44 -go,episodes-create-help,44,4768.487,2332.000,2332.000,13304,703,0,35,29 -rust,episodes-create-help,44,2127.327,0.000,2068.000,6884,350,0,1,1 -rust,episodes-create-help,45,2069.056,0.000,1994.000,6760,351,0,1,1 -go,episodes-create-help,45,5276.993,4763.000,0.000,13276,693,0,30,24 -go,episodes-create-help,46,5341.324,2838.000,1892.000,13288,688,0,15,10 -rust,episodes-create-help,46,2194.012,712.000,1424.000,6800,352,0,1,1 -rust,episodes-create-help,47,2519.624,0.000,2057.000,6848,352,0,1,2 -go,episodes-create-help,47,4648.363,2722.000,1814.000,13308,705,0,40,35 -go,episodes-create-help,48,4852.296,1767.000,2651.000,13276,692,0,43,35 -rust,episodes-create-help,48,2901.581,1175.000,1175.000,6792,352,0,1,2 -rust,episodes-create-help,49,1887.897,0.000,1817.000,6856,352,0,1,1 -go,episodes-create-help,49,5018.708,1207.000,3623.000,13292,705,0,46,38 -go,episodes-create-help,50,5886.318,2125.000,3188.000,13308,690,0,20,17 -rust,episodes-create-help,50,2644.769,0.000,2176.000,6684,351,0,1,2 -rust,episodes-create-help,51,2366.485,1139.000,1139.000,6724,353,0,1,2 -go,episodes-create-help,51,4701.532,3438.000,1146.000,13216,687,0,17,13 -go,episodes-create-help,52,4194.530,3081.000,1027.000,13304,709,0,20,12 -rust,episodes-create-help,52,2669.575,740.000,1481.000,6876,352,0,1,2 -rust,episodes-create-help,53,1945.615,0.000,1862.000,6804,351,0,1,2 -go,episodes-create-help,53,4953.295,3806.000,951.000,13276,692,0,21,13 -go,episodes-create-help,54,5363.806,3167.000,2111.000,13288,693,0,48,41 -rust,episodes-create-help,54,2849.062,0.000,2354.000,6780,350,0,1,2 -rust,episodes-create-help,55,1837.432,0.000,1748.000,6756,350,0,1,3 -go,episodes-create-help,55,4995.725,3914.000,978.000,13300,687,0,46,43 -go,episodes-create-help,56,5021.093,2675.000,1783.000,13304,707,0,41,35 -rust,episodes-create-help,56,1867.479,902.000,902.000,6880,353,0,1,2 -rust,episodes-create-help,57,1810.651,872.000,872.000,6876,351,0,1,1 -go,episodes-create-help,57,4841.626,2612.000,1741.000,13308,703,0,41,36 -go,episodes-create-help,58,4520.253,0.000,4446.000,13312,709,0,42,35 -rust,episodes-create-help,58,1956.685,1893.000,0.000,6840,350,0,1,1 -rust,episodes-create-help,59,2359.093,631.000,1263.000,6640,350,0,1,2 -go,episodes-create-help,59,4284.679,3142.000,1047.000,13344,694,0,41,36 -go,episodes-create-help,60,5257.047,958.000,3835.000,13304,698,0,44,39 -rust,episodes-create-help,60,1990.320,0.000,1884.000,6668,354,0,1,2 -rust,episodes-create-help,61,1590.388,766.000,766.000,6768,351,0,1,1 -go,episodes-create-help,61,4480.989,1015.000,3045.000,13288,685,0,39,33 -go,episodes-create-help,62,4243.050,1037.000,3111.000,13292,682,0,39,33 -rust,episodes-create-help,62,2265.697,1721.000,0.000,6724,354,0,1,2 -rust,episodes-create-help,63,1725.041,0.000,1676.000,6684,351,0,1,1 -go,episodes-create-help,63,4630.128,2066.000,2066.000,13308,693,0,42,35 -go,episodes-create-help,64,4894.194,3502.000,875.000,13308,702,0,25,20 -rust,episodes-create-help,64,2117.058,0.000,2052.000,6900,353,0,1,2 -rust,episodes-create-help,65,2522.468,0.000,2055.000,6696,350,0,1,2 -go,episodes-create-help,65,4833.481,2352.000,2352.000,13276,700,0,17,13 -go,episodes-create-help,66,4377.444,1073.000,3219.000,13268,689,0,15,11 -rust,episodes-create-help,66,1863.753,1793.000,0.000,6848,351,0,1,2 -rust,episodes-create-help,67,1553.818,1500.000,0.000,6664,350,0,1,1 -go,episodes-create-help,67,4408.081,2376.000,1584.000,13304,688,0,12,12 -go,episodes-create-help,68,4527.033,2060.000,2060.000,13292,693,0,35,31 -rust,episodes-create-help,68,2109.623,0.000,2049.000,6792,351,0,1,1 -rust,episodes-create-help,69,1856.328,0.000,1779.000,6824,351,0,1,1 -go,episodes-create-help,69,4449.268,1718.000,2577.000,13312,700,0,40,38 -go,episodes-create-help,70,4083.131,1001.000,3005.000,13212,692,0,40,35 -rust,episodes-create-help,70,1787.980,871.000,871.000,6716,354,0,1,1 -rust,episodes-create-help,71,1306.824,0.000,1269.000,6876,352,0,1,1 -go,episodes-create-help,71,4239.245,0.000,3829.000,13268,693,0,30,27 -go,episodes-create-help,72,4166.006,2030.000,2030.000,13344,684,0,41,36 -rust,episodes-create-help,72,2193.001,586.000,1172.000,6624,354,0,1,2 -rust,episodes-create-help,73,1449.954,0.000,1404.000,6876,352,0,1,1 -go,episodes-create-help,73,4146.139,2825.000,941.000,13348,703,0,40,33 -go,episodes-create-help,74,4745.336,1032.000,3098.000,13276,693,0,41,34 -rust,episodes-create-help,74,1909.457,1842.000,0.000,6712,352,0,1,2 -rust,episodes-create-help,75,2033.500,0.000,1960.000,6804,351,0,1,1 -go,episodes-create-help,75,5359.980,1976.000,2964.000,13348,710,0,46,43 -go,episodes-create-help,76,5378.705,809.000,4045.000,13304,712,0,28,26 -rust,episodes-create-help,76,2426.659,2024.000,0.000,6888,350,0,1,2 -rust,episodes-create-help,77,1638.229,788.000,788.000,6840,350,0,1,2 -go,episodes-create-help,77,4181.275,2054.000,2054.000,13312,695,0,41,33 -go,episodes-create-help,78,5590.804,1685.000,3371.000,13312,701,0,41,36 -rust,episodes-create-help,78,2334.535,0.000,2266.000,6884,350,0,1,1 -rust,episodes-create-help,79,1984.448,0.000,1917.000,6576,353,0,1,1 -go,episodes-create-help,79,5100.391,3685.000,921.000,13300,702,0,27,24 -go,episodes-create-help,80,4961.692,1782.000,2673.000,13308,708,0,21,18 -rust,episodes-create-help,80,2147.405,0.000,2083.000,6408,357,0,1,2 -rust,episodes-create-help,81,2246.211,1806.000,0.000,6756,349,0,1,2 -go,episodes-create-help,81,5194.890,1014.000,4056.000,13292,700,0,46,38 -go,episodes-create-help,82,4493.361,2031.000,2031.000,13344,694,0,34,30 -rust,episodes-create-help,82,2101.668,1020.000,1020.000,6788,349,0,1,1 -rust,episodes-create-help,83,1957.837,944.000,944.000,6744,351,0,1,1 -go,episodes-create-help,83,4896.950,2631.000,1754.000,13308,693,0,43,35 -go,episodes-create-help,84,4726.420,2562.000,1708.000,13308,693,0,21,19 -rust,episodes-create-help,84,1777.038,862.000,862.000,6632,351,0,1,2 -rust,episodes-create-help,85,1875.974,751.000,751.000,6828,351,0,1,2 -go,episodes-create-help,85,3784.920,2473.000,1236.000,13220,707,0,40,35 -go,episodes-create-help,86,4568.653,826.000,3304.000,13292,702,0,40,31 -rust,episodes-create-help,86,1904.358,0.000,1840.000,6632,353,0,1,2 -rust,episodes-create-help,87,1405.951,0.000,1360.000,6668,349,0,1,1 -go,episodes-create-help,87,3926.166,1917.000,1917.000,13292,706,0,14,8 -go,episodes-create-help,88,5513.959,1546.000,3092.000,13308,699,0,44,39 -rust,episodes-create-help,88,1954.502,1896.000,0.000,6624,354,0,1,2 -rust,episodes-create-help,89,1804.431,0.000,1747.000,6868,353,0,1,1 -go,episodes-create-help,89,4747.158,1060.000,3181.000,13292,706,0,28,23 -go,episodes-create-help,90,4147.842,1016.000,3049.000,13288,701,0,41,33 -rust,episodes-create-help,90,2125.303,0.000,2058.000,6768,353,0,1,1 -rust,episodes-create-help,91,1433.082,1388.000,0.000,6908,352,0,1,1 -go,episodes-create-help,91,4888.794,1758.000,2637.000,13300,704,0,45,40 -go,episodes-create-help,92,4228.433,2868.000,956.000,13300,693,0,37,30 -rust,episodes-create-help,92,1810.722,878.000,878.000,6792,351,0,1,1 -rust,episodes-create-help,93,1919.194,929.000,929.000,6712,352,0,1,1 -go,episodes-create-help,93,4710.970,0.000,3968.000,13300,701,0,30,22 -go,episodes-create-help,94,3911.517,1914.000,1914.000,13292,692,0,38,34 -rust,episodes-create-help,94,2122.858,865.000,865.000,6408,356,0,1,2 -rust,episodes-create-help,95,1523.943,0.000,1469.000,6600,350,0,1,2 -go,episodes-create-help,95,4495.896,816.000,3267.000,13308,703,0,41,35 -go,episodes-create-help,96,5181.895,1157.000,3473.000,13300,703,0,16,12 -rust,episodes-create-help,96,1740.680,1683.000,0.000,6784,351,0,1,2 -rust,episodes-create-help,97,1720.623,0.000,1667.000,6888,351,0,1,1 -go,episodes-create-help,97,4328.281,2882.000,960.000,13300,683,0,14,8 -go,episodes-create-help,98,4273.157,0.000,3902.000,13288,692,0,13,8 -rust,episodes-create-help,98,1959.832,950.000,950.000,6728,351,0,1,2 -rust,episodes-create-help,99,1533.612,0.000,1489.000,6792,351,0,1,1 -go,episodes-create-help,99,4505.534,1106.000,3320.000,13276,692,0,45,41 -go,episodes-create-help,100,5298.413,1937.000,2906.000,13308,707,0,43,39 -rust,episodes-create-help,100,1727.987,837.000,837.000,6820,350,0,1,2 -rust,episodes-create-help,101,1567.105,757.000,757.000,6740,351,0,1,1 -go,episodes-create-help,101,4312.341,2108.000,2108.000,13276,690,0,16,13 -go,episodes-create-help,102,4042.515,2971.000,990.000,13304,691,0,14,7 -rust,episodes-create-help,102,2346.939,1131.000,1131.000,6708,350,0,1,2 -rust,episodes-create-help,103,1614.312,0.000,1543.000,6664,355,0,1,1 -go,episodes-create-help,103,4123.648,934.000,2804.000,13344,694,0,21,16 -go,episodes-create-help,104,5568.892,2471.000,2471.000,13212,689,0,39,34 -rust,episodes-create-help,104,2163.344,0.000,2102.000,6872,350,0,1,1 -rust,episodes-create-help,105,1653.326,799.000,799.000,6644,350,0,1,1 -go,episodes-create-help,105,3872.867,0.000,3766.000,13312,685,0,40,35 -go,episodes-create-help,106,4735.776,3238.000,1079.000,13308,689,0,38,33 -rust,episodes-create-help,106,2236.682,0.000,1752.000,6788,352,0,1,1 -rust,episodes-create-help,107,1603.682,1552.000,0.000,6728,351,0,1,1 -go,episodes-create-help,107,3989.755,2928.000,976.000,13344,700,0,16,13 -go,episodes-create-help,108,4033.168,2965.000,988.000,13308,688,0,16,13 -rust,episodes-create-help,108,2114.983,883.000,883.000,6756,350,0,1,2 -rust,episodes-create-help,109,1681.028,0.000,1621.000,6580,351,0,1,1 -go,episodes-create-help,109,5254.682,2873.000,1915.000,13304,699,0,44,36 -go,episodes-create-help,110,4356.975,841.000,3364.000,13344,698,0,39,30 -rust,episodes-create-help,110,1934.354,0.000,1885.000,6660,350,0,1,1 -rust,episodes-create-help,111,1825.149,1760.000,0.000,6584,351,0,1,2 -go,episodes-create-help,111,5033.596,1977.000,2966.000,13308,702,0,46,37 -go,episodes-create-help,112,4639.737,1821.000,2732.000,13212,685,0,14,8 -rust,episodes-create-help,112,2323.786,956.000,956.000,6764,350,0,1,2 -rust,episodes-create-help,113,1781.316,858.000,858.000,6492,357,0,1,1 -go,episodes-create-help,113,4241.930,1037.000,3111.000,13304,687,0,15,11 -go,episodes-create-help,114,4899.135,3848.000,962.000,13304,696,0,43,37 -rust,episodes-create-help,114,2283.149,1107.000,1107.000,6492,357,0,1,1 -rust,episodes-create-help,115,2282.548,1857.000,0.000,6964,353,0,1,2 -go,episodes-create-help,115,4059.156,2976.000,992.000,13292,689,0,15,11 -go,episodes-create-help,116,3999.454,3915.000,0.000,13308,687,0,39,37 -rust,episodes-create-help,116,1868.181,1801.000,0.000,6916,350,0,1,1 -rust,episodes-create-help,117,2260.316,0.000,1818.000,6864,351,0,1,2 -go,episodes-create-help,117,4614.317,2254.000,2254.000,13312,705,0,45,40 -go,episodes-create-help,118,4580.334,2683.000,1789.000,13276,692,0,19,12 -rust,episodes-create-help,118,2119.613,1023.000,1023.000,6676,350,0,1,1 -rust,episodes-create-help,119,1723.197,0.000,1668.000,6876,353,0,1,1 -go,episodes-create-help,119,4476.639,1014.000,3043.000,13348,686,0,16,13 -go,episodes-create-help,120,4145.759,4053.000,0.000,13304,696,0,14,8 -rust,episodes-create-help,120,2332.432,1245.000,622.000,6736,352,0,1,2 -rust,episodes-create-help,121,1541.485,0.000,1492.000,6876,352,0,1,1 -go,episodes-create-help,121,4793.114,3413.000,853.000,13308,688,0,20,13 -go,episodes-create-help,122,4626.242,908.000,3633.000,13344,689,0,16,12 -rust,episodes-create-help,122,2108.501,1022.000,1022.000,6880,353,0,1,1 -rust,episodes-create-help,123,1994.367,0.000,1625.000,6792,351,0,1,2 -go,episodes-create-help,123,3858.709,1891.000,1891.000,13348,694,0,38,35 -go,episodes-create-help,124,4125.181,1833.000,1833.000,13308,698,0,18,16 -rust,episodes-create-help,124,2183.742,0.000,2108.000,6872,352,0,1,2 -rust,episodes-create-help,125,1972.055,0.000,1909.000,6852,353,0,1,1 -go,episodes-create-help,125,4163.742,3057.000,1019.000,13276,692,0,29,27 -go,episodes-create-help,126,4305.919,3153.000,1051.000,13308,696,0,40,33 -rust,episodes-create-help,126,2039.561,0.000,1977.000,6740,351,0,1,1 -rust,episodes-create-help,127,1621.336,0.000,1563.000,6672,349,0,1,1 -go,episodes-create-help,127,4837.478,1746.000,2619.000,13312,692,0,37,30 -go,episodes-create-help,128,4510.182,1637.000,2455.000,13312,692,0,40,36 -rust,episodes-create-help,128,2145.491,0.000,2067.000,6740,351,0,1,2 -rust,episodes-create-help,129,1826.712,0.000,1760.000,6848,350,0,1,1 -go,episodes-create-help,129,4482.081,1011.000,3033.000,13292,692,0,38,35 -go,episodes-create-help,130,3874.028,951.000,2855.000,13288,702,0,19,12 -rust,episodes-create-help,130,2379.670,1972.000,0.000,6988,353,0,1,2 -rust,episodes-create-help,131,1858.011,0.000,1795.000,6684,355,0,1,2 -go,episodes-create-help,131,4519.241,2458.000,1638.000,13344,686,0,40,34 -go,episodes-create-help,132,4702.915,1061.000,3184.000,13308,703,0,37,32 -rust,episodes-create-help,132,2012.741,0.000,1957.000,6504,356,0,1,1 -rust,episodes-create-help,133,1759.526,1687.000,0.000,6680,353,0,1,1 -go,episodes-create-help,133,5369.106,2225.000,2225.000,13212,693,0,24,19 -go,episodes-create-help,134,5686.342,3067.000,2045.000,13308,685,0,38,33 -rust,episodes-create-help,134,2761.259,756.000,1512.000,6652,351,0,1,2 -rust,episodes-create-help,135,1993.576,961.000,961.000,6864,352,0,1,2 -go,episodes-create-help,135,5026.613,1811.000,2717.000,13276,689,0,41,36 -go,episodes-create-help,136,4482.652,2610.000,1740.000,13276,692,0,42,35 -rust,episodes-create-help,136,1921.589,0.000,1872.000,6744,351,0,1,1 -rust,episodes-create-help,137,2224.620,874.000,874.000,6792,352,0,1,2 -go,episodes-create-help,137,3977.172,1950.000,1950.000,13148,693,0,14,11 -go,episodes-create-help,138,4733.693,2098.000,2098.000,13300,692,0,26,22 -rust,episodes-create-help,138,1843.634,1792.000,0.000,6668,352,0,1,1 -rust,episodes-create-help,139,1736.402,0.000,1667.000,6892,352,0,1,1 -go,episodes-create-help,139,4096.456,1850.000,1850.000,13308,689,0,34,28 -go,episodes-create-help,140,3703.016,2728.000,909.000,13288,692,0,39,33 -rust,episodes-create-help,140,1809.088,877.000,877.000,6476,358,0,1,2 -rust,episodes-create-help,141,1219.822,1184.000,0.000,6688,351,0,1,1 -go,episodes-create-help,141,3436.606,0.000,3385.000,13172,679,0,30,24 -go,episodes-create-help,142,4179.703,2232.000,1488.000,13300,704,0,39,28 -rust,episodes-create-help,142,1666.561,1614.000,0.000,6816,351,0,1,2 -rust,episodes-create-help,143,1565.731,752.000,752.000,6692,350,0,1,2 -go,episodes-create-help,143,4038.046,1985.000,1985.000,13292,698,0,41,34 -go,episodes-create-help,144,5629.715,3393.000,1696.000,13312,702,0,47,42 -rust,episodes-create-help,144,2346.379,0.000,2281.000,6708,351,0,1,1 -rust,episodes-create-help,145,1997.052,956.000,956.000,6808,350,0,1,2 -go,episodes-create-help,145,4304.617,1939.000,1939.000,13308,689,0,39,31 -go,episodes-create-help,146,4582.338,1629.000,2444.000,13344,708,0,41,35 -rust,episodes-create-help,146,2032.087,968.000,968.000,6644,350,0,1,2 -rust,episodes-create-help,147,1742.783,835.000,835.000,6792,351,0,1,2 -go,episodes-create-help,147,4340.655,979.000,2938.000,13276,703,0,41,36 -go,episodes-create-help,148,4793.686,1716.000,2574.000,13276,692,0,42,38 -rust,episodes-create-help,148,2021.588,983.000,983.000,6756,349,0,1,1 -rust,episodes-create-help,149,1840.377,0.000,1767.000,6676,351,0,1,1 -go,episodes-create-help,149,4643.423,1045.000,3137.000,13292,709,0,40,32 -go,episodes-create-help,150,4970.690,974.000,3896.000,13304,700,0,43,38 -rust,episodes-create-help,150,2234.487,604.000,1209.000,6740,351,0,1,2 -rust,episodes-create-help,151,1507.843,1460.000,0.000,6952,351,0,1,1 -go,episodes-create-help,151,4372.514,1981.000,1981.000,13308,687,0,40,34 -go,episodes-create-help,152,4889.286,3267.000,1089.000,13288,690,0,41,35 -rust,episodes-create-help,152,1880.181,913.000,913.000,6876,353,0,1,2 -rust,episodes-create-help,153,2084.856,814.000,814.000,6728,352,0,1,2 -go,episodes-create-help,153,4389.387,1716.000,2574.000,13332,703,0,40,35 -go,episodes-create-help,154,4773.758,2597.000,1731.000,13212,705,0,41,35 -rust,episodes-create-help,154,1847.571,0.000,1791.000,6792,351,0,1,1 -rust,episodes-create-help,155,1944.272,0.000,1563.000,6712,350,0,1,1 -go,episodes-create-help,155,4131.503,1006.000,3018.000,13308,702,0,38,33 -go,episodes-create-help,156,4449.439,2175.000,2175.000,13312,693,0,44,39 -rust,episodes-create-help,156,2127.015,0.000,2060.000,6860,353,0,1,1 -rust,episodes-create-help,157,2129.902,857.000,857.000,6492,357,0,1,2 -go,episodes-create-help,157,4350.804,1581.000,2372.000,13288,693,0,39,33 -go,episodes-create-help,158,4364.169,2144.000,2144.000,13348,694,0,43,38 -rust,episodes-create-help,158,1949.222,0.000,1894.000,6660,351,0,1,1 -rust,episodes-create-help,159,1567.244,757.000,757.000,6788,353,0,1,1 -go,episodes-create-help,159,4361.945,982.000,2946.000,13292,688,0,40,34 -go,episodes-create-help,160,4190.403,2053.000,2053.000,13276,700,0,15,11 -rust,episodes-create-help,160,2285.895,0.000,1894.000,6748,350,0,1,2 -rust,episodes-create-help,161,1746.441,844.000,844.000,6684,353,0,1,2 -go,episodes-create-help,161,4293.626,3154.000,1051.000,13288,689,0,35,34 -go,episodes-create-help,162,4454.940,1012.000,3037.000,13344,693,0,27,24 -rust,episodes-create-help,162,2696.926,730.000,1461.000,6752,350,0,1,3 -rust,episodes-create-help,163,1611.939,1544.000,0.000,6684,355,0,1,2 -go,episodes-create-help,163,4044.569,1838.000,1838.000,13344,698,0,38,35 -go,episodes-create-help,164,3716.322,2738.000,912.000,13300,691,0,38,34 -rust,episodes-create-help,164,1538.330,0.000,1500.000,6884,351,0,1,1 -rust,episodes-create-help,165,1290.846,0.000,1240.000,6600,354,0,1,1 -go,episodes-create-help,165,4583.090,3160.000,1053.000,13344,710,0,44,38 -go,episodes-create-help,166,4598.911,0.000,4248.000,13176,693,0,15,12 -rust,episodes-create-help,166,1612.138,782.000,782.000,6476,359,0,1,2 -rust,episodes-create-help,167,1352.421,1311.000,0.000,6800,352,0,1,1 -go,episodes-create-help,167,3981.150,0.000,3619.000,13308,693,0,40,34 -go,episodes-create-help,168,4116.283,0.000,4031.000,13300,688,0,16,11 -rust,episodes-create-help,168,2243.054,0.000,1793.000,6828,351,0,1,1 -rust,episodes-create-help,169,1552.476,0.000,1502.000,6748,349,0,1,1 -go,episodes-create-help,169,4929.320,2894.000,1929.000,13276,696,0,48,41 -go,episodes-create-help,170,4386.110,1988.000,1988.000,13304,687,0,39,35 -rust,episodes-create-help,170,1850.626,0.000,1797.000,6740,351,0,1,2 -rust,episodes-create-help,171,1654.619,0.000,1577.000,6792,351,0,1,2 -go,episodes-create-help,171,4400.247,0.000,4321.000,13164,697,0,16,12 -go,episodes-create-help,172,4283.126,1546.000,2319.000,13276,704,0,38,34 -rust,episodes-create-help,172,1790.864,1736.000,0.000,6792,352,0,1,2 -rust,episodes-create-help,173,1810.431,0.000,1436.000,6872,353,0,1,2 -go,episodes-create-help,173,3754.413,3691.000,0.000,13276,714,0,40,35 -go,episodes-create-help,174,3954.820,971.000,2913.000,13348,711,0,42,35 -rust,episodes-create-help,174,1787.598,872.000,872.000,6864,352,0,1,1 -rust,episodes-create-help,175,1655.590,0.000,1604.000,6908,351,0,1,1 -go,episodes-create-help,175,4986.818,2686.000,1790.000,13304,706,0,39,32 -go,episodes-create-help,176,4321.319,1565.000,2347.000,13312,687,0,23,18 -rust,episodes-create-help,176,1664.988,0.000,1619.000,6836,350,0,1,2 -rust,episodes-create-help,177,1395.121,0.000,1354.000,6684,351,0,1,1 -go,episodes-create-help,177,4409.884,988.000,2965.000,13288,692,0,41,35 -go,episodes-create-help,178,4291.322,841.000,3364.000,13276,708,0,24,20 -rust,episodes-create-help,178,1903.326,1850.000,0.000,6740,351,0,1,1 -rust,episodes-create-help,179,2139.459,0.000,1476.000,6592,350,0,1,2 -go,episodes-create-help,179,4372.264,1070.000,3210.000,13344,702,0,43,39 -go,episodes-create-help,180,4251.627,2096.000,2096.000,13312,687,0,38,31 -rust,episodes-create-help,180,1858.110,0.000,1808.000,6668,350,0,1,1 -rust,episodes-create-help,181,1790.674,692.000,692.000,6692,351,0,1,2 -go,episodes-create-help,181,4515.934,2032.000,2032.000,13312,694,0,43,36 -go,episodes-create-help,182,3882.454,3817.000,0.000,13288,694,0,39,34 -rust,episodes-create-help,182,1693.089,0.000,1651.000,6792,352,0,1,1 -rust,episodes-create-help,183,1799.972,0.000,1734.000,6756,351,0,1,1 -go,episodes-create-help,183,4303.615,2728.000,909.000,13348,702,0,24,19 -go,episodes-create-help,184,3839.773,1885.000,1885.000,13276,711,0,34,32 -rust,episodes-create-help,184,1606.518,0.000,1571.000,6828,352,0,1,1 -rust,episodes-create-help,185,1648.637,0.000,1317.000,6752,352,0,1,2 -go,episodes-create-help,185,4348.670,784.000,3138.000,13212,703,0,41,37 -go,episodes-create-help,186,4141.531,2005.000,2005.000,13212,703,0,42,37 -rust,episodes-create-help,186,1921.860,935.000,935.000,6748,351,0,1,1 -rust,episodes-create-help,187,1913.706,772.000,772.000,6772,351,0,1,2 -go,episodes-create-help,187,4004.392,3595.000,0.000,13344,693,0,38,31 -go,episodes-create-help,188,4319.846,1940.000,1940.000,13288,707,0,39,35 -rust,episodes-create-help,188,1716.745,834.000,834.000,6756,350,0,1,2 -rust,episodes-create-help,189,1457.037,0.000,1416.000,6780,351,0,1,1 -go,episodes-create-help,189,4112.296,934.000,2803.000,13276,693,0,40,36 -go,episodes-create-help,190,4023.108,897.000,2693.000,13292,701,0,15,11 -rust,episodes-create-help,190,1641.774,795.000,795.000,6856,351,0,1,2 -rust,episodes-create-help,191,1586.330,764.000,764.000,6804,352,0,1,2 -go,episodes-create-help,191,3763.871,0.000,3700.000,13276,698,0,15,11 -go,episodes-create-help,192,4747.329,2419.000,1613.000,13292,703,0,39,31 -rust,episodes-create-help,192,1692.329,0.000,1645.000,6936,352,0,1,2 -rust,episodes-create-help,193,1404.068,0.000,1365.000,6748,351,0,1,1 -go,episodes-create-help,193,3841.528,1883.000,1883.000,13308,686,0,38,33 -go,episodes-create-help,194,5130.367,2014.000,3021.000,13300,699,0,45,38 -rust,episodes-create-help,194,2258.824,0.000,2172.000,6760,351,0,1,1 -rust,episodes-create-help,195,2416.791,0.000,1949.000,6836,353,0,1,2 -go,episodes-create-help,195,4574.623,3580.000,895.000,13292,692,0,34,30 -go,episodes-create-help,196,5369.869,976.000,3906.000,13276,704,0,43,39 -rust,episodes-create-help,196,2592.610,0.000,2144.000,6784,353,0,1,2 -rust,episodes-create-help,197,1770.276,0.000,1704.000,6668,355,0,1,2 -go,episodes-create-help,197,4280.731,837.000,3349.000,13292,701,0,43,38 -go,episodes-create-help,198,4359.079,2136.000,2136.000,13292,700,0,42,37 -rust,episodes-create-help,198,2506.779,0.000,2062.000,6744,352,0,1,2 -rust,episodes-create-help,199,2417.912,0.000,1896.000,6728,351,0,1,1 -go,episodes-create-help,199,4869.579,1894.000,2841.000,13292,709,0,27,20 -go,episodes-create-help,200,5946.442,1799.000,3599.000,13344,708,0,43,41 -rust,episodes-create-help,200,2431.608,974.000,974.000,6780,351,0,1,2 -rust,episodes-create-help,201,1861.969,0.000,1791.000,6824,351,0,1,2 -go,episodes-create-help,201,4209.728,2062.000,2062.000,13308,702,0,42,38 -go,episodes-create-help,202,4780.041,939.000,3757.000,13292,693,0,42,38 -rust,episodes-create-help,202,2523.761,683.000,1367.000,6848,353,0,1,2 -rust,episodes-create-help,203,1880.301,0.000,1812.000,6820,352,0,1,2 -go,episodes-create-help,203,4742.039,3491.000,1163.000,13292,701,0,17,12 -go,episodes-create-help,204,4979.844,2699.000,1799.000,13308,706,0,43,40 -rust,episodes-create-help,204,1859.002,0.000,1809.000,6732,350,0,1,1 -rust,episodes-create-help,205,1577.574,764.000,764.000,6684,352,0,1,1 -go,episodes-create-help,205,5939.017,1704.000,3409.000,13292,696,0,41,38 -go,episodes-create-help,206,4619.609,2269.000,2269.000,13308,701,0,42,38 -rust,episodes-create-help,206,2786.224,0.000,2333.000,6824,351,0,1,2 -rust,episodes-create-help,207,2248.615,0.000,2167.000,6648,350,0,1,2 -go,episodes-create-help,207,4448.447,1738.000,2607.000,13300,683,0,15,11 -go,episodes-create-help,208,4323.412,2119.000,2119.000,13312,692,0,40,38 -rust,episodes-create-help,208,2116.086,0.000,2034.000,6680,355,0,1,1 -rust,episodes-create-help,209,1566.883,0.000,1515.000,6912,353,0,1,1 -go,episodes-create-help,209,5838.708,3506.000,1753.000,13160,693,0,41,37 -go,episodes-create-help,210,4335.345,3186.000,1062.000,13292,700,0,18,11 -rust,episodes-create-help,210,2050.272,0.000,1991.000,6792,350,0,1,1 -rust,episodes-create-help,211,2000.317,814.000,814.000,6672,350,0,1,2 -go,episodes-create-help,211,4238.093,3110.000,1036.000,13212,688,0,32,31 -go,episodes-create-help,212,4650.507,0.000,4554.000,13292,707,0,42,38 -rust,episodes-create-help,212,2036.886,991.000,991.000,6876,353,0,1,1 -rust,episodes-create-help,213,1587.503,0.000,1534.000,6824,352,0,1,1 -go,episodes-create-help,213,4360.000,986.000,2959.000,13344,696,0,37,33 -go,episodes-create-help,214,4467.984,3490.000,872.000,13236,694,0,42,36 -rust,episodes-create-help,214,2308.617,0.000,1885.000,6660,354,0,1,2 -rust,episodes-create-help,215,1624.712,0.000,1570.000,6756,349,0,1,2 -go,episodes-create-help,215,5323.931,962.000,3848.000,13148,682,0,18,16 -go,episodes-create-help,216,5067.570,1827.000,2740.000,13344,688,0,20,17 -rust,episodes-create-help,216,2148.686,1040.000,1040.000,6764,350,0,1,2 -rust,episodes-create-help,217,1653.827,1599.000,0.000,6732,351,0,1,1 -go,episodes-create-help,217,4592.548,2083.000,2083.000,13244,704,0,32,30 -go,episodes-create-help,218,4347.097,828.000,3315.000,13276,701,0,40,35 -rust,episodes-create-help,218,2146.743,1045.000,1045.000,6712,353,0,1,1 -rust,episodes-create-help,219,1423.674,0.000,1376.000,6852,352,0,1,1 -go,episodes-create-help,219,3795.702,930.000,2792.000,13304,688,0,16,11 -go,episodes-create-help,220,5211.500,3550.000,887.000,13276,692,0,42,39 -rust,episodes-create-help,220,2007.351,968.000,968.000,6824,352,0,1,2 -rust,episodes-create-help,221,2245.649,0.000,1766.000,6756,354,0,1,2 -go,episodes-create-help,221,5211.110,3025.000,2016.000,13268,688,0,47,42 -go,episodes-create-help,222,4828.952,2848.000,1898.000,13292,706,0,42,36 -rust,episodes-create-help,222,2618.109,0.000,2175.000,6656,350,0,1,2 -rust,episodes-create-help,223,1916.831,0.000,1849.000,6752,350,0,1,2 -go,episodes-create-help,223,5027.885,1970.000,2956.000,13308,694,0,46,38 -go,episodes-create-help,224,5388.764,1605.000,3211.000,13208,683,0,38,35 -rust,episodes-create-help,224,2107.639,1020.000,1020.000,6668,352,0,1,2 -rust,episodes-create-help,225,2185.005,0.000,1743.000,6888,352,0,1,2 -go,episodes-create-help,225,4763.799,1867.000,2801.000,13304,687,0,16,11 -go,episodes-create-help,226,4976.368,2724.000,1816.000,13292,684,0,46,38 -rust,episodes-create-help,226,2087.933,1015.000,1015.000,6800,352,0,1,1 -rust,episodes-create-help,227,1601.308,0.000,1554.000,6792,349,0,0,2 -go,episodes-create-help,227,4255.193,3128.000,1042.000,13308,693,0,41,35 -go,episodes-create-help,228,4467.103,2186.000,2186.000,13304,691,0,22,15 -rust,episodes-create-help,228,2405.108,0.000,2337.000,6736,351,0,1,1 -rust,episodes-create-help,229,1952.318,0.000,1884.000,6816,350,0,1,1 -go,episodes-create-help,229,5002.277,2696.000,1797.000,13304,685,0,13,8 -go,episodes-create-help,230,5184.219,3808.000,952.000,13240,703,0,44,37 -rust,episodes-create-help,230,1966.745,0.000,1911.000,6720,350,0,1,2 -rust,episodes-create-help,231,1838.504,716.000,716.000,6856,353,0,1,2 -go,episodes-create-help,231,3640.750,1195.000,2391.000,13348,694,0,39,31 -go,episodes-create-help,232,3693.810,893.000,2679.000,13308,693,0,40,31 -rust,episodes-create-help,232,1811.152,0.000,1764.000,6648,350,0,1,1 -rust,episodes-create-help,233,1329.188,0.000,1287.000,6720,352,0,1,1 -go,episodes-create-help,233,3987.310,904.000,2713.000,13348,695,0,36,29 -go,episodes-create-help,234,3209.058,2109.000,1054.000,13304,704,0,24,17 -rust,episodes-create-help,234,1886.614,0.000,1536.000,6868,353,0,1,2 -rust,episodes-create-help,235,1254.877,0.000,1215.000,6784,351,0,1,2 -go,episodes-create-help,235,3550.491,3494.000,0.000,13348,690,0,39,30 -go,episodes-create-help,236,4182.657,1898.000,1898.000,13308,701,0,26,24 -rust,episodes-create-help,236,1703.841,0.000,1650.000,6732,351,0,1,1 -rust,episodes-create-help,237,1733.346,0.000,1393.000,6672,350,0,1,2 -go,episodes-create-help,237,4043.166,1839.000,1839.000,13260,693,0,42,36 -go,episodes-create-help,238,3996.408,2939.000,979.000,13304,704,0,40,33 -rust,episodes-create-help,238,1749.837,0.000,1708.000,6556,352,0,1,1 -rust,episodes-create-help,239,1331.712,0.000,1291.000,6824,352,0,1,1 -go,episodes-create-help,239,4306.120,2888.000,962.000,13292,690,0,37,35 -go,episodes-create-help,240,4201.201,937.000,2813.000,13348,712,0,37,29 -rust,episodes-create-help,240,2169.206,867.000,867.000,6652,352,0,1,2 -rust,episodes-create-help,241,1311.604,1273.000,0.000,6824,352,0,1,1 -go,episodes-create-help,241,4008.671,1969.000,1969.000,13308,694,0,41,37 -go,episodes-create-help,242,4019.020,0.000,3593.000,13344,690,0,15,13 -rust,episodes-create-help,242,2132.526,868.000,868.000,6884,350,0,1,2 -rust,episodes-create-help,243,1490.321,721.000,721.000,6660,356,0,1,2 -go,episodes-create-help,243,4159.203,932.000,2796.000,13276,683,0,34,31 -go,episodes-create-help,244,3468.736,2272.000,1136.000,13276,704,0,13,10 -rust,episodes-create-help,244,1848.001,768.000,768.000,6664,351,0,1,2 -rust,episodes-create-help,245,1283.131,1247.000,0.000,6812,352,0,1,1 -go,episodes-create-help,245,3703.968,0.000,3370.000,13308,693,0,28,22 -go,episodes-create-help,246,3267.959,2140.000,1070.000,13300,690,0,24,18 -rust,episodes-create-help,246,1656.081,0.000,1616.000,6792,351,0,1,1 -rust,episodes-create-help,247,1342.261,1301.000,0.000,6692,350,0,1,1 -go,episodes-create-help,247,3885.230,880.000,2640.000,13312,689,0,16,11 -go,episodes-create-help,248,3425.265,840.000,2520.000,13300,687,0,35,27 -rust,episodes-create-help,248,1661.089,0.000,1622.000,6880,351,0,1,1 -rust,episodes-create-help,249,1362.992,0.000,1325.000,6832,350,0,1,1 -go,episodes-create-help,249,4141.390,886.000,2658.000,13300,693,0,39,33 -go,episodes-create-help,250,3452.666,2236.000,1118.000,13348,708,0,39,32 -rust,episodes-create-help,250,1537.999,0.000,1503.000,6700,351,0,1,1 -rust,episodes-create-help,251,1312.485,0.000,1273.000,6932,352,0,1,1 -go,episodes-create-help,251,4050.830,998.000,2994.000,13312,693,0,38,31 -go,episodes-create-help,252,3948.679,878.000,2635.000,13292,703,0,38,26 -rust,episodes-create-help,252,1901.172,885.000,885.000,6860,351,0,1,1 -rust,episodes-create-help,253,1620.163,786.000,786.000,6744,351,0,1,1 -go,episodes-create-help,253,5394.505,905.000,3622.000,13308,696,0,37,34 -go,episodes-create-help,254,4358.057,2349.000,1566.000,13308,708,0,20,15 -rust,episodes-create-help,254,1804.069,0.000,1745.000,6860,352,0,1,2 -rust,episodes-create-help,255,2188.512,0.000,1700.000,6832,351,0,1,2 -go,episodes-create-help,255,4850.163,3561.000,1187.000,13304,691,0,42,36 -go,episodes-create-help,256,4699.959,2093.000,2093.000,13276,688,0,40,35 -rust,episodes-create-help,256,2243.364,0.000,1884.000,6716,352,0,1,2 -rust,episodes-create-help,257,1518.472,731.000,731.000,6792,351,0,1,2 -go,episodes-create-help,257,4503.290,4409.000,0.000,13288,709,0,22,13 -go,episodes-create-help,258,5505.353,3016.000,2010.000,13216,681,0,16,14 -rust,episodes-create-help,258,2154.608,0.000,2091.000,6864,352,0,1,2 -rust,episodes-create-help,259,2601.037,1405.000,702.000,6768,352,0,1,2 -go,episodes-create-help,259,4934.720,1916.000,2875.000,13308,702,0,46,42 -go,episodes-create-help,260,5267.986,2845.000,1897.000,13264,705,0,43,34 -rust,episodes-create-help,260,2067.225,0.000,2014.000,6688,354,0,1,1 -rust,episodes-create-help,261,1716.144,833.000,833.000,6864,353,0,1,1 -go,episodes-create-help,261,4839.200,949.000,3796.000,13308,704,0,35,30 -go,episodes-create-help,262,4754.263,3488.000,1162.000,13148,679,0,16,11 -rust,episodes-create-help,262,2010.978,837.000,837.000,6656,351,0,1,2 -rust,episodes-create-help,263,1302.687,0.000,1261.000,6844,351,0,1,2 -go,episodes-create-help,263,3972.414,1817.000,1817.000,13212,704,0,15,14 -go,episodes-create-help,264,3699.320,1210.000,2420.000,13276,684,0,38,28 -rust,episodes-create-help,264,1633.439,0.000,1592.000,6812,349,0,1,1 -rust,episodes-create-help,265,1590.087,1268.000,0.000,6880,351,0,1,2 -go,episodes-create-help,265,3518.680,1151.000,2302.000,13344,693,0,38,30 -go,episodes-create-help,266,3699.721,1815.000,1815.000,13276,707,0,36,27 -rust,episodes-create-help,266,1582.323,0.000,1544.000,6728,350,0,1,1 -rust,episodes-create-help,267,1296.646,0.000,1258.000,6872,352,0,1,1 -go,episodes-create-help,267,3856.795,2845.000,948.000,13276,684,0,39,33 -go,episodes-create-help,268,3712.154,1118.000,2236.000,13292,694,0,36,27 -rust,episodes-create-help,268,1807.676,0.000,1493.000,6752,350,0,1,2 -rust,episodes-create-help,269,1211.947,0.000,1177.000,6860,351,0,1,1 -go,episodes-create-help,269,3553.666,1606.000,1606.000,13308,695,0,24,19 -go,episodes-create-help,270,3295.110,2162.000,1081.000,13308,691,0,36,31 -rust,episodes-create-help,270,1516.069,739.000,739.000,6664,350,0,1,1 -rust,episodes-create-help,271,1317.084,1268.000,0.000,6680,355,0,1,2 -go,episodes-create-help,271,3268.071,0.000,3219.000,13276,703,0,22,16 -go,episodes-create-help,272,3716.341,1677.000,1677.000,13300,691,0,33,26 -rust,episodes-create-help,272,1948.059,0.000,1571.000,6812,350,0,1,2 -rust,episodes-create-help,273,1204.012,0.000,1164.000,6720,350,0,1,2 -go,episodes-create-help,273,3309.569,2441.000,813.000,13268,703,0,16,10 -go,episodes-create-help,274,3943.679,1673.000,1673.000,13344,693,0,29,24 -rust,episodes-create-help,274,1510.869,1475.000,0.000,6744,352,0,1,1 -rust,episodes-create-help,275,1196.397,0.000,1161.000,6716,350,0,1,1 -go,episodes-create-help,275,4299.307,2189.000,1459.000,13300,693,0,39,32 -go,episodes-create-help,276,3556.762,2329.000,1164.000,13276,692,0,38,32 -rust,episodes-create-help,276,1577.144,0.000,1538.000,6736,351,0,1,1 -rust,episodes-create-help,277,1289.372,0.000,1248.000,6860,352,0,1,1 -go,episodes-create-help,277,3685.985,0.000,3630.000,13344,698,0,39,33 -go,episodes-create-help,278,4029.170,2746.000,915.000,13300,708,0,40,29 -rust,episodes-create-help,278,2267.740,0.000,1885.000,6876,350,0,1,2 -rust,episodes-create-help,279,1707.919,821.000,821.000,6804,351,0,1,2 -go,episodes-create-help,279,4335.616,978.000,2934.000,13276,705,0,14,9 -go,episodes-create-help,280,4867.704,2865.000,1910.000,13184,681,0,17,11 -rust,episodes-create-help,280,2202.578,0.000,1849.000,6872,353,0,1,2 -rust,episodes-create-help,281,1540.975,1482.000,0.000,6656,351,0,1,2 -go,episodes-create-help,281,4256.937,1934.000,1934.000,13276,703,0,39,33 -go,episodes-create-help,282,3933.280,1929.000,1929.000,13304,684,0,39,33 -rust,episodes-create-help,282,2161.871,0.000,1743.000,6716,350,0,1,2 -rust,episodes-create-help,283,1434.805,0.000,1376.000,6740,352,0,1,2 -go,episodes-create-help,283,4551.821,2229.000,2229.000,13224,693,0,43,40 -go,episodes-create-help,284,4213.045,4133.000,0.000,13292,702,0,39,33 -rust,episodes-create-help,284,2256.509,0.000,1821.000,6804,351,0,1,2 -rust,episodes-create-help,285,1793.029,0.000,1735.000,6724,353,0,1,1 -go,episodes-create-help,285,4591.476,1665.000,2497.000,13212,693,0,44,35 -go,episodes-create-help,286,3937.436,2706.000,902.000,13300,688,0,33,28 -rust,episodes-create-help,286,1542.779,0.000,1492.000,6740,352,0,1,2 -rust,episodes-create-help,287,1447.669,0.000,1403.000,6492,357,0,1,1 -go,episodes-create-help,287,4055.971,1831.000,1831.000,13308,693,0,40,33 -go,episodes-create-help,288,3572.581,1751.000,1751.000,13276,689,0,14,8 -rust,episodes-create-help,288,1900.058,790.000,790.000,6536,354,0,1,2 -rust,episodes-create-help,289,1373.790,0.000,1330.000,6944,355,0,1,2 -go,episodes-create-help,289,4521.364,4439.000,0.000,13308,689,0,16,12 -go,episodes-create-help,290,5626.449,2017.000,3026.000,13312,705,0,48,43 -rust,episodes-create-help,290,2333.896,0.000,2252.000,6860,350,0,1,2 -rust,episodes-create-help,291,2358.531,1117.000,1117.000,6816,350,0,1,2 -go,episodes-create-help,291,5544.907,3263.000,2175.000,13276,697,0,44,39 -go,episodes-create-help,292,6043.053,925.000,4628.000,13312,703,0,17,12 -rust,episodes-create-help,292,2441.005,781.000,1562.000,6840,350,0,1,2 -rust,episodes-create-help,293,2384.180,0.000,2285.000,6580,352,0,1,2 -go,episodes-create-help,293,5413.679,1769.000,3538.000,13236,693,0,35,29 -go,episodes-create-help,294,6533.395,3996.000,1998.000,13276,692,0,51,43 -rust,episodes-create-help,294,2410.258,0.000,2349.000,6684,352,0,1,1 -rust,episodes-create-help,295,1852.852,0.000,1781.000,6820,353,0,1,2 -go,episodes-create-help,295,3861.575,3794.000,0.000,13300,715,0,42,35 -go,episodes-create-help,296,3681.616,1810.000,1810.000,13304,696,0,14,10 -rust,episodes-create-help,296,2007.251,833.000,833.000,6740,352,0,1,2 -rust,episodes-create-help,297,1533.271,728.000,728.000,6756,350,0,1,3 -go,episodes-create-help,297,4222.102,3786.000,0.000,13276,697,0,44,36 -go,episodes-create-help,298,4020.823,988.000,2966.000,13300,704,0,37,33 -rust,episodes-create-help,298,1956.356,0.000,1530.000,6488,357,0,1,2 -rust,episodes-create-help,299,1462.367,0.000,1421.000,6864,351,0,1,1 -go,episodes-create-help,299,3838.522,1879.000,1879.000,13292,693,0,39,35 diff --git a/bench/results/linux-amd64-cpu7.json b/bench/results/linux-amd64-cpu7.json deleted file mode 100644 index 3160f6a..0000000 --- a/bench/results/linux-amd64-cpu7.json +++ /dev/null @@ -1,322 +0,0 @@ -[ - { - "implementation": "go", - "command": "root-help", - "samples": 300, - "cpu_percent": 93.50948253283174, - "metrics": { - "wall_us": { - "mean": 4720.968626666667, - "median": 4622.384, - "p95": 5912.527, - "min": 3375.221, - "max": 6992.305, - "stddev": 631.6451131576396 - }, - "user_us": { - "mean": 1952.5066666666667, - "median": 1928.5, - "p95": 3682.0, - "min": 0.0, - "max": 4604.0, - "stddev": 1015.33600183497 - }, - "system_us": { - "mean": 2462.0466666666666, - "median": 2539.0, - "p95": 4180.0, - "min": 0.0, - "max": 5316.0, - "stddev": 1029.4935087162469 - }, - "peak_rss_kib": { - "mean": 13166.08, - "median": 13176.0, - "p95": 13220.0, - "min": 12956.0, - "max": 13276.0, - "stddev": 41.14162855308477 - }, - "minor_faults": { - "mean": 695.0166666666667, - "median": 692.5, - "p95": 707.0, - "min": 679.0, - "max": 715.0, - "stddev": 7.46032096420046 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 33.15, - "median": 39.0, - "p95": 47.0, - "min": 13.0, - "max": 55.0, - "stddev": 11.628735958822007 - }, - "involuntary_context_switches": { - "mean": 28.283333333333335, - "median": 33.5, - "p95": 41.0, - "min": 7.0, - "max": 50.0, - "stddev": 11.386090442094492 - }, - "total_cpu_us": { - "mean": 4414.553333333333, - "median": 4352.5, - "p95": 5336.0, - "min": 3321.0, - "max": 6008.0, - "stddev": 518.9405172935419 - } - } - }, - { - "implementation": "rust", - "command": "root-help", - "samples": 300, - "cpu_percent": 92.83125340154282, - "metrics": { - "wall_us": { - "mean": 1988.4359333333334, - "median": 1959.8470000000002, - "p95": 2629.261, - "min": 1205.684, - "max": 3357.978, - "stddev": 367.70908589376586 - }, - "user_us": { - "mean": 562.8766666666667, - "median": 0.0, - "p95": 1738.0, - "min": 0.0, - "max": 2291.0, - "stddev": 649.7072736668073 - }, - "system_us": { - "mean": 1283.0133333333333, - "median": 1399.0, - "p95": 2231.0, - "min": 0.0, - "max": 2649.0, - "stddev": 684.7279653182634 - }, - "peak_rss_kib": { - "mean": 6624.106666666667, - "median": 6632.0, - "p95": 6824.0, - "min": 6152.0, - "max": 6940.0, - "stddev": 127.51026869323985 - }, - "minor_faults": { - "mean": 350.2266666666667, - "median": 350.0, - "p95": 352.0, - "min": 348.0, - "max": 357.0, - "stddev": 1.3839396261719255 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 0.9933333333333333, - "median": 1.0, - "p95": 1.0, - "min": 0.0, - "max": 1.0, - "stddev": 0.08137703743822469 - }, - "involuntary_context_switches": { - "mean": 1.4466666666666668, - "median": 1.0, - "p95": 2.0, - "min": 1.0, - "max": 3.0, - "stddev": 0.5232802520850774 - }, - "total_cpu_us": { - "mean": 1845.89, - "median": 1841.0, - "p95": 2322.0, - "min": 1168.0, - "max": 2703.0, - "stddev": 294.91329443301356 - } - } - }, - { - "implementation": "go", - "command": "episodes-create-help", - "samples": 300, - "cpu_percent": 93.64261759923433, - "metrics": { - "wall_us": { - "mean": 4420.87884, - "median": 4343.876, - "p95": 5544.907, - "min": 3209.058, - "max": 6533.395, - "stddev": 611.5106530930111 - }, - "user_us": { - "mean": 1937.4866666666667, - "median": 1927.0, - "p95": 3685.0, - "min": 0.0, - "max": 4763.0, - "stddev": 1013.5703148551439 - }, - "system_us": { - "mean": 2202.34, - "median": 2125.5, - "p95": 3887.0, - "min": 0.0, - "max": 4628.0, - "stddev": 1023.4428486241916 - }, - "peak_rss_kib": { - "mean": 13292.546666666667, - "median": 13300.0, - "p95": 13344.0, - "min": 13148.0, - "max": 13348.0, - "stddev": 37.14576452601591 - }, - "minor_faults": { - "mean": 696.2433333333333, - "median": 694.0, - "p95": 709.0, - "min": 679.0, - "max": 715.0, - "stddev": 7.637896016283251 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 33.36666666666667, - "median": 38.0, - "p95": 45.0, - "min": 12.0, - "max": 51.0, - "stddev": 10.44759408774203 - }, - "involuntary_context_switches": { - "mean": 28.006666666666668, - "median": 32.0, - "p95": 40.0, - "min": 7.0, - "max": 44.0, - "stddev": 10.13607199834115 - }, - "total_cpu_us": { - "mean": 4139.826666666667, - "median": 4079.5, - "p95": 5089.0, - "min": 3110.0, - "max": 5994.0, - "stddev": 538.3954958537038 - } - } - }, - { - "implementation": "rust", - "command": "episodes-create-help", - "samples": 300, - "cpu_percent": 91.7690330649965, - "metrics": { - "wall_us": { - "mean": 1876.3809633333333, - "median": 1849.3135, - "p95": 2519.624, - "min": 1171.471, - "max": 3116.085, - "stddev": 372.07468855878295 - }, - "user_us": { - "mean": 480.33, - "median": 0.0, - "p95": 1721.0, - "min": 0.0, - "max": 2216.0, - "stddev": 600.9832064486772 - }, - "system_us": { - "mean": 1241.6066666666666, - "median": 1357.0, - "p95": 2108.0, - "min": 0.0, - "max": 2362.0, - "stddev": 643.8449129686089 - }, - "peak_rss_kib": { - "mean": 6756.066666666667, - "median": 6758.0, - "p95": 6888.0, - "min": 6408.0, - "max": 6988.0, - "stddev": 105.63658877911995 - }, - "minor_faults": { - "mean": 351.68, - "median": 351.0, - "p95": 355.0, - "min": 349.0, - "max": 359.0, - "stddev": 1.7255723688098392 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 0.9933333333333333, - "median": 1.0, - "p95": 1.0, - "min": 0.0, - "max": 1.0, - "stddev": 0.08137703743822469 - }, - "involuntary_context_switches": { - "mean": 1.5433333333333332, - "median": 2.0, - "p95": 2.0, - "min": 1.0, - "max": 3.0, - "stddev": 0.5242030988928708 - }, - "total_cpu_us": { - "mean": 1721.9366666666667, - "median": 1720.0, - "p95": 2268.0, - "min": 1138.0, - "max": 2624.0, - "stddev": 299.9694973196812 - } - } - } -] diff --git a/bench/results/linux-amd64.csv b/bench/results/linux-amd64.csv deleted file mode 100644 index 39d9199..0000000 --- a/bench/results/linux-amd64.csv +++ /dev/null @@ -1,1201 +0,0 @@ -implementation,command,sample,wall_us,user_us,system_us,peak_rss_kib,minor_faults,major_faults,voluntary_context_switches,involuntary_context_switches -go,root-help,0,6631.167,3199.000,5332.000,13436,819,0,76,0 -rust,root-help,0,3218.116,0.000,3154.000,6772,351,0,1,0 -rust,root-help,1,3226.871,1052.000,2105.000,6644,350,0,1,0 -go,root-help,1,5929.760,4900.000,2940.000,13176,749,0,67,0 -go,root-help,2,6136.658,2953.000,4922.000,13304,793,0,64,0 -rust,root-help,2,3200.284,0.000,3128.000,6584,349,0,1,0 -rust,root-help,3,2279.401,0.000,2170.000,6556,351,0,1,0 -go,root-help,3,8655.641,3100.000,7233.000,13432,798,0,79,0 -go,root-help,4,6526.011,2089.000,6269.000,13556,837,0,70,2 -rust,root-help,4,2657.953,850.000,1701.000,6672,350,0,1,1 -rust,root-help,5,3772.848,1237.000,2474.000,6600,348,0,1,0 -go,root-help,5,7324.591,4841.000,4841.000,13172,768,0,74,0 -go,root-help,6,6128.943,5810.000,2324.000,13524,852,0,68,0 -rust,root-help,6,2328.294,0.000,2254.000,6452,351,0,1,0 -rust,root-help,7,2157.434,0.000,2071.000,6720,349,0,1,0 -go,root-help,7,8615.014,3204.000,7476.000,13276,804,0,92,0 -go,root-help,8,6660.242,2278.000,5695.000,13564,827,0,74,0 -rust,root-help,8,3543.778,1132.000,2264.000,6792,351,0,1,1 -rust,root-help,9,3002.000,944.000,1889.000,6648,349,0,1,1 -go,root-help,9,6513.817,1020.000,7146.000,13204,765,0,74,0 -go,root-help,10,6462.451,5797.000,2319.000,13152,806,0,77,0 -rust,root-help,10,2965.922,0.000,2866.000,6548,349,0,1,0 -rust,root-help,11,3503.293,0.000,3392.000,6676,351,0,1,0 -go,root-help,11,4522.126,4497.000,1499.000,13428,815,0,61,0 -go,root-help,12,8255.109,4683.000,5854.000,13548,842,0,82,0 -rust,root-help,12,3936.316,0.000,3835.000,6572,349,0,1,0 -rust,root-help,13,2514.503,0.000,2404.000,6712,349,0,1,0 -go,root-help,13,9008.434,4698.000,7048.000,13308,802,0,93,0 -go,root-help,14,7076.154,2181.000,6543.000,13532,836,0,84,0 -rust,root-help,14,3482.162,849.000,2547.000,6692,351,0,1,0 -rust,root-help,15,3595.886,1158.000,2316.000,6740,351,0,1,1 -go,root-help,15,5922.317,1540.000,6162.000,13244,799,0,68,1 -go,root-help,16,5861.251,2257.000,5643.000,13164,769,0,69,0 -rust,root-help,16,2466.464,793.000,1586.000,6680,350,0,1,0 -rust,root-help,17,3604.271,0.000,3535.000,6592,349,0,1,0 -go,root-help,17,8202.770,4143.000,6215.000,13212,805,0,79,0 -go,root-help,18,9314.849,4413.000,7356.000,13420,802,0,97,0 -rust,root-help,18,4323.923,2106.000,2106.000,6452,349,0,1,0 -rust,root-help,19,2722.995,0.000,2601.000,6800,351,0,1,0 -go,root-help,19,6460.418,1998.000,6993.000,13308,813,0,70,0 -go,root-help,20,8972.957,2204.000,8818.000,13564,822,0,82,1 -rust,root-help,20,2608.600,837.000,1675.000,6496,349,0,1,0 -rust,root-help,21,3473.125,1133.000,2266.000,6396,355,0,1,0 -go,root-help,21,6495.271,2700.000,6751.000,13164,773,0,71,0 -go,root-help,22,8398.518,5885.000,4904.000,13532,837,0,91,0 -rust,root-help,22,4194.961,1644.000,2466.000,6632,349,0,1,0 -rust,root-help,23,3749.384,1220.000,2441.000,6604,351,0,1,0 -go,root-help,23,9007.792,6598.000,5498.000,13532,847,0,92,0 -go,root-help,24,7901.133,5607.000,4486.000,13292,794,0,74,1 -rust,root-help,24,3351.687,0.000,3194.000,6548,349,0,1,1 -rust,root-help,25,2011.048,0.000,1902.000,6284,351,0,1,0 -go,root-help,25,6185.330,3466.000,4621.000,13176,746,0,64,0 -go,root-help,26,7346.752,6230.000,3115.000,13476,806,0,71,0 -rust,root-help,26,2414.346,0.000,2301.000,6736,351,0,1,1 -rust,root-help,27,2308.387,1113.000,1113.000,6660,349,0,1,0 -go,root-help,27,5704.978,2749.000,4582.000,13240,803,0,73,0 -go,root-help,28,8143.689,6304.000,4202.000,13420,816,0,76,0 -rust,root-help,28,3376.514,0.000,3258.000,6632,351,0,1,0 -rust,root-help,29,2184.593,1045.000,1045.000,6624,350,0,1,0 -go,root-help,29,6755.321,4452.000,3339.000,13544,833,0,65,2 -go,root-help,30,5328.640,1971.000,4929.000,13272,831,0,72,0 -rust,root-help,30,2437.570,1185.000,1185.000,6600,349,0,1,0 -rust,root-help,31,1857.730,0.000,1800.000,6800,351,0,1,0 -go,root-help,31,5922.667,1735.000,6072.000,13420,829,0,60,2 -go,root-help,32,6535.046,0.000,8282.000,13436,804,0,62,0 -rust,root-help,32,3540.412,1713.000,1713.000,6668,349,0,1,0 -rust,root-help,33,2286.656,0.000,2204.000,6660,350,0,1,0 -go,root-help,33,8223.619,3096.000,7224.000,13404,794,0,87,0 -go,root-help,34,7461.808,3553.000,5923.000,13564,836,0,71,0 -rust,root-help,34,2624.401,1699.000,849.000,6684,350,0,1,0 -rust,root-help,35,3535.162,2300.000,1150.000,6492,350,0,1,0 -go,root-help,35,6596.552,4195.000,4195.000,13556,827,0,79,0 -go,root-help,36,6747.978,5049.000,3786.000,13148,754,0,80,0 -rust,root-help,36,2437.659,1181.000,1181.000,6840,350,0,1,0 -rust,root-help,37,2889.108,926.000,1853.000,6688,350,0,1,0 -go,root-help,37,5330.264,3120.000,4160.000,13420,829,0,70,2 -go,root-help,38,8008.775,2252.000,5632.000,13104,751,0,48,0 -rust,root-help,38,3477.754,0.000,3381.000,6732,350,0,1,1 -rust,root-help,39,2294.470,0.000,2215.000,6588,350,0,1,0 -go,root-help,39,6447.513,4038.000,4038.000,13532,822,0,75,0 -go,root-help,40,6060.816,2180.000,5451.000,13404,788,0,68,2 -rust,root-help,40,3658.041,1191.000,2382.000,6520,350,0,1,0 -rust,root-help,41,1628.720,0.000,1534.000,6704,351,0,1,0 -go,root-help,41,6646.446,4820.000,3615.000,13304,783,0,73,0 -go,root-help,42,5217.171,1679.000,4197.000,13436,797,0,69,1 -rust,root-help,42,4271.596,2065.000,2065.000,6668,350,0,1,1 -rust,root-help,43,1796.655,0.000,1679.000,6760,350,0,1,0 -go,root-help,43,4753.912,2138.000,4277.000,13020,762,0,66,0 -go,root-help,44,4871.002,2368.000,3552.000,13212,803,0,60,0 -rust,root-help,44,2556.431,0.000,1981.000,6700,350,0,1,1 -rust,root-help,45,1789.743,844.000,844.000,6556,353,0,1,0 -go,root-help,45,4216.561,5481.000,0.000,13180,751,0,58,0 -go,root-help,46,4026.955,2078.000,3118.000,13148,746,0,45,0 -rust,root-help,46,1911.261,0.000,1860.000,6496,349,0,1,0 -rust,root-help,47,1768.453,858.000,858.000,6472,352,0,1,0 -go,root-help,47,4190.373,1584.000,3960.000,13600,838,0,56,0 -go,root-help,48,4725.296,2011.000,4022.000,13468,834,0,59,0 -rust,root-help,48,2402.012,0.000,2354.000,6668,350,0,1,0 -rust,root-help,49,2013.272,0.000,1965.000,6688,350,0,1,0 -go,root-help,49,6098.287,3255.000,4340.000,13416,793,0,73,1 -go,root-help,50,5596.425,1113.000,5566.000,13560,829,0,76,1 -rust,root-help,50,1869.501,0.000,1790.000,6344,350,0,1,1 -rust,root-help,51,1609.183,0.000,1540.000,6856,351,0,1,0 -go,root-help,51,4593.299,3922.000,1307.000,13292,801,0,57,1 -go,root-help,52,5700.769,2074.000,5186.000,13604,858,0,74,0 -rust,root-help,52,2410.038,1160.000,1160.000,6476,349,0,1,0 -rust,root-help,53,1644.239,0.000,1570.000,6712,348,0,1,0 -go,root-help,53,4466.119,4087.000,1362.000,13604,836,0,58,0 -go,root-help,54,4641.330,771.000,4626.000,13436,819,0,59,2 -rust,root-help,54,1866.777,0.000,1811.000,6656,351,0,1,0 -rust,root-help,55,2004.915,967.000,967.000,6528,350,0,1,0 -go,root-help,55,4262.397,2563.000,2563.000,13404,818,0,59,0 -go,root-help,56,4410.376,2680.000,2680.000,13432,817,0,58,0 -rust,root-help,56,1742.825,0.000,1691.000,6652,350,0,1,0 -rust,root-help,57,1528.260,0.000,1476.000,6548,350,0,1,0 -go,root-help,57,3921.427,1624.000,3249.000,13420,836,0,57,0 -go,root-help,58,4333.461,2735.000,2735.000,13428,804,0,57,0 -rust,root-help,58,1799.352,0.000,1756.000,6648,350,0,1,0 -rust,root-help,59,1387.266,0.000,1349.000,6600,352,0,1,0 -go,root-help,59,6560.164,2562.000,2562.000,13276,770,0,54,0 -go,root-help,60,4559.656,0.000,5725.000,13308,783,0,54,0 -rust,root-help,60,2396.362,0.000,2343.000,6716,349,0,1,0 -rust,root-help,61,1892.095,0.000,1858.000,6632,350,0,1,0 -go,root-help,61,4212.563,2690.000,2690.000,13180,757,0,55,0 -go,root-help,62,5624.497,1286.000,5789.000,13420,795,0,69,0 -rust,root-help,62,2391.703,1163.000,1163.000,6672,349,0,1,0 -rust,root-help,63,3087.210,0.000,3035.000,6828,352,0,1,0 -go,root-help,63,5630.508,2404.000,4809.000,13564,836,0,67,0 -go,root-help,64,6786.039,2259.000,6779.000,13564,853,0,70,0 -rust,root-help,64,2582.391,1246.000,1246.000,6680,351,0,1,0 -rust,root-help,65,2459.851,0.000,2380.000,6648,349,0,1,0 -go,root-help,65,4200.792,3199.000,2399.000,13420,807,0,59,0 -go,root-help,66,5070.375,2120.000,4240.000,13440,811,0,61,0 -rust,root-help,66,2463.859,0.000,2391.000,6544,349,0,1,0 -rust,root-help,67,1942.861,1821.000,0.000,6412,356,0,1,1 -go,root-help,67,5411.786,4033.000,2688.000,13560,830,0,64,0 -go,root-help,68,4520.453,4297.000,1432.000,13204,795,0,62,0 -rust,root-help,68,3267.499,0.000,2629.000,6696,351,0,1,1 -rust,root-help,69,3060.810,1971.000,985.000,6560,349,0,1,1 -go,root-help,69,6691.682,6203.000,2067.000,13436,798,0,81,1 -go,root-help,70,6426.525,1136.000,6818.000,13432,811,0,74,0 -rust,root-help,70,2475.991,0.000,2379.000,6852,351,0,1,0 -rust,root-help,71,3239.706,0.000,3112.000,6480,350,0,1,0 -go,root-help,71,5779.688,3064.000,4596.000,13164,769,0,71,0 -go,root-help,72,5720.177,4836.000,2418.000,13420,805,0,70,0 -rust,root-help,72,2549.460,0.000,2467.000,6716,350,0,1,0 -rust,root-help,73,3341.047,1086.000,2173.000,6812,351,0,1,1 -go,root-help,73,5574.102,4562.000,2737.000,13276,776,0,66,0 -go,root-help,74,8685.326,6656.000,3993.000,13436,809,0,90,1 -rust,root-help,74,4632.603,986.000,2960.000,6664,351,0,1,0 -rust,root-help,75,2693.210,0.000,2584.000,6564,350,0,1,0 -go,root-help,75,7412.937,5321.000,4256.000,13180,768,0,84,0 -go,root-help,76,5689.880,2831.000,4718.000,13300,804,0,74,0 -rust,root-help,76,2311.603,0.000,2221.000,6488,350,0,1,0 -rust,root-help,77,2817.353,0.000,2737.000,6580,349,0,1,0 -go,root-help,77,6884.113,6236.000,2494.000,13340,814,0,72,0 -go,root-help,78,8405.972,3620.000,7240.000,13416,834,0,79,2 -rust,root-help,78,3327.471,0.000,3245.000,6564,350,0,1,1 -rust,root-help,79,2873.599,928.000,1857.000,6612,350,0,1,0 -go,root-help,79,4554.276,1226.000,4906.000,13436,800,0,65,0 -go,root-help,80,6521.091,5380.000,2690.000,13472,839,0,81,0 -rust,root-help,80,2007.881,972.000,972.000,6588,349,0,1,1 -rust,root-help,81,2619.442,2563.000,0.000,6588,349,0,1,0 -go,root-help,81,4406.137,889.000,4446.000,13556,833,0,62,0 -go,root-help,82,4772.855,2886.000,2886.000,13420,816,0,66,0 -rust,root-help,82,2395.892,1164.000,1164.000,6592,351,0,1,0 -rust,root-help,83,2389.629,1152.000,1152.000,6580,351,0,1,0 -go,root-help,83,4071.258,0.000,4945.000,13180,770,0,58,0 -go,root-help,84,4211.823,2728.000,2728.000,13436,814,0,60,1 -rust,root-help,84,2216.944,0.000,2125.000,6648,349,0,1,1 -rust,root-help,85,2280.574,1108.000,1108.000,6720,349,0,1,0 -go,root-help,85,9454.873,3301.000,7703.000,13396,836,0,96,1 -go,root-help,86,7800.455,2826.000,7065.000,13440,819,0,86,0 -rust,root-help,86,2555.170,1212.000,1212.000,6556,350,0,1,0 -rust,root-help,87,2985.599,0.000,2837.000,6632,348,0,1,0 -go,root-help,87,6012.906,2149.000,5372.000,13436,822,0,74,0 -go,root-help,88,4346.035,2695.000,2695.000,13216,753,0,48,0 -rust,root-help,88,1845.398,0.000,1755.000,6640,349,0,1,0 -rust,root-help,89,3275.213,779.000,2339.000,6644,349,0,1,0 -go,root-help,89,7370.287,4218.000,5273.000,13564,824,0,82,0 -go,root-help,90,8091.331,4475.000,5370.000,13276,775,0,85,1 -rust,root-help,90,2212.296,1051.000,1051.000,6696,348,0,1,0 -rust,root-help,91,1683.312,0.000,1549.000,6420,351,0,1,1 -go,root-help,91,8732.976,3958.000,6928.000,13288,801,0,81,0 -go,root-help,92,6887.699,3529.000,5882.000,13604,849,0,68,0 -rust,root-help,92,2084.526,0.000,1955.000,6632,348,0,1,0 -rust,root-help,93,2401.832,1134.000,1134.000,6280,356,0,1,0 -go,root-help,93,7817.726,6336.000,3168.000,13432,803,0,89,0 -go,root-help,94,5533.976,1084.000,5965.000,13404,806,0,71,0 -rust,root-help,94,2265.767,1105.000,1105.000,6472,351,0,1,0 -rust,root-help,95,2663.654,1728.000,864.000,6572,350,0,1,0 -go,root-help,95,5113.856,6757.000,0.000,13420,814,0,60,0 -go,root-help,96,5355.772,2041.000,5104.000,13292,804,0,59,0 -rust,root-help,96,3581.378,865.000,2595.000,6428,351,0,1,1 -rust,root-help,97,3670.506,2362.000,1181.000,6512,350,0,1,0 -go,root-help,97,5992.148,1453.000,5815.000,13176,751,0,63,1 -go,root-help,98,6650.274,3636.000,4848.000,13436,816,0,76,2 -rust,root-help,98,2038.018,0.000,1962.000,6612,349,0,1,0 -rust,root-help,99,1909.658,0.000,1844.000,6560,352,0,1,0 -go,root-help,99,5878.945,4854.000,2427.000,13168,782,0,80,0 -go,root-help,100,6914.730,6048.000,2419.000,13260,803,0,82,0 -rust,root-help,100,1998.865,972.000,972.000,6556,349,0,1,0 -rust,root-help,101,2655.719,866.000,1732.000,6680,349,0,1,0 -go,root-help,101,5343.919,2322.000,4644.000,13436,794,0,62,0 -go,root-help,102,5105.282,4432.000,1773.000,13084,764,0,48,1 -rust,root-help,102,2019.845,0.000,1955.000,6540,352,0,1,0 -rust,root-help,103,2626.825,0.000,2558.000,6664,350,0,1,0 -go,root-help,103,4936.244,1880.000,4700.000,13492,851,0,65,1 -go,root-help,104,5441.493,4443.000,2221.000,13404,835,0,61,0 -rust,root-help,104,1996.952,0.000,1933.000,6724,349,0,1,0 -rust,root-help,105,2076.361,0.000,1997.000,6336,355,0,1,0 -go,root-help,105,4897.521,2414.000,4024.000,13476,804,0,69,2 -go,root-help,106,4873.927,3108.000,3108.000,13476,821,0,68,2 -rust,root-help,106,2742.683,894.000,1789.000,6724,350,0,1,0 -rust,root-help,107,3417.800,0.000,3354.000,6712,350,0,1,0 -go,root-help,107,7301.508,2243.000,6730.000,13476,805,0,79,0 -go,root-help,108,8228.768,5665.000,4532.000,13420,832,0,82,2 -rust,root-help,108,3485.067,2274.000,1137.000,6668,351,0,1,0 -rust,root-help,109,2971.864,0.000,2878.000,6812,351,0,1,0 -go,root-help,109,6817.307,6442.000,2576.000,13160,764,0,79,0 -go,root-help,110,8402.586,5677.000,4542.000,13120,784,0,89,3 -rust,root-help,110,2992.713,1446.000,1446.000,6792,350,0,1,0 -rust,root-help,111,3153.624,0.000,3086.000,6556,351,0,1,0 -go,root-help,111,6883.862,4715.000,3536.000,13216,756,0,72,2 -go,root-help,112,7296.779,6465.000,2586.000,13224,822,0,87,0 -rust,root-help,112,3225.188,1047.000,2095.000,6588,351,0,1,0 -rust,root-help,113,3654.815,0.000,3602.000,6600,349,0,1,0 -go,root-help,113,4839.633,1095.000,5475.000,13240,802,0,60,3 -go,root-help,114,4383.966,1944.000,3888.000,13428,788,0,58,1 -rust,root-help,114,2886.974,1896.000,948.000,6228,356,0,1,0 -rust,root-help,115,1617.158,0.000,1569.000,6660,348,0,1,0 -go,root-help,115,7718.901,5664.000,4248.000,13344,795,0,91,0 -go,root-help,116,8823.135,5486.000,4572.000,13404,800,0,66,3 -rust,root-help,116,2549.580,0.000,2487.000,6572,349,0,1,1 -rust,root-help,117,2617.106,1281.000,1281.000,6372,350,0,1,0 -go,root-help,117,5925.643,3637.000,5456.000,13204,782,0,66,1 -go,root-help,118,8314.520,6460.000,3876.000,13436,812,0,75,2 -rust,root-help,118,2489.989,1180.000,1180.000,6560,351,0,1,0 -rust,root-help,119,2203.761,1057.000,1057.000,6668,349,0,1,0 -go,root-help,119,5981.867,2623.000,5246.000,13180,750,0,68,0 -go,root-help,120,6329.120,4170.000,4170.000,13432,814,0,70,0 -rust,root-help,120,2476.603,0.000,2388.000,6480,353,0,1,0 -rust,root-help,121,3895.849,1917.000,1917.000,6724,348,0,1,0 -go,root-help,121,8849.324,5118.000,6398.000,13544,851,0,79,3 -go,root-help,122,7463.632,7622.000,2177.000,13428,818,0,84,1 -rust,root-help,122,2148.987,0.000,2020.000,6576,350,0,1,0 -rust,root-help,123,5580.644,2245.000,2245.000,6484,349,0,1,1 -go,root-help,123,10243.974,6459.000,6459.000,13404,820,0,92,0 -go,root-help,124,6202.682,1198.000,5990.000,13404,828,0,76,0 -rust,root-help,124,2544.159,1234.000,1234.000,6624,349,0,1,0 -rust,root-help,125,4737.621,1019.000,3059.000,6600,350,0,1,1 -go,root-help,125,6096.793,0.000,8272.000,13436,814,0,58,0 -go,root-help,126,7030.127,3119.000,7279.000,13148,754,0,61,1 -rust,root-help,126,2310.090,1112.000,1112.000,6752,350,0,1,0 -rust,root-help,127,2433.862,2316.000,0.000,6656,350,0,1,0 -go,root-help,127,4812.170,2046.000,4092.000,13440,805,0,59,0 -go,root-help,128,5353.788,3953.000,2635.000,13340,813,0,52,0 -rust,root-help,128,2381.815,1157.000,1157.000,6724,351,0,1,0 -rust,root-help,129,1987.144,0.000,1878.000,6284,358,0,1,0 -go,root-help,129,4509.292,2467.000,3701.000,13564,848,0,61,0 -go,root-help,130,6017.044,2983.000,3978.000,13132,772,0,73,1 -rust,root-help,130,2049.901,976.000,976.000,6656,349,0,1,0 -rust,root-help,131,1886.674,1797.000,0.000,6572,350,0,1,0 -go,root-help,131,5023.999,4774.000,1909.000,13304,822,0,62,0 -go,root-help,132,5270.942,3790.000,2526.000,13248,794,0,62,0 -rust,root-help,132,3059.989,996.000,1992.000,6684,350,0,1,0 -rust,root-help,133,2458.187,789.000,1579.000,6272,355,0,1,0 -go,root-help,133,5300.297,3393.000,3393.000,13312,797,0,59,0 -go,root-help,134,5322.710,2560.000,4267.000,13548,834,0,67,0 -rust,root-help,134,2635.260,0.000,2590.000,6684,350,0,1,0 -rust,root-help,135,2266.908,0.000,2220.000,6676,350,0,1,0 -go,root-help,135,5024.600,2527.000,3791.000,13292,793,0,64,0 -go,root-help,136,4232.091,1733.000,3466.000,13148,789,0,58,0 -rust,root-help,136,1681.670,0.000,1617.000,6516,349,0,1,0 -rust,root-help,137,1573.525,760.000,760.000,6600,349,0,1,0 -go,root-help,137,6455.969,3144.000,5241.000,13432,829,0,86,0 -go,root-help,138,6171.184,2282.000,5324.000,13416,816,0,79,0 -rust,root-help,138,2729.247,1048.000,1048.000,6608,351,0,1,1 -rust,root-help,139,2797.244,0.000,2751.000,6748,350,0,1,0 -go,root-help,139,4310.818,4214.000,1404.000,13180,764,0,60,0 -go,root-help,140,5708.524,1415.000,5660.000,13164,753,0,68,0 -rust,root-help,140,1827.904,0.000,1776.000,6680,349,0,1,0 -rust,root-help,141,1564.430,0.000,1499.000,6732,351,0,1,0 -go,root-help,141,4837.127,3171.000,3171.000,13560,848,0,71,0 -go,root-help,142,5544.536,888.000,6219.000,13096,775,0,62,0 -rust,root-help,142,3879.859,957.000,2871.000,6704,350,0,1,0 -rust,root-help,143,1949.714,0.000,1882.000,6652,351,0,1,0 -go,root-help,143,4844.671,2126.000,4252.000,13404,817,0,65,0 -go,root-help,144,4346.626,4143.000,1381.000,13404,809,0,55,0 -rust,root-help,144,3564.045,0.000,3503.000,6780,352,0,1,0 -rust,root-help,145,3660.107,0.000,3592.000,6604,350,0,1,0 -go,root-help,145,9425.717,6438.000,5365.000,13300,793,0,89,0 -go,root-help,146,7200.417,4491.000,4491.000,13436,806,0,87,0 -rust,root-help,146,2866.545,1834.000,917.000,6688,350,0,1,1 -rust,root-help,147,2694.583,0.000,2613.000,6368,350,0,1,0 -go,root-help,147,8064.820,4508.000,5635.000,13504,838,0,90,0 -go,root-help,148,5989.152,5322.000,2661.000,13404,836,0,76,0 -rust,root-help,148,2413.564,1159.000,1159.000,6600,349,0,1,0 -rust,root-help,149,3034.881,0.000,2926.000,6720,351,0,1,1 -go,root-help,149,6711.938,2173.000,6521.000,13568,828,0,66,0 -go,root-help,150,8313.900,6612.000,4408.000,13348,800,0,79,1 -rust,root-help,150,3526.084,1725.000,1725.000,6640,349,0,1,0 -rust,root-help,151,2909.927,946.000,1893.000,6596,351,0,1,0 -go,root-help,151,5898.270,2080.000,6240.000,13212,804,0,71,0 -go,root-help,152,7502.805,4895.000,4895.000,13220,766,0,60,0 -rust,root-help,152,2450.594,784.000,1568.000,6556,349,0,1,0 -rust,root-help,153,3581.930,2319.000,1159.000,6232,357,0,1,0 -go,root-help,153,9579.797,2642.000,9687.000,13476,792,0,83,1 -go,root-help,154,9436.347,5926.000,5926.000,13184,747,0,84,0 -rust,root-help,154,3465.080,0.000,3358.000,6656,350,0,1,0 -rust,root-help,155,2728.916,0.000,2607.000,6648,351,0,1,0 -go,root-help,155,5292.652,4484.000,2242.000,13436,791,0,61,0 -go,root-help,156,6125.368,3750.000,3750.000,13404,821,0,76,0 -rust,root-help,156,2739.686,889.000,1778.000,6728,348,0,1,0 -rust,root-help,157,2356.558,1119.000,1119.000,6684,351,0,1,0 -go,root-help,157,6002.887,3994.000,3994.000,13436,824,0,69,0 -go,root-help,158,5098.368,2217.000,4434.000,13404,808,0,64,0 -rust,root-help,158,2551.824,0.000,1996.000,6848,350,0,1,1 -rust,root-help,159,2581.109,0.000,2487.000,6672,350,0,1,0 -go,root-help,159,5096.224,2001.000,5004.000,13436,822,0,62,0 -go,root-help,160,9312.183,6052.000,4842.000,13104,782,0,82,1 -rust,root-help,160,3464.980,0.000,3366.000,6664,350,0,1,0 -rust,root-help,161,3943.698,952.000,2856.000,6656,350,0,1,0 -go,root-help,161,6192.154,4449.000,4449.000,13180,776,0,72,1 -go,root-help,162,8562.274,2665.000,7997.000,13600,856,0,67,2 -rust,root-help,162,2506.970,1197.000,1197.000,6684,350,0,1,1 -rust,root-help,163,3610.072,1160.000,2321.000,6540,349,0,1,0 -go,root-help,163,7574.120,5872.000,2936.000,13476,829,0,72,6 -go,root-help,164,5422.477,2813.000,3750.000,13216,768,0,67,5 -rust,root-help,164,2131.924,1031.000,1031.000,6652,350,0,1,0 -rust,root-help,165,2428.371,0.000,2360.000,6540,349,0,1,0 -go,root-help,165,4824.163,3511.000,2341.000,13216,748,0,63,4 -go,root-help,166,4847.567,1272.000,5089.000,13404,804,0,63,5 -rust,root-help,166,2893.937,1151.000,1151.000,6664,350,0,1,1 -rust,root-help,167,2199.382,0.000,1956.000,6556,349,0,1,0 -go,root-help,167,6171.514,4387.000,2925.000,13404,819,0,67,4 -go,root-help,168,6102.374,5623.000,2249.000,13428,805,0,67,3 -rust,root-help,168,2318.395,0.000,2220.000,6408,355,0,1,1 -rust,root-help,169,3310.210,0.000,3204.000,6616,351,0,1,1 -go,root-help,169,4710.228,1551.000,4654.000,13436,805,0,62,0 -go,root-help,170,5501.105,4065.000,3049.000,13148,758,0,69,2 -rust,root-help,170,2473.307,1198.000,1198.000,6592,350,0,1,0 -rust,root-help,171,1663.153,0.000,1586.000,6556,349,0,1,0 -go,root-help,171,5043.204,957.000,5746.000,13436,829,0,74,0 -go,root-help,172,5537.995,1973.000,4934.000,13440,810,0,73,0 -rust,root-help,172,1878.870,0.000,1773.000,6720,348,0,1,0 -rust,root-help,173,1538.670,0.000,1438.000,6552,350,0,1,1 -go,root-help,173,5093.179,1799.000,4497.000,13404,794,0,57,0 -go,root-help,174,6158.961,1322.000,6612.000,13304,798,0,71,0 -rust,root-help,174,2275.004,895.000,895.000,6668,349,0,1,1 -rust,root-help,175,1883.949,0.000,1766.000,6560,350,0,1,0 -go,root-help,175,5853.657,1127.000,6763.000,13604,844,0,76,0 -go,root-help,176,7113.834,2562.000,6406.000,13564,840,0,83,0 -rust,root-help,176,2673.883,1300.000,1300.000,6724,350,0,1,0 -rust,root-help,177,2544.972,0.000,2475.000,6724,349,0,1,0 -go,root-help,177,5773.968,1087.000,6526.000,13416,813,0,63,0 -go,root-help,178,5325.464,3839.000,2879.000,13404,818,0,65,0 -rust,root-help,178,2250.468,0.000,2160.000,6588,350,0,1,1 -rust,root-help,179,2411.770,770.000,1540.000,6632,351,0,1,0 -go,root-help,179,5016.524,0.000,6393.000,13180,764,0,68,1 -go,root-help,180,6119.155,3851.000,3851.000,13404,831,0,78,1 -rust,root-help,180,2979.346,0.000,2365.000,6564,350,0,1,1 -rust,root-help,181,2353.782,1133.000,1133.000,6784,352,0,1,0 -go,root-help,181,4947.725,3422.000,3422.000,13604,864,0,74,0 -go,root-help,182,5873.274,2954.000,3939.000,13404,821,0,72,1 -rust,root-help,182,2122.888,0.000,2062.000,6500,350,0,1,0 -rust,root-help,183,2299.650,1118.000,1118.000,6792,349,0,1,1 -go,root-help,183,4884.636,2069.000,4139.000,13420,812,0,68,0 -go,root-help,184,4350.252,2368.000,3158.000,13228,788,0,58,0 -rust,root-help,184,2382.676,1164.000,1164.000,6596,350,0,1,0 -rust,root-help,185,1966.104,948.000,948.000,6592,350,0,1,0 -go,root-help,185,4762.808,2334.000,3502.000,13476,808,0,69,0 -go,root-help,186,4604.310,4303.000,1434.000,13436,808,0,67,1 -rust,root-help,186,2523.380,1210.000,1210.000,6604,350,0,1,0 -rust,root-help,187,1933.582,0.000,1834.000,6712,350,0,1,0 -go,root-help,187,4645.737,2424.000,3636.000,13292,815,0,62,1 -go,root-help,188,5696.282,1229.000,6149.000,13180,772,0,72,0 -rust,root-help,188,2535.043,814.000,1629.000,6596,350,0,1,0 -rust,root-help,189,2324.868,0.000,2215.000,6580,349,0,1,0 -go,root-help,189,5995.644,4843.000,2905.000,13176,759,0,67,1 -go,root-help,190,5641.909,6440.000,1073.000,13300,793,0,61,0 -rust,root-help,190,2833.704,0.000,2147.000,6388,356,0,1,1 -rust,root-help,191,2239.869,1067.000,1067.000,6576,350,0,1,0 -go,root-help,191,6231.947,2126.000,5317.000,13180,782,0,83,1 -go,root-help,192,6161.977,1282.000,6414.000,13340,817,0,72,0 -rust,root-help,192,2659.285,869.000,1738.000,6816,351,0,1,0 -rust,root-help,193,2090.146,1021.000,1021.000,6616,351,0,1,0 -go,root-help,193,5794.545,3166.000,4221.000,13164,757,0,79,0 -go,root-help,194,4614.108,2927.000,2927.000,13420,809,0,67,0 -rust,root-help,194,2442.549,0.000,2377.000,6640,348,0,1,0 -rust,root-help,195,2823.172,2714.000,0.000,6700,350,0,1,0 -go,root-help,195,4573.783,3031.000,3031.000,13288,788,0,68,0 -go,root-help,196,5695.140,3631.000,3631.000,13288,790,0,73,0 -rust,root-help,196,2183.603,0.000,2064.000,6856,351,0,1,1 -rust,root-help,197,2228.977,714.000,1429.000,6676,350,0,1,1 -go,root-help,197,4560.287,3531.000,2354.000,13432,818,0,63,0 -go,root-help,198,6630.616,4659.000,3494.000,13404,803,0,84,0 -rust,root-help,198,2780.945,0.000,2709.000,6664,349,0,1,0 -rust,root-help,199,2845.286,1372.000,1372.000,6720,350,0,1,0 -go,root-help,199,5083.890,2795.000,3727.000,13404,843,0,70,0 -go,root-help,200,5084.122,1306.000,5226.000,13404,800,0,63,0 -rust,root-help,200,3884.598,0.000,3822.000,6580,351,0,0,1 -rust,root-help,201,1977.666,0.000,1883.000,6620,350,0,1,0 -go,root-help,201,4868.257,1234.000,4937.000,13236,789,0,51,2 -go,root-help,202,10478.786,4831.000,8455.000,13436,832,0,93,0 -rust,root-help,202,4253.692,2095.000,2095.000,6556,349,0,1,0 -rust,root-help,203,3760.244,917.000,2753.000,6572,351,0,1,0 -go,root-help,203,7692.542,4187.000,5234.000,13348,817,0,93,0 -go,root-help,204,8920.147,5719.000,5719.000,13480,848,0,89,0 -rust,root-help,204,3943.228,2525.000,1262.000,6708,350,0,1,0 -rust,root-help,205,3010.587,0.000,2840.000,6608,350,0,1,1 -go,root-help,205,9417.902,5133.000,6159.000,13340,833,0,97,2 -go,root-help,206,9669.646,3032.000,9098.000,13404,804,0,81,0 -rust,root-help,206,2919.776,0.000,2839.000,6704,351,0,1,0 -rust,root-help,207,2727.534,1311.000,1311.000,6504,352,0,1,0 -go,root-help,207,6916.243,2999.000,5999.000,13340,826,0,78,0 -go,root-help,208,6562.479,2457.000,6142.000,13228,828,0,81,0 -rust,root-help,208,3743.303,898.000,2694.000,6520,350,0,1,1 -rust,root-help,209,2479.428,0.000,2328.000,6684,350,0,1,0 -go,root-help,209,9586.489,3499.000,7874.000,13500,830,0,98,1 -go,root-help,210,5870.579,1757.000,6150.000,13436,820,0,66,0 -rust,root-help,210,3717.183,1186.000,2373.000,6600,350,0,1,1 -rust,root-help,211,3908.563,2486.000,1243.000,6696,350,0,1,0 -go,root-help,211,8473.268,4597.000,5746.000,13436,817,0,82,0 -go,root-help,212,8189.125,5763.000,4802.000,13288,807,0,78,0 -rust,root-help,212,3378.287,0.000,3250.000,6552,350,0,1,0 -rust,root-help,213,3595.716,3453.000,0.000,6548,350,0,1,0 -go,root-help,213,5822.679,5087.000,2543.000,13128,758,0,66,0 -go,root-help,214,8540.684,4733.000,5916.000,13180,754,0,91,0 -rust,root-help,214,2249.065,1081.000,1081.000,6764,350,0,1,0 -rust,root-help,215,2112.850,0.000,1999.000,6732,351,0,1,1 -go,root-help,215,8786.577,6090.000,4872.000,13532,846,0,70,0 -go,root-help,216,7629.954,4914.000,4914.000,13344,805,0,70,0 -rust,root-help,216,3680.455,982.000,1964.000,6692,349,0,1,1 -rust,root-help,217,3400.268,2210.000,1105.000,6644,349,0,1,0 -go,root-help,217,6005.302,3310.000,4413.000,13436,809,0,65,0 -go,root-help,218,7149.902,5517.000,3310.000,13600,837,0,80,0 -rust,root-help,218,2884.670,0.000,2752.000,6748,348,0,1,0 -rust,root-help,219,3240.968,0.000,3113.000,6668,351,0,1,0 -go,root-help,219,8124.724,9684.000,0.000,13472,832,0,87,2 -go,root-help,220,5655.856,4958.000,2479.000,13176,767,0,67,0 -rust,root-help,220,2904.908,0.000,2760.000,6860,353,0,1,0 -rust,root-help,221,3274.040,1054.000,2108.000,6532,348,0,1,1 -go,root-help,221,8662.865,3593.000,6289.000,13340,804,0,81,2 -go,root-help,222,7541.899,4825.000,4825.000,13148,775,0,78,1 -rust,root-help,222,3082.401,0.000,2977.000,6632,348,0,1,1 -rust,root-help,223,3301.302,0.000,2567.000,6464,353,0,1,1 -go,root-help,223,8041.057,3223.000,6447.000,13348,807,0,59,0 -go,root-help,224,6393.811,5367.000,2683.000,13472,822,0,72,0 -rust,root-help,224,4069.826,981.000,2944.000,6620,349,0,1,0 -rust,root-help,225,2225.400,1056.000,1056.000,6708,350,0,1,0 -go,root-help,225,9276.939,5406.000,6487.000,13432,804,0,80,0 -go,root-help,226,5775.089,3807.000,3807.000,13432,810,0,70,0 -rust,root-help,226,3775.693,1207.000,2415.000,6684,352,0,1,0 -rust,root-help,227,3394.797,0.000,3288.000,6680,349,0,1,0 -go,root-help,227,6207.322,1413.000,7068.000,13304,783,0,64,0 -go,root-help,228,9140.932,5233.000,6280.000,13560,834,0,83,1 -rust,root-help,228,3450.482,0.000,3337.000,6280,351,0,1,0 -rust,root-help,229,3643.956,1123.000,2247.000,6672,350,0,1,0 -go,root-help,229,6959.114,3595.000,4793.000,13244,810,0,69,0 -go,root-help,230,10026.426,3683.000,8594.000,13404,815,0,91,0 -rust,root-help,230,2734.306,0.000,2606.000,6332,355,0,1,0 -rust,root-help,231,2674.845,1719.000,859.000,6636,349,0,1,0 -go,root-help,231,5934.980,2228.000,5571.000,13432,810,0,70,0 -go,root-help,232,6585.924,2086.000,6259.000,13436,807,0,78,0 -rust,root-help,232,3010.767,0.000,2927.000,6540,348,0,1,0 -rust,root-help,233,2812.924,0.000,2716.000,6696,350,0,1,0 -go,root-help,233,6628.724,3550.000,5325.000,13436,820,0,76,0 -go,root-help,234,6011.654,2425.000,5659.000,13180,755,0,71,0 -rust,root-help,234,2798.096,1342.000,1342.000,6724,349,0,1,0 -rust,root-help,235,2060.451,0.000,2001.000,6564,349,0,1,0 -go,root-help,235,6050.276,3617.000,4823.000,13148,776,0,68,2 -go,root-help,236,8722.136,4795.000,5994.000,13468,824,0,86,0 -rust,root-help,236,3417.701,1094.000,2189.000,6580,349,0,1,0 -rust,root-help,237,2518.953,1197.000,1197.000,6684,350,0,1,0 -go,root-help,237,9995.457,3826.000,8608.000,13564,836,0,88,1 -go,root-help,238,8696.918,4916.000,6145.000,13276,779,0,86,0 -rust,root-help,238,3842.319,0.000,3706.000,6664,350,0,1,1 -rust,root-help,239,2607.969,1221.000,1221.000,6680,350,0,1,0 -go,root-help,239,8832.973,4962.000,6202.000,13396,800,0,81,0 -go,root-help,240,8728.118,3375.000,6751.000,13468,827,0,77,2 -rust,root-help,240,3841.707,1847.000,1847.000,6472,349,0,1,1 -rust,root-help,241,3132.095,0.000,2980.000,6684,349,0,0,0 -go,root-help,241,8708.881,2397.000,8391.000,13184,747,0,93,0 -go,root-help,242,7246.052,2396.000,7189.000,13500,837,0,74,0 -rust,root-help,242,2371.725,0.000,2307.000,6588,351,0,1,0 -rust,root-help,243,3050.902,994.000,1988.000,6728,349,0,1,0 -go,root-help,243,8884.782,3712.000,7424.000,13440,815,0,92,0 -go,root-help,244,9026.858,7154.000,4088.000,13164,773,0,94,0 -rust,root-help,244,2203.801,2104.000,0.000,6612,350,0,1,0 -rust,root-help,245,3770.293,911.000,2735.000,6676,349,0,1,0 -go,root-help,245,8396.915,5625.000,5625.000,13220,766,0,89,0 -go,root-help,246,5640.485,1161.000,6966.000,13440,824,0,67,1 -rust,root-help,246,2892.604,938.000,1876.000,6572,350,0,1,0 -rust,root-help,247,2535.444,1219.000,1219.000,6744,350,0,1,0 -go,root-help,247,9493.184,4596.000,6435.000,13476,816,0,83,2 -go,root-help,248,6310.455,3670.000,4587.000,13564,819,0,69,0 -rust,root-help,248,2532.247,1220.000,1220.000,6748,350,0,1,0 -rust,root-help,249,2484.036,0.000,2392.000,6732,349,0,1,1 -go,root-help,249,7980.052,3765.000,6589.000,13308,805,0,90,1 -go,root-help,250,7247.065,1220.000,8542.000,12968,773,0,78,0 -rust,root-help,250,3159.475,0.000,3040.000,6664,350,0,1,1 -rust,root-help,251,2462.807,1150.000,1150.000,6592,350,0,1,1 -go,root-help,251,7714.774,6239.000,4159.000,13084,771,0,86,0 -go,root-help,252,7768.925,3816.000,6360.000,13176,774,0,80,1 -rust,root-help,252,2974.558,0.000,2859.000,6556,350,0,1,0 -rust,root-help,253,3243.624,1045.000,2091.000,6672,353,0,1,0 -go,root-help,253,4756.255,1688.000,5065.000,13344,809,0,64,0 -go,root-help,254,5513.888,4494.000,2996.000,13472,833,0,65,1 -rust,root-help,254,3680.304,0.000,3604.000,6852,351,0,1,0 -rust,root-help,255,3510.976,0.000,3429.000,6692,351,0,1,0 -go,root-help,255,8000.230,5626.000,4501.000,13392,814,0,82,2 -go,root-help,256,8199.463,5402.000,4322.000,13436,819,0,83,0 -rust,root-help,256,3686.315,2348.000,1174.000,6628,350,0,1,1 -rust,root-help,257,1904.979,914.000,914.000,6684,350,0,1,0 -go,root-help,257,4877.031,2103.000,4207.000,13436,815,0,54,0 -go,root-help,258,5547.052,4237.000,3178.000,13276,819,0,71,0 -rust,root-help,258,3755.115,981.000,1962.000,6716,349,0,1,1 -rust,root-help,259,2250.497,0.000,2081.000,6600,350,0,1,0 -go,root-help,259,5235.485,4404.000,3303.000,13476,811,0,72,4 -go,root-help,260,5728.502,5081.000,2540.000,13476,797,0,67,2 -rust,root-help,260,3459.690,1131.000,2263.000,6684,349,0,1,0 -rust,root-help,261,2729.528,1081.000,1081.000,6724,351,0,1,1 -go,root-help,261,9423.574,6470.000,5391.000,13348,809,0,80,0 -go,root-help,262,5101.383,2137.000,4275.000,13184,769,0,64,0 -rust,root-help,262,2353.342,0.000,2243.000,6796,351,0,1,0 -rust,root-help,263,1948.971,0.000,1838.000,6240,355,0,1,0 -go,root-help,263,7087.356,3816.000,6361.000,13308,804,0,75,0 -go,root-help,264,6482.789,5346.000,2673.000,13368,830,0,80,1 -rust,root-help,264,1859.815,0.000,1771.000,6560,352,0,1,0 -rust,root-help,265,2435.264,771.000,1543.000,6412,351,0,1,0 -go,root-help,265,7844.658,5180.000,5180.000,13348,792,0,74,0 -go,root-help,266,7890.795,7077.000,2830.000,13108,784,0,80,0 -rust,root-help,266,3198.328,774.000,2323.000,6560,349,0,1,1 -rust,root-help,267,2431.307,0.000,2335.000,6684,349,0,1,0 -go,root-help,267,5019.571,2507.000,4179.000,13160,764,0,66,0 -go,root-help,268,5329.031,4482.000,2241.000,13308,805,0,65,0 -rust,root-help,268,2338.954,0.000,2257.000,6680,351,0,1,0 -rust,root-help,269,1688.573,0.000,1617.000,6732,351,0,1,0 -go,root-help,269,5095.353,6381.000,0.000,13432,794,0,61,0 -go,root-help,270,4810.557,1504.000,4513.000,13180,774,0,52,0 -rust,root-help,270,3064.107,0.000,2990.000,6684,350,0,1,1 -rust,root-help,271,1865.625,0.000,1766.000,6720,352,0,1,0 -go,root-help,271,4991.698,3231.000,3231.000,13532,848,0,56,0 -go,root-help,272,4698.587,1169.000,4678.000,13532,822,0,67,0 -rust,root-help,272,2002.973,970.000,970.000,6664,350,0,1,0 -rust,root-help,273,2675.937,1093.000,1093.000,6664,352,0,1,1 -go,root-help,273,6288.944,1708.000,5980.000,13488,841,0,60,0 -go,root-help,274,5236.789,2093.000,4187.000,13164,768,0,59,2 -rust,root-help,274,2917.091,0.000,2846.000,6652,349,0,1,0 -rust,root-help,275,2001.269,0.000,1928.000,6604,349,0,1,0 -go,root-help,275,6802.981,5135.000,3081.000,13468,829,0,69,0 -go,root-help,276,5414.062,986.000,5920.000,13404,807,0,63,0 -rust,root-help,276,2365.223,1148.000,1148.000,6424,350,0,1,0 -rust,root-help,277,2406.360,0.000,2347.000,6472,350,0,1,0 -go,root-help,277,5802.060,2795.000,4659.000,13564,836,0,63,0 -go,root-help,278,7862.491,2982.000,6958.000,13216,772,0,86,0 -rust,root-help,278,2770.696,902.000,1804.000,6732,351,0,1,0 -rust,root-help,279,1732.655,0.000,1633.000,6624,350,0,1,0 -go,root-help,279,6867.281,3753.000,4691.000,13560,819,0,76,2 -go,root-help,280,5551.649,2033.000,5083.000,13244,807,0,66,0 -rust,root-help,280,2121.135,0.000,2052.000,6572,350,0,1,1 -rust,root-help,281,3046.234,0.000,2969.000,6476,349,0,1,0 -go,root-help,281,5356.022,2705.000,4057.000,13436,801,0,59,0 -go,root-help,282,4323.242,1399.000,4199.000,13276,780,0,53,0 -rust,root-help,282,3765.514,924.000,2773.000,6716,349,0,1,0 -rust,root-help,283,3510.285,0.000,3437.000,6548,349,0,1,0 -go,root-help,283,5461.992,3340.000,3340.000,13148,753,0,57,1 -go,root-help,284,4786.832,2948.000,2948.000,13220,746,0,59,0 -rust,root-help,284,2227.955,0.000,2138.000,6608,351,0,1,0 -rust,root-help,285,1900.731,0.000,1813.000,6592,350,0,1,0 -go,root-help,285,5168.289,4260.000,2130.000,13276,798,0,65,0 -go,root-help,286,4893.783,1759.000,4397.000,13404,816,0,66,0 -rust,root-help,286,2005.428,967.000,967.000,6344,350,0,1,1 -rust,root-help,287,2417.522,1177.000,1177.000,6736,349,0,1,0 -go,root-help,287,4440.412,2185.000,3278.000,13404,797,0,56,0 -go,root-help,288,5462.362,2195.000,4390.000,13164,753,0,67,0 -rust,root-help,288,2010.417,965.000,965.000,6656,351,0,1,0 -rust,root-help,289,2180.657,0.000,2121.000,6800,350,0,1,0 -go,root-help,289,4684.590,1762.000,4405.000,13472,822,0,70,1 -go,root-help,290,5148.262,1178.000,4715.000,13428,838,0,66,0 -rust,root-help,290,1917.061,926.000,926.000,6352,356,0,1,1 -rust,root-help,291,1609.243,0.000,1551.000,6752,349,0,1,0 -go,root-help,291,4378.917,2704.000,2704.000,13180,761,0,61,0 -go,root-help,292,4653.202,4595.000,1148.000,13392,819,0,70,1 -rust,root-help,292,2044.831,993.000,993.000,6732,351,0,1,0 -rust,root-help,293,1997.362,968.000,968.000,6640,350,0,1,0 -go,root-help,293,4532.485,2318.000,3477.000,13436,818,0,61,0 -go,root-help,294,4523.428,3427.000,2284.000,13404,798,0,64,0 -rust,root-help,294,2308.787,0.000,2244.000,6552,352,0,1,0 -rust,root-help,295,1660.559,795.000,795.000,6548,350,0,1,0 -go,root-help,295,5223.112,1979.000,3959.000,13476,811,0,67,2 -go,root-help,296,5491.677,2138.000,4277.000,13308,813,0,64,1 -rust,root-help,296,2303.777,0.000,2238.000,6744,350,0,1,0 -rust,root-help,297,1822.103,882.000,882.000,6608,350,0,1,0 -go,root-help,297,4566.608,2787.000,2787.000,13084,750,0,68,0 -go,root-help,298,4512.477,0.000,5490.000,13308,792,0,63,0 -rust,root-help,298,1901.212,1836.000,0.000,6600,350,0,1,0 -rust,root-help,299,1927.921,931.000,931.000,6408,352,0,1,0 -go,root-help,299,4844.710,1225.000,4902.000,13404,804,0,64,0 -go,episodes-create-help,0,5623.895,2031.000,5079.000,13340,810,0,74,0 -rust,episodes-create-help,0,2365.784,0.000,2292.000,6716,353,0,1,0 -rust,episodes-create-help,1,2795.121,1792.000,896.000,6792,351,0,1,0 -go,episodes-create-help,1,6439.628,1976.000,5929.000,13336,829,0,78,0 -go,episodes-create-help,2,6046.890,2404.000,4809.000,13420,826,0,67,1 -rust,episodes-create-help,2,2269.463,0.000,2194.000,6752,353,0,1,0 -rust,episodes-create-help,3,1682.310,0.000,1619.000,6632,350,0,1,0 -go,episodes-create-help,3,5037.795,2754.000,3672.000,13276,786,0,68,0 -go,episodes-create-help,4,5787.182,1824.000,5473.000,13436,804,0,76,0 -rust,episodes-create-help,4,2131.934,1002.000,1002.000,6748,351,0,1,0 -rust,episodes-create-help,5,2044.992,964.000,964.000,6728,351,0,1,0 -go,episodes-create-help,5,5808.863,2452.000,4904.000,13148,750,0,77,0 -go,episodes-create-help,6,5166.787,3999.000,2666.000,13568,816,0,68,0 -rust,episodes-create-help,6,3617.436,0.000,3502.000,6744,352,0,1,0 -rust,episodes-create-help,7,2288.209,0.000,2181.000,6724,350,0,1,0 -go,episodes-create-help,7,8714.200,5059.000,6070.000,13476,793,0,75,1 -go,episodes-create-help,8,6087.466,3449.000,4599.000,13404,790,0,68,0 -rust,episodes-create-help,8,3121.444,0.000,3003.000,6712,350,0,1,1 -rust,episodes-create-help,9,3276.566,1564.000,1564.000,6764,349,0,1,0 -go,episodes-create-help,9,8182.934,5651.000,4521.000,13476,813,0,83,0 -go,episodes-create-help,10,5826.065,4489.000,3367.000,13020,774,0,71,0 -rust,episodes-create-help,10,3099.774,1000.000,2001.000,6644,350,0,1,0 -rust,episodes-create-help,11,4248.591,2061.000,2061.000,6816,351,0,1,0 -go,episodes-create-help,11,5069.213,1599.000,5596.000,13340,791,0,67,0 -go,episodes-create-help,12,8857.069,2744.000,8234.000,13544,836,0,85,0 -rust,episodes-create-help,12,2967.977,956.000,1913.000,6780,351,0,1,1 -rust,episodes-create-help,13,2399.218,0.000,2311.000,6748,352,0,1,0 -go,episodes-create-help,13,5697.724,2500.000,5000.000,13340,812,0,69,0 -go,episodes-create-help,14,6161.464,5830.000,2332.000,13440,823,0,77,2 -rust,episodes-create-help,14,3244.445,0.000,3144.000,6876,353,0,0,1 -rust,episodes-create-help,15,2836.238,1348.000,1348.000,6408,357,0,1,1 -go,episodes-create-help,15,5863.206,5054.000,2527.000,13308,784,0,69,0 -go,episodes-create-help,16,4583.951,836.000,5021.000,13176,762,0,59,0 -rust,episodes-create-help,16,1871.085,888.000,888.000,6680,354,0,1,1 -rust,episodes-create-help,17,2417.010,1170.000,1170.000,6628,350,0,1,0 -go,episodes-create-help,17,4748.571,3688.000,2459.000,13396,806,0,66,0 -go,episodes-create-help,18,4920.965,2546.000,3819.000,13356,821,0,62,4 -rust,episodes-create-help,18,2374.251,0.000,1903.000,6624,352,0,1,1 -rust,episodes-create-help,19,3968.747,973.000,2919.000,6652,352,0,1,0 -go,episodes-create-help,19,6099.158,0.000,7899.000,13148,759,0,81,0 -go,episodes-create-help,20,4872.755,3893.000,2595.000,13532,790,0,66,0 -rust,episodes-create-help,20,2141.904,1031.000,1031.000,6728,351,0,1,0 -rust,episodes-create-help,21,2358.611,1136.000,1136.000,6600,350,0,1,0 -go,episodes-create-help,21,4374.239,1370.000,4111.000,13180,750,0,61,0 -go,episodes-create-help,22,4961.020,2328.000,3881.000,13428,820,0,60,0 -rust,episodes-create-help,22,1986.532,0.000,1899.000,6504,358,0,1,0 -rust,episodes-create-help,23,2225.851,0.000,2180.000,6852,351,0,1,0 -go,episodes-create-help,23,4437.246,2400.000,3200.000,13476,823,0,67,0 -go,episodes-create-help,24,4542.815,4249.000,1416.000,13604,826,0,61,2 -rust,episodes-create-help,24,1928.552,937.000,937.000,6684,355,0,1,0 -rust,episodes-create-help,25,1685.776,0.000,1630.000,6716,352,0,1,0 -go,episodes-create-help,25,5057.702,3071.000,3071.000,13352,792,0,57,0 -go,episodes-create-help,26,5344.109,4045.000,2697.000,13548,838,0,74,0 -rust,episodes-create-help,26,2341.890,1130.000,1130.000,6832,352,0,1,0 -rust,episodes-create-help,27,1996.822,971.000,971.000,6812,353,0,1,0 -go,episodes-create-help,27,4393.113,3445.000,2296.000,13292,781,0,57,0 -go,episodes-create-help,28,7681.261,1075.000,8604.000,13300,785,0,89,0 -rust,episodes-create-help,28,2066.442,0.000,2025.000,6736,351,0,1,0 -rust,episodes-create-help,29,3696.745,897.000,2692.000,6536,354,0,1,0 -go,episodes-create-help,29,8360.226,4681.000,5852.000,13432,817,0,82,2 -go,episodes-create-help,30,8506.109,3179.000,7419.000,13420,821,0,90,0 -rust,episodes-create-help,30,3497.541,2258.000,1129.000,6644,351,0,1,0 -rust,episodes-create-help,31,3654.356,886.000,2658.000,6688,351,0,1,0 -go,episodes-create-help,31,7980.724,4066.000,6099.000,13568,809,0,84,1 -go,episodes-create-help,32,9454.390,4593.000,6890.000,13404,839,0,87,0 -rust,episodes-create-help,32,3213.878,0.000,3105.000,6800,355,0,1,0 -rust,episodes-create-help,33,2532.798,1225.000,1225.000,6776,352,0,1,0 -go,episodes-create-help,33,9752.621,4044.000,10111.000,13308,818,0,84,0 -go,episodes-create-help,34,6083.499,1171.000,7031.000,13432,828,0,65,1 -rust,episodes-create-help,34,4193.268,1028.000,3085.000,6856,353,0,1,0 -rust,episodes-create-help,35,2618.348,839.000,1678.000,6768,350,0,1,0 -go,episodes-create-help,35,6565.114,3245.000,5680.000,13176,749,0,67,2 -go,episodes-create-help,36,6189.027,2227.000,5569.000,13404,801,0,73,0 -rust,episodes-create-help,36,2806.753,0.000,2739.000,6700,350,0,1,0 -rust,episodes-create-help,37,2579.707,0.000,2503.000,6672,350,0,1,0 -go,episodes-create-help,37,6634.574,4818.000,3613.000,13340,806,0,69,0 -go,episodes-create-help,38,7909.629,5998.000,4498.000,13176,768,0,74,0 -rust,episodes-create-help,38,3878.287,0.000,3805.000,6496,358,0,1,0 -rust,episodes-create-help,39,2572.783,0.000,2469.000,6640,352,0,1,0 -go,episodes-create-help,39,7291.598,4850.000,4850.000,13628,862,0,86,0 -go,episodes-create-help,40,6894.272,5871.000,2935.000,13356,828,0,74,1 -rust,episodes-create-help,40,2536.534,0.000,2424.000,6720,350,0,1,0 -rust,episodes-create-help,41,4488.452,1095.000,3286.000,6760,349,0,1,0 -go,episodes-create-help,41,6907.988,7891.000,1127.000,13404,838,0,73,0 -go,episodes-create-help,42,9119.331,4542.000,6813.000,13344,809,0,91,1 -rust,episodes-create-help,42,3400.779,0.000,3326.000,6776,351,0,1,0 -rust,episodes-create-help,43,2830.538,0.000,2745.000,6800,350,0,1,0 -go,episodes-create-help,43,9089.847,4209.000,7365.000,13420,815,0,92,0 -go,episodes-create-help,44,6839.248,2846.000,5693.000,13568,835,0,68,0 -rust,episodes-create-help,44,3051.132,0.000,2910.000,6776,352,0,1,1 -rust,episodes-create-help,45,3319.496,1074.000,2148.000,6688,351,0,1,0 -go,episodes-create-help,45,6125.448,2011.000,6033.000,13488,791,0,79,0 -go,episodes-create-help,46,5597.966,3560.000,3560.000,13180,757,0,65,0 -rust,episodes-create-help,46,2599.284,0.000,2417.000,6624,354,0,1,0 -rust,episodes-create-help,47,2094.635,995.000,995.000,6788,353,0,1,0 -go,episodes-create-help,47,5459.837,2676.000,4014.000,13584,843,0,64,0 -go,episodes-create-help,48,7109.076,2984.000,5969.000,13148,748,0,77,0 -rust,episodes-create-help,48,2135.612,1017.000,1017.000,6792,350,0,1,1 -rust,episodes-create-help,49,3330.136,811.000,2434.000,6740,351,0,1,0 -go,episodes-create-help,49,4736.537,3622.000,2414.000,13420,810,0,68,0 -go,episodes-create-help,50,4306.319,1174.000,4697.000,13560,849,0,59,0 -rust,episodes-create-help,50,2243.805,0.000,2184.000,6704,352,0,1,0 -rust,episodes-create-help,51,1815.630,883.000,883.000,6720,350,0,1,0 -go,episodes-create-help,51,4647.611,2270.000,3405.000,13304,819,0,60,0 -go,episodes-create-help,52,5217.582,0.000,7196.000,13564,857,0,52,0 -rust,episodes-create-help,52,2726.082,1312.000,1312.000,6688,351,0,1,0 -rust,episodes-create-help,53,2719.940,862.000,1724.000,6420,357,0,1,0 -go,episodes-create-help,53,5069.364,1881.000,4703.000,13180,774,0,66,0 -go,episodes-create-help,54,4274.591,5619.000,0.000,13220,757,0,64,0 -rust,episodes-create-help,54,4612.294,2661.000,0.000,6788,353,0,1,0 -rust,episodes-create-help,55,2647.263,0.000,2503.000,6724,351,0,1,1 -go,episodes-create-help,55,5537.834,4207.000,2804.000,13248,762,0,71,0 -go,episodes-create-help,56,4783.147,0.000,5753.000,13184,759,0,57,1 -rust,episodes-create-help,56,2778.700,0.000,2695.000,6728,351,0,1,0 -rust,episodes-create-help,57,2045.513,0.000,1983.000,6772,352,0,1,0 -go,episodes-create-help,57,5139.566,2870.000,3827.000,13276,794,0,72,0 -go,episodes-create-help,58,4492.911,4082.000,1633.000,13476,792,0,64,0 -rust,episodes-create-help,58,1815.461,1758.000,0.000,6800,353,0,1,0 -rust,episodes-create-help,59,2158.545,0.000,2050.000,6476,359,0,1,0 -go,episodes-create-help,59,5148.642,3256.000,3256.000,13556,834,0,66,1 -go,episodes-create-help,60,4888.474,4764.000,1588.000,13532,840,0,66,0 -rust,episodes-create-help,60,2186.299,1062.000,1062.000,6672,350,0,1,0 -rust,episodes-create-help,61,1842.771,892.000,892.000,6684,355,0,1,0 -go,episodes-create-help,61,5612.434,3605.000,3605.000,13476,813,0,73,0 -go,episodes-create-help,62,5003.189,1643.000,4929.000,13476,833,0,65,1 -rust,episodes-create-help,62,1896.784,919.000,919.000,6792,352,0,1,0 -rust,episodes-create-help,63,2493.695,800.000,1600.000,6880,353,0,1,1 -go,episodes-create-help,63,5004.081,915.000,5493.000,13532,814,0,63,0 -go,episodes-create-help,64,5293.995,2798.000,3731.000,13292,762,0,61,0 -rust,episodes-create-help,64,2069.277,2014.000,0.000,6868,352,0,1,0 -rust,episodes-create-help,65,2330.398,1121.000,1121.000,6812,352,0,1,0 -go,episodes-create-help,65,4726.600,1814.000,4537.000,13436,802,0,61,0 -go,episodes-create-help,66,5462.171,999.000,5997.000,13464,838,0,68,1 -rust,episodes-create-help,66,2416.860,0.000,2317.000,6692,352,0,1,0 -rust,episodes-create-help,67,2138.016,0.000,2049.000,6696,351,0,1,0 -go,episodes-create-help,67,4828.190,1570.000,4710.000,13492,796,0,62,0 -go,episodes-create-help,68,5217.912,2719.000,4079.000,13436,829,0,68,0 -rust,episodes-create-help,68,2585.066,2443.000,0.000,6772,352,0,1,0 -rust,episodes-create-help,69,2555.672,806.000,1612.000,6800,351,0,1,0 -go,episodes-create-help,69,4655.927,0.000,5835.000,13292,794,0,59,0 -go,episodes-create-help,70,7037.191,3242.000,4322.000,13308,795,0,62,0 -rust,episodes-create-help,70,2434.793,771.000,1543.000,6864,352,0,1,0 -rust,episodes-create-help,71,3051.293,965.000,1930.000,6640,351,0,1,0 -go,episodes-create-help,71,5449.057,2066.000,5167.000,13436,822,0,68,0 -go,episodes-create-help,72,5826.766,1268.000,6341.000,13420,831,0,76,0 -rust,episodes-create-help,72,2428.371,784.000,1569.000,6600,351,0,1,0 -rust,episodes-create-help,73,2256.560,1065.000,1065.000,6712,352,0,1,0 -go,episodes-create-help,73,5508.298,2290.000,4580.000,13404,808,0,68,0 -go,episodes-create-help,74,5293.296,3432.000,3432.000,13436,827,0,74,0 -rust,episodes-create-help,74,2065.691,1981.000,0.000,6760,352,0,1,1 -rust,episodes-create-help,75,2304.198,0.000,2231.000,6732,351,0,1,0 -go,episodes-create-help,75,4854.691,2605.000,3474.000,13340,832,0,71,0 -go,episodes-create-help,76,4952.674,969.000,5819.000,13416,832,0,71,0 -rust,episodes-create-help,76,2277.679,0.000,2200.000,6704,350,0,1,1 -rust,episodes-create-help,77,1840.918,890.000,890.000,6872,352,0,1,0 -go,episodes-create-help,77,4825.306,2646.000,3970.000,13404,827,0,73,0 -go,episodes-create-help,78,4992.138,931.000,5587.000,13148,777,0,74,0 -rust,episodes-create-help,78,1951.517,0.000,1890.000,6648,351,0,1,0 -rust,episodes-create-help,79,2023.982,0.000,1931.000,6672,350,0,1,1 -go,episodes-create-help,79,5721.108,628.000,6909.000,13440,824,0,61,1 -go,episodes-create-help,80,5297.111,1093.000,5465.000,13300,740,0,64,0 -rust,episodes-create-help,80,3651.079,0.000,3539.000,6488,356,0,1,0 -rust,episodes-create-help,81,2684.753,0.000,2552.000,6684,351,0,1,1 -go,episodes-create-help,81,5339.171,3907.000,2930.000,13436,813,0,57,0 -go,episodes-create-help,82,5318.802,2006.000,5016.000,13340,821,0,69,1 -rust,episodes-create-help,82,2101.358,1011.000,1011.000,6748,350,0,1,0 -rust,episodes-create-help,83,2076.652,0.000,1985.000,6628,353,0,1,0 -go,episodes-create-help,83,5650.285,2872.000,4308.000,13548,855,0,73,0 -go,episodes-create-help,84,5751.345,2411.000,4822.000,13256,770,0,73,0 -rust,episodes-create-help,84,2574.136,0.000,2445.000,6692,354,0,1,0 -rust,episodes-create-help,85,1981.012,0.000,1841.000,6656,351,0,1,0 -go,episodes-create-help,85,5511.213,4015.000,3212.000,13436,808,0,74,0 -go,episodes-create-help,86,4984.595,1049.000,5246.000,13164,774,0,64,2 -rust,episodes-create-help,86,2143.077,0.000,2041.000,6712,350,0,1,0 -rust,episodes-create-help,87,3437.799,0.000,3312.000,6868,353,0,1,1 -go,episodes-create-help,87,6609.757,4128.000,3096.000,13180,759,0,57,0 -go,episodes-create-help,88,5497.097,1150.000,5751.000,13340,827,0,72,0 -rust,episodes-create-help,88,2306.092,1086.000,1086.000,6684,354,0,1,0 -rust,episodes-create-help,89,2241.801,1080.000,1080.000,6856,351,0,1,0 -go,episodes-create-help,89,5107.736,3097.000,3872.000,13288,790,0,68,0 -go,episodes-create-help,90,8602.813,3905.000,6835.000,13304,794,0,85,0 -rust,episodes-create-help,90,2409.977,0.000,2322.000,6724,350,0,1,1 -rust,episodes-create-help,91,1926.810,918.000,918.000,6760,349,0,1,0 -go,episodes-create-help,91,9292.898,7026.000,3513.000,13532,822,0,91,1 -go,episodes-create-help,92,5757.066,3369.000,4493.000,13172,771,0,74,0 -rust,episodes-create-help,92,3621.593,1757.000,1757.000,6360,358,0,1,1 -rust,episodes-create-help,93,1837.823,1728.000,0.000,6792,352,0,1,0 -go,episodes-create-help,93,4639.025,3683.000,2455.000,13176,753,0,50,0 -go,episodes-create-help,94,5116.984,3640.000,3640.000,13416,800,0,70,0 -rust,episodes-create-help,94,3315.789,1075.000,2151.000,6620,351,0,1,1 -rust,episodes-create-help,95,2435.666,0.000,2350.000,6736,351,0,1,0 -go,episodes-create-help,95,5224.725,1720.000,5160.000,13288,804,0,64,0 -go,episodes-create-help,96,7559.883,1288.000,6440.000,13180,756,0,60,0 -rust,episodes-create-help,96,2716.173,1747.000,873.000,6752,351,0,1,0 -rust,episodes-create-help,97,2674.263,864.000,1729.000,6876,351,0,1,0 -go,episodes-create-help,97,4975.217,1341.000,5367.000,13172,772,0,67,0 -go,episodes-create-help,98,5008.750,2821.000,4232.000,13164,777,0,61,2 -rust,episodes-create-help,98,2413.365,1161.000,1161.000,6800,351,0,1,0 -rust,episodes-create-help,99,3693.779,0.000,3610.000,6832,349,0,1,0 -go,episodes-create-help,99,4388.204,1477.000,4433.000,13308,797,0,58,0 -go,episodes-create-help,100,4771.032,2299.000,3449.000,13348,794,0,66,0 -rust,episodes-create-help,100,2472.646,0.000,2049.000,6880,352,0,1,1 -rust,episodes-create-help,101,2357.809,1134.000,1134.000,6672,355,0,1,0 -go,episodes-create-help,101,4993.290,2520.000,3780.000,13436,853,0,77,0 -go,episodes-create-help,102,4512.408,4255.000,1418.000,13496,831,0,69,0 -rust,episodes-create-help,102,1701.387,0.000,1649.000,6768,350,0,1,0 -rust,episodes-create-help,103,1498.375,0.000,1450.000,6940,352,0,1,0 -go,episodes-create-help,103,4643.654,2307.000,3461.000,13180,759,0,63,0 -go,episodes-create-help,104,4362.395,1078.000,4313.000,13468,836,0,62,0 -rust,episodes-create-help,104,2571.831,1252.000,1252.000,6656,351,0,1,0 -rust,episodes-create-help,105,1967.747,950.000,950.000,6804,350,0,1,0 -go,episodes-create-help,105,5375.840,1900.000,4750.000,13420,820,0,65,1 -go,episodes-create-help,106,5137.671,1061.000,5308.000,13216,747,0,61,0 -rust,episodes-create-help,106,1874.121,0.000,1817.000,6888,353,0,1,1 -rust,episodes-create-help,107,1495.249,0.000,1450.000,6504,356,0,1,0 -go,episodes-create-help,107,4050.129,5277.000,0.000,13292,793,0,55,0 -go,episodes-create-help,108,4059.707,1310.000,3932.000,13600,850,0,59,0 -rust,episodes-create-help,108,1889.470,0.000,1840.000,6872,352,0,1,0 -rust,episodes-create-help,109,1818.736,0.000,1753.000,6744,355,0,1,0 -go,episodes-create-help,109,4300.941,2729.000,2729.000,13340,829,0,65,0 -go,episodes-create-help,110,4847.226,1056.000,5280.000,13532,845,0,63,0 -rust,episodes-create-help,110,1806.012,0.000,1741.000,6720,350,0,1,0 -rust,episodes-create-help,111,2199.942,0.000,2121.000,6852,352,0,1,0 -go,episodes-create-help,111,6495.292,1634.000,6538.000,13344,809,0,69,0 -go,episodes-create-help,112,6096.203,2195.000,5489.000,13404,808,0,72,0 -rust,episodes-create-help,112,1729.419,0.000,1670.000,6796,351,0,1,0 -rust,episodes-create-help,113,2304.630,1115.000,1115.000,6752,350,0,1,0 -go,episodes-create-help,113,6865.288,2435.000,6087.000,13180,767,0,79,0 -go,episodes-create-help,114,5750.503,4483.000,2989.000,13532,791,0,67,0 -rust,episodes-create-help,114,2282.788,1100.000,1100.000,6668,352,0,1,0 -rust,episodes-create-help,115,2420.076,0.000,2368.000,6816,350,0,1,0 -go,episodes-create-help,115,5352.917,2738.000,4107.000,13164,745,0,58,0 -go,episodes-create-help,116,5771.212,4259.000,3194.000,13436,806,0,69,0 -rust,episodes-create-help,116,2345.546,0.000,2264.000,6808,350,0,1,0 -rust,episodes-create-help,117,3946.705,0.000,3871.000,6640,351,0,1,0 -go,episodes-create-help,117,5532.303,5495.000,1373.000,13164,756,0,68,0 -go,episodes-create-help,118,5298.814,1132.000,5661.000,13404,800,0,67,0 -rust,episodes-create-help,118,2042.989,0.000,1962.000,6876,350,0,1,0 -rust,episodes-create-help,119,3490.327,2243.000,1121.000,6832,352,0,1,0 -go,episodes-create-help,119,5420.523,2226.000,4453.000,13556,807,0,69,0 -go,episodes-create-help,120,5161.577,1284.000,5137.000,13436,808,0,68,0 -rust,episodes-create-help,120,2332.732,1130.000,1130.000,6872,351,0,1,1 -rust,episodes-create-help,121,2080.038,1992.000,0.000,6856,352,0,1,0 -go,episodes-create-help,121,5836.424,3326.000,3326.000,13176,774,0,69,1 -go,episodes-create-help,122,4823.873,3482.000,2611.000,13420,821,0,61,0 -rust,episodes-create-help,122,2165.148,2072.000,0.000,6672,350,0,1,0 -rust,episodes-create-help,123,2126.435,1019.000,1019.000,6736,350,0,1,0 -go,episodes-create-help,123,5904.803,2119.000,5299.000,13436,815,0,73,0 -go,episodes-create-help,124,5762.936,5080.000,2032.000,13212,807,0,70,0 -rust,episodes-create-help,124,3542.034,1154.000,2309.000,6876,352,0,1,0 -rust,episodes-create-help,125,3128.307,1472.000,1472.000,6760,353,0,1,0 -go,episodes-create-help,125,5218.203,0.000,7100.000,13420,817,0,65,0 -go,episodes-create-help,126,5259.090,3427.000,3427.000,13428,819,0,67,0 -rust,episodes-create-help,126,2340.597,1139.000,1139.000,6780,351,0,1,0 -rust,episodes-create-help,127,1947.619,1855.000,0.000,6740,352,0,1,0 -go,episodes-create-help,127,4583.001,2521.000,3361.000,13112,775,0,67,0 -go,episodes-create-help,128,4644.354,1672.000,4181.000,13276,814,0,67,0 -rust,episodes-create-help,128,1737.574,0.000,1685.000,6688,351,0,1,0 -rust,episodes-create-help,129,1912.814,930.000,930.000,6600,353,0,1,0 -go,episodes-create-help,129,5593.889,3931.000,2948.000,13476,823,0,70,1 -go,episodes-create-help,130,4896.779,1781.000,4454.000,13440,811,0,73,0 -rust,episodes-create-help,130,2072.563,0.000,1965.000,6700,351,0,1,0 -rust,episodes-create-help,131,2603.762,1240.000,1240.000,6796,351,0,1,0 -go,episodes-create-help,131,5254.491,2637.000,3956.000,13432,830,0,68,0 -go,episodes-create-help,132,5041.442,5014.000,1253.000,13404,815,0,78,0 -rust,episodes-create-help,132,2164.606,0.000,2089.000,6680,355,0,1,0 -rust,episodes-create-help,133,2128.731,832.000,832.000,6676,351,0,1,1 -go,episodes-create-help,133,4768.007,4199.000,2099.000,13532,848,0,67,1 -go,episodes-create-help,134,4581.417,1149.000,4596.000,13476,806,0,62,0 -rust,episodes-create-help,134,2356.847,1134.000,1134.000,6844,350,0,1,0 -rust,episodes-create-help,135,1865.446,888.000,888.000,6944,352,0,1,0 -go,episodes-create-help,135,4763.910,2041.000,4083.000,13176,766,0,57,0 -go,episodes-create-help,136,5417.949,1183.000,5918.000,13216,772,0,67,0 -rust,episodes-create-help,136,2279.743,1112.000,1112.000,6796,349,0,1,0 -rust,episodes-create-help,137,2441.827,0.000,2375.000,6744,351,0,1,0 -go,episodes-create-help,137,4623.486,2897.000,2897.000,13432,804,0,64,0 -go,episodes-create-help,138,4840.262,0.000,6068.000,13352,825,0,69,1 -rust,episodes-create-help,138,2850.676,0.000,2288.000,6820,352,0,1,1 -rust,episodes-create-help,139,2370.132,1136.000,1136.000,6664,351,0,1,0 -go,episodes-create-help,139,4461.282,1902.000,3804.000,13428,804,0,67,0 -go,episodes-create-help,140,4343.410,3378.000,2252.000,13404,809,0,62,0 -rust,episodes-create-help,140,2789.169,0.000,2724.000,6404,356,0,1,0 -rust,episodes-create-help,141,2031.717,0.000,1961.000,6716,351,0,1,0 -go,episodes-create-help,141,4494.754,0.000,5583.000,13276,796,0,66,0 -go,episodes-create-help,142,5648.032,2782.000,4636.000,13432,818,0,64,3 -rust,episodes-create-help,142,3833.692,1871.000,1871.000,6816,353,0,1,1 -rust,episodes-create-help,143,2725.692,0.000,2625.000,6816,352,0,1,0 -go,episodes-create-help,143,7852.552,2483.000,7450.000,13532,856,0,85,0 -go,episodes-create-help,144,9191.547,6535.000,4668.000,13568,839,0,93,0 -rust,episodes-create-help,144,3483.115,0.000,3389.000,6824,352,0,1,0 -rust,episodes-create-help,145,3046.322,0.000,2961.000,6556,351,0,1,0 -go,episodes-create-help,145,5668.971,2602.000,5205.000,13564,843,0,71,0 -go,episodes-create-help,146,7781.008,4036.000,6054.000,13164,747,0,72,0 -rust,episodes-create-help,146,3494.655,846.000,2538.000,6728,356,0,1,1 -rust,episodes-create-help,147,2136.273,0.000,2029.000,6616,351,0,1,0 -go,episodes-create-help,147,8674.236,5234.000,5234.000,13428,810,0,93,0 -go,episodes-create-help,148,6826.566,4404.000,4404.000,13432,806,0,77,0 -rust,episodes-create-help,148,3560.038,872.000,2617.000,6688,353,0,1,0 -rust,episodes-create-help,149,2171.430,1029.000,1029.000,6344,358,0,1,0 -go,episodes-create-help,149,8997.363,5277.000,6332.000,13176,766,0,83,0 -go,episodes-create-help,150,6945.919,3341.000,5569.000,13176,762,0,65,0 -rust,episodes-create-help,150,2218.428,0.000,2148.000,6840,350,0,1,1 -rust,episodes-create-help,151,2148.976,1034.000,1034.000,6876,354,0,1,0 -go,episodes-create-help,151,7661.563,5221.000,4177.000,13432,810,0,82,0 -go,episodes-create-help,152,5194.559,1882.000,4705.000,13556,836,0,57,0 -rust,episodes-create-help,152,2193.961,0.000,2066.000,6692,350,0,1,0 -rust,episodes-create-help,153,2655.199,0.000,2533.000,6716,353,0,1,0 -go,episodes-create-help,153,4206.573,2785.000,2785.000,13164,758,0,55,0 -go,episodes-create-help,154,4907.620,1182.000,4728.000,13180,746,0,58,2 -rust,episodes-create-help,154,1939.544,0.000,1861.000,6504,358,0,1,1 -rust,episodes-create-help,155,1741.091,0.000,1672.000,6688,355,0,1,0 -go,episodes-create-help,155,7227.558,4047.000,5059.000,13564,836,0,73,0 -go,episodes-create-help,156,6610.459,4306.000,4306.000,13308,787,0,66,0 -rust,episodes-create-help,156,2724.498,0.000,2661.000,6652,351,0,1,0 -rust,episodes-create-help,157,2543.579,1231.000,1231.000,6340,358,0,1,0 -go,episodes-create-help,157,7017.583,2939.000,5879.000,13160,749,0,67,2 -go,episodes-create-help,158,9144.379,4967.000,6209.000,13604,843,0,86,0 -rust,episodes-create-help,158,2807.864,0.000,2682.000,6532,354,0,1,0 -rust,episodes-create-help,159,2009.856,0.000,1921.000,6832,353,0,1,0 -go,episodes-create-help,159,7138.732,4552.000,3414.000,13436,803,0,78,0 -go,episodes-create-help,160,4851.724,1187.000,4750.000,13548,835,0,66,0 -rust,episodes-create-help,160,4114.200,2008.000,2008.000,6492,357,0,1,1 -rust,episodes-create-help,161,2355.676,0.000,2267.000,6608,351,0,1,0 -go,episodes-create-help,161,10026.026,6378.000,5315.000,13420,797,0,101,0 -go,episodes-create-help,162,9565.751,3568.000,4757.000,13564,836,0,76,0 -rust,episodes-create-help,162,2287.897,737.000,1474.000,6744,351,0,1,0 -rust,episodes-create-help,163,2665.587,0.000,2544.000,6724,350,0,1,0 -go,episodes-create-help,163,8039.032,1237.000,9897.000,13400,819,0,72,1 -go,episodes-create-help,164,7482.517,1907.000,8581.000,13544,857,0,57,2 -rust,episodes-create-help,164,3182.769,1017.000,2035.000,6480,357,0,1,0 -rust,episodes-create-help,165,2751.348,1753.000,876.000,6792,351,0,1,0 -go,episodes-create-help,165,6155.704,4698.000,2819.000,13420,820,0,64,1 -go,episodes-create-help,166,5521.743,962.000,5777.000,13216,750,0,68,0 -rust,episodes-create-help,166,2529.251,1222.000,1222.000,6876,350,0,1,1 -rust,episodes-create-help,167,2288.590,0.000,2207.000,6836,351,0,1,0 -go,episodes-create-help,167,5526.362,3344.000,3344.000,13176,770,0,72,0 -go,episodes-create-help,168,7211.608,4124.000,5774.000,13212,775,0,77,1 -rust,episodes-create-help,168,2454.882,789.000,1578.000,6804,351,0,1,1 -rust,episodes-create-help,169,2561.343,0.000,2474.000,6720,350,0,1,0 -go,episodes-create-help,169,6052.731,2910.000,4851.000,13564,831,0,68,0 -go,episodes-create-help,170,5911.144,1204.000,6022.000,13440,808,0,64,0 -rust,episodes-create-help,170,2546.004,1637.000,818.000,6792,350,0,1,1 -rust,episodes-create-help,171,2297.756,0.000,2199.000,6872,353,0,1,1 -go,episodes-create-help,171,6188.075,3994.000,3994.000,13428,778,0,68,0 -go,episodes-create-help,172,4935.241,1548.000,4646.000,13160,760,0,69,0 -rust,episodes-create-help,172,2059.518,988.000,988.000,6668,355,0,1,0 -rust,episodes-create-help,173,1974.229,946.000,946.000,6844,353,0,1,0 -go,episodes-create-help,173,5217.001,2428.000,4047.000,13404,845,0,73,0 -go,episodes-create-help,174,4793.254,5991.000,0.000,13440,807,0,65,0 -rust,episodes-create-help,174,1946.026,0.000,1884.000,6872,351,0,1,0 -rust,episodes-create-help,175,1718.398,0.000,1663.000,6708,351,0,1,0 -go,episodes-create-help,175,4262.448,1388.000,4164.000,13472,826,0,66,0 -go,episodes-create-help,176,4965.478,1060.000,5304.000,13172,778,0,68,0 -rust,episodes-create-help,176,2041.645,0.000,1974.000,6408,356,0,1,0 -rust,episodes-create-help,177,1787.868,0.000,1727.000,6736,351,0,1,0 -go,episodes-create-help,177,4684.773,1757.000,4393.000,13600,819,0,58,1 -go,episodes-create-help,178,4521.824,3363.000,2242.000,13548,839,0,67,0 -rust,episodes-create-help,178,1923.073,934.000,934.000,6688,352,0,1,0 -rust,episodes-create-help,179,1796.875,0.000,1732.000,6828,351,0,1,0 -go,episodes-create-help,179,4625.620,3410.000,2273.000,13436,802,0,69,0 -go,episodes-create-help,180,4687.105,2958.000,2958.000,13400,836,0,67,0 -rust,episodes-create-help,180,1857.510,1787.000,0.000,6728,352,0,1,0 -rust,episodes-create-help,181,2202.058,0.000,2126.000,6716,350,0,1,0 -go,episodes-create-help,181,4559.035,1660.000,4151.000,13420,808,0,61,0 -go,episodes-create-help,182,5475.637,2233.000,4466.000,13416,805,0,72,0 -rust,episodes-create-help,182,1866.096,909.000,909.000,6896,352,0,1,0 -rust,episodes-create-help,183,1590.438,1542.000,0.000,6720,351,0,1,0 -go,episodes-create-help,183,3908.033,2643.000,2643.000,13396,827,0,63,0 -go,episodes-create-help,184,4011.816,2146.000,3219.000,13432,813,0,62,1 -rust,episodes-create-help,184,1805.031,872.000,872.000,6556,351,0,1,1 -rust,episodes-create-help,185,2173.854,1064.000,1064.000,6712,351,0,1,0 -go,episodes-create-help,185,4188.849,1280.000,3840.000,13472,806,0,64,1 -go,episodes-create-help,186,4493.863,2294.000,3441.000,13432,818,0,62,1 -rust,episodes-create-help,186,4218.344,2082.000,2082.000,6824,349,0,1,0 -rust,episodes-create-help,187,1941.267,0.000,1896.000,6824,352,0,1,0 -go,episodes-create-help,187,3973.895,2496.000,2496.000,13432,799,0,64,0 -go,episodes-create-help,188,3929.342,3867.000,966.000,13148,775,0,63,0 -rust,episodes-create-help,188,1666.952,0.000,1620.000,6776,351,0,1,0 -rust,episodes-create-help,189,1568.386,1516.000,0.000,6408,356,0,1,0 -go,episodes-create-help,189,4205.349,2453.000,2453.000,13548,834,0,55,3 -go,episodes-create-help,190,3878.597,2916.000,1944.000,13304,793,0,55,0 -rust,episodes-create-help,190,3588.971,0.000,3536.000,6876,352,0,1,0 -rust,episodes-create-help,191,1880.904,904.000,904.000,6704,352,0,1,0 -go,episodes-create-help,191,8034.043,2728.000,7276.000,13232,833,0,94,3 -go,episodes-create-help,192,9029.635,5202.000,5202.000,13420,842,0,95,0 -rust,episodes-create-help,192,3338.602,2167.000,1083.000,6732,351,0,1,0 -rust,episodes-create-help,193,1632.588,1549.000,0.000,6876,352,0,1,0 -go,episodes-create-help,193,7940.617,4647.000,4647.000,13428,810,0,84,0 -go,episodes-create-help,194,8338.685,6935.000,3467.000,13180,766,0,89,0 -rust,episodes-create-help,194,3016.067,0.000,2891.000,6856,350,0,1,1 -rust,episodes-create-help,195,3861.234,939.000,2819.000,6632,351,0,1,1 -go,episodes-create-help,195,6212.781,2731.000,5463.000,13392,802,0,70,0 -go,episodes-create-help,196,6406.395,1821.000,6376.000,13340,808,0,70,0 -rust,episodes-create-help,196,2619.370,1270.000,1270.000,6880,351,0,1,0 -rust,episodes-create-help,197,3698.128,1808.000,1808.000,6816,353,0,1,0 -go,episodes-create-help,197,6448.003,2501.000,6254.000,13184,771,0,74,0 -go,episodes-create-help,198,8192.911,4010.000,7018.000,12988,773,0,78,0 -rust,episodes-create-help,198,3617.056,1180.000,2360.000,6716,351,0,1,0 -rust,episodes-create-help,199,2874.460,0.000,2767.000,6712,352,0,1,0 -go,episodes-create-help,199,8156.272,4648.000,5810.000,13148,775,0,75,2 -go,episodes-create-help,200,6248.891,2362.000,5905.000,13288,783,0,60,0 -rust,episodes-create-help,200,2441.205,0.000,2339.000,6784,352,0,1,0 -rust,episodes-create-help,201,4022.186,2932.000,977.000,6784,351,0,1,0 -go,episodes-create-help,201,9300.652,6740.000,5616.000,13548,858,0,87,0 -go,episodes-create-help,202,6768.977,4509.000,4509.000,12972,752,0,79,0 -rust,episodes-create-help,202,4059.597,1992.000,1992.000,6652,351,0,1,0 -rust,episodes-create-help,203,4419.903,934.000,2804.000,6804,352,0,1,1 -go,episodes-create-help,203,7848.895,4888.000,4888.000,13564,830,0,71,0 -go,episodes-create-help,204,5961.130,2256.000,5641.000,13488,824,0,72,2 -rust,episodes-create-help,204,2209.222,0.000,2124.000,6868,351,0,1,0 -rust,episodes-create-help,205,3669.342,2389.000,1194.000,6408,356,0,1,0 -go,episodes-create-help,205,5848.929,2974.000,3966.000,13364,802,0,64,0 -go,episodes-create-help,206,5785.429,1816.000,5448.000,13248,811,0,70,0 -rust,episodes-create-help,206,2012.962,956.000,956.000,6944,353,0,1,0 -rust,episodes-create-help,207,3345.755,0.000,2741.000,6732,353,0,1,1 -go,episodes-create-help,207,4800.980,4603.000,1534.000,13428,827,0,68,0 -go,episodes-create-help,208,7732.777,5355.000,4284.000,13148,766,0,76,0 -rust,episodes-create-help,208,3656.538,881.000,2645.000,6572,350,0,1,0 -rust,episodes-create-help,209,3772.948,1192.000,2384.000,6612,351,0,1,1 -go,episodes-create-help,209,8446.469,5143.000,5143.000,13564,825,0,86,1 -go,episodes-create-help,210,8952.559,2931.000,7816.000,13436,835,0,96,0 -rust,episodes-create-help,210,2568.045,0.000,2031.000,6876,351,0,1,1 -rust,episodes-create-help,211,3919.653,946.000,2838.000,6728,352,0,1,0 -go,episodes-create-help,211,9559.019,2371.000,8300.000,13432,814,0,92,2 -go,episodes-create-help,212,7505.581,1857.000,7431.000,13600,857,0,80,0 -rust,episodes-create-help,212,3723.355,1213.000,2427.000,6740,351,0,1,0 -rust,episodes-create-help,213,3385.610,1084.000,2169.000,6736,350,0,1,1 -go,episodes-create-help,213,7160.813,3353.000,5588.000,13544,838,0,75,0 -go,episodes-create-help,214,5586.234,1060.000,6362.000,13436,819,0,71,0 -rust,episodes-create-help,214,2485.350,1188.000,1188.000,6704,352,0,1,0 -rust,episodes-create-help,215,2139.879,1030.000,1030.000,6720,351,0,1,0 -go,episodes-create-help,215,7664.920,3751.000,5627.000,13336,809,0,88,1 -go,episodes-create-help,216,4612.405,857.000,5144.000,13164,754,0,63,0 -rust,episodes-create-help,216,1790.814,0.000,1745.000,6712,350,0,1,0 -rust,episodes-create-help,217,2317.624,747.000,1495.000,6696,351,0,1,0 -go,episodes-create-help,217,4241.829,2875.000,2875.000,13404,799,0,59,0 -go,episodes-create-help,218,4910.005,2987.000,2987.000,13560,838,0,64,0 -rust,episodes-create-help,218,2381.685,0.000,1916.000,6744,350,0,1,1 -rust,episodes-create-help,219,2455.544,0.000,2318.000,6660,351,0,1,1 -go,episodes-create-help,219,4666.076,3121.000,3121.000,13164,745,0,51,0 -go,episodes-create-help,220,5498.420,3025.000,4034.000,13304,816,0,66,0 -rust,episodes-create-help,220,2232.274,0.000,2143.000,6888,352,0,1,1 -rust,episodes-create-help,221,2431.438,0.000,1934.000,6748,350,0,1,1 -go,episodes-create-help,221,4479.805,4103.000,1641.000,13224,755,0,64,0 -go,episodes-create-help,222,6063.871,3708.000,3708.000,13420,819,0,74,0 -rust,episodes-create-help,222,3489.586,0.000,3402.000,6732,352,0,1,1 -rust,episodes-create-help,223,3112.978,2009.000,1004.000,6884,352,0,1,0 -go,episodes-create-help,223,7495.812,4909.000,4909.000,13436,827,0,90,0 -go,episodes-create-help,224,7497.594,5901.000,3540.000,13420,804,0,75,0 -rust,episodes-create-help,224,2444.333,1159.000,1159.000,6796,349,0,1,0 -rust,episodes-create-help,225,2571.992,0.000,2410.000,6492,357,0,1,0 -go,episodes-create-help,225,6581.274,3462.000,5193.000,13468,842,0,72,0 -go,episodes-create-help,226,5509.561,1222.000,6110.000,13564,836,0,68,0 -rust,episodes-create-help,226,4522.766,0.000,4398.000,6728,355,0,1,1 -rust,episodes-create-help,227,2184.133,0.000,2093.000,6600,350,0,1,0 -go,episodes-create-help,227,8473.129,4496.000,5620.000,13532,855,0,91,1 -go,episodes-create-help,228,8090.169,1020.000,9185.000,13484,834,0,83,1 -rust,episodes-create-help,228,2032.488,0.000,1948.000,6812,351,0,1,0 -rust,episodes-create-help,229,2006.690,0.000,1884.000,6616,351,0,1,0 -go,episodes-create-help,229,4911.267,1355.000,5421.000,13432,831,0,65,0 -go,episodes-create-help,230,9197.679,6957.000,3478.000,13432,812,0,84,3 -rust,episodes-create-help,230,3864.521,1252.000,2504.000,6668,353,0,1,0 -rust,episodes-create-help,231,3779.190,1215.000,2431.000,6688,352,0,1,1 -go,episodes-create-help,231,7680.609,983.000,8855.000,13172,761,0,73,0 -go,episodes-create-help,232,8432.581,5921.000,4737.000,13184,753,0,85,0 -rust,episodes-create-help,232,3465.961,1127.000,2255.000,6896,352,0,1,0 -rust,episodes-create-help,233,2868.427,1862.000,931.000,6700,351,0,1,0 -go,episodes-create-help,233,5729.924,1858.000,5574.000,13184,768,0,66,0 -go,episodes-create-help,234,6265.872,2096.000,4193.000,13532,806,0,60,0 -rust,episodes-create-help,234,2798.206,882.000,1765.000,6860,352,0,1,1 -rust,episodes-create-help,235,2662.361,1269.000,1269.000,6724,352,0,1,0 -go,episodes-create-help,235,5361.103,4016.000,2677.000,13404,821,0,67,0 -go,episodes-create-help,236,5289.197,2506.000,3759.000,13276,789,0,72,1 -rust,episodes-create-help,236,2186.447,0.000,2101.000,6800,351,0,1,0 -rust,episodes-create-help,237,2186.768,1046.000,1046.000,6840,350,0,1,0 -go,episodes-create-help,237,4360.231,1608.000,4021.000,13440,806,0,63,0 -go,episodes-create-help,238,4526.745,4252.000,1417.000,13420,821,0,68,0 -rust,episodes-create-help,238,2105.515,1013.000,1013.000,6792,350,0,1,1 -rust,episodes-create-help,239,2523.441,0.000,2440.000,6720,350,0,1,0 -go,episodes-create-help,239,5233.120,5275.000,1318.000,13548,831,0,74,2 -go,episodes-create-help,240,4902.310,3087.000,3087.000,13432,796,0,59,0 -rust,episodes-create-help,240,2055.972,0.000,2001.000,6496,356,0,1,0 -rust,episodes-create-help,241,1910.279,0.000,1844.000,6864,352,0,1,0 -go,episodes-create-help,241,4322.540,1351.000,4055.000,13340,819,0,67,0 -go,episodes-create-help,242,4688.197,3622.000,2414.000,13220,768,0,68,0 -rust,episodes-create-help,242,2299.640,623.000,1246.000,6936,353,0,1,1 -rust,episodes-create-help,243,2388.437,0.000,2328.000,6756,351,0,1,0 -go,episodes-create-help,243,4537.274,2945.000,2945.000,13600,854,0,53,1 -go,episodes-create-help,244,4426.887,939.000,4699.000,13416,824,0,64,0 -rust,episodes-create-help,244,2081.470,0.000,2009.000,6500,357,0,1,1 -rust,episodes-create-help,245,2425.907,0.000,2371.000,6668,352,0,1,0 -go,episodes-create-help,245,6988.689,2759.000,4139.000,13404,803,0,90,1 -go,episodes-create-help,246,5708.495,2561.000,3842.000,13548,847,0,66,1 -rust,episodes-create-help,246,2581.530,0.000,2525.000,6744,352,0,1,0 -rust,episodes-create-help,247,2809.979,0.000,2741.000,6792,352,0,1,1 -go,episodes-create-help,247,4718.424,0.000,5955.000,13492,803,0,67,1 -go,episodes-create-help,248,4787.164,925.000,5555.000,13404,812,0,64,0 -rust,episodes-create-help,248,2508.442,0.000,2410.000,6804,350,0,1,1 -rust,episodes-create-help,249,2380.083,1129.000,1129.000,6668,352,0,1,0 -go,episodes-create-help,249,6104.739,4323.000,3458.000,13308,809,0,69,0 -go,episodes-create-help,250,4413.902,2412.000,3217.000,13084,763,0,56,0 -rust,episodes-create-help,250,1948.181,0.000,1872.000,6792,352,0,1,0 -rust,episodes-create-help,251,1713.840,1645.000,0.000,6728,352,0,1,0 -go,episodes-create-help,251,5353.567,3421.000,3421.000,13556,835,0,80,0 -go,episodes-create-help,252,7716.006,5657.000,3771.000,13420,815,0,86,0 -rust,episodes-create-help,252,2842.701,0.000,2735.000,6756,351,0,1,1 -rust,episodes-create-help,253,3849.181,932.000,2798.000,6664,350,0,1,1 -go,episodes-create-help,253,6637.880,2801.000,5602.000,13340,820,0,70,0 -go,episodes-create-help,254,5366.442,3873.000,2904.000,13276,813,0,65,0 -rust,episodes-create-help,254,2485.138,795.000,1590.000,6528,351,0,1,1 -rust,episodes-create-help,255,3264.783,1053.000,2107.000,6488,356,0,1,0 -go,episodes-create-help,255,9883.639,5431.000,6517.000,13536,808,0,92,1 -go,episodes-create-help,256,6887.809,2270.000,5677.000,13420,808,0,73,2 -rust,episodes-create-help,256,3674.514,0.000,3604.000,6668,351,0,1,0 -rust,episodes-create-help,257,3627.524,2657.000,885.000,6856,352,0,1,0 -go,episodes-create-help,257,9184.775,6897.000,4598.000,13416,824,0,91,0 -go,episodes-create-help,258,5786.280,3284.000,4105.000,13436,827,0,67,0 -rust,episodes-create-help,258,2129.320,1024.000,1024.000,6808,351,0,1,0 -rust,episodes-create-help,259,2062.386,988.000,988.000,6700,351,0,1,0 -go,episodes-create-help,259,4422.028,943.000,4718.000,13392,795,0,64,0 -go,episodes-create-help,260,6709.665,2624.000,5248.000,13564,830,0,78,0 -rust,episodes-create-help,260,1905.170,911.000,911.000,6684,351,0,1,0 -rust,episodes-create-help,261,3236.359,0.000,3151.000,6808,354,0,1,0 -go,episodes-create-help,261,5426.645,2839.000,4259.000,13276,800,0,66,0 -go,episodes-create-help,262,5316.797,5536.000,1107.000,13440,795,0,61,0 -rust,episodes-create-help,262,2933.922,945.000,1890.000,6816,350,0,1,1 -rust,episodes-create-help,263,2059.831,0.000,1984.000,6832,352,0,1,0 -go,episodes-create-help,263,5836.164,2491.000,4983.000,13292,809,0,65,0 -go,episodes-create-help,264,6242.217,5164.000,2582.000,13416,810,0,79,0 -rust,episodes-create-help,264,2667.893,1284.000,1284.000,6816,351,0,1,0 -rust,episodes-create-help,265,1502.272,0.000,1443.000,6916,352,0,1,0 -go,episodes-create-help,265,5190.440,3953.000,2635.000,13236,749,0,66,0 -go,episodes-create-help,266,6421.243,3408.000,4260.000,13240,795,0,80,0 -rust,episodes-create-help,266,3376.675,824.000,2474.000,6632,351,0,1,0 -rust,episodes-create-help,267,1916.068,0.000,1854.000,6752,351,0,1,0 -go,episodes-create-help,267,7751.311,6777.000,2259.000,13172,762,0,79,1 -go,episodes-create-help,268,10719.990,6722.000,6722.000,13420,806,0,92,0 -rust,episodes-create-help,268,2901.761,0.000,2775.000,6828,351,0,1,0 -rust,episodes-create-help,269,2088.904,0.000,1929.000,6876,351,0,1,0 -go,episodes-create-help,269,6969.633,3392.000,5654.000,13432,820,0,84,0 -go,episodes-create-help,270,8699.924,7605.000,3802.000,13480,818,0,88,0 -rust,episodes-create-help,270,3511.648,1133.000,2267.000,6776,351,0,1,1 -rust,episodes-create-help,271,3619.120,0.000,3455.000,6676,352,0,1,0 -go,episodes-create-help,271,6030.280,3006.000,5011.000,13416,822,0,63,0 -go,episodes-create-help,272,8174.397,2016.000,8064.000,13436,819,0,76,0 -rust,episodes-create-help,272,3045.502,980.000,1960.000,6544,351,0,1,1 -rust,episodes-create-help,273,3168.302,1010.000,2021.000,6344,358,0,1,0 -go,episodes-create-help,273,6705.397,5824.000,2912.000,13148,765,0,64,0 -go,episodes-create-help,274,7990.561,3316.000,6632.000,13244,828,0,90,0 -rust,episodes-create-help,274,3113.249,0.000,3068.000,6828,351,0,1,0 -rust,episodes-create-help,275,4038.156,1659.000,1659.000,6668,349,0,1,1 -go,episodes-create-help,275,7986.994,4237.000,5649.000,13404,805,0,79,0 -go,episodes-create-help,276,9602.470,7613.000,4350.000,13368,788,0,95,0 -rust,episodes-create-help,276,2489.498,774.000,1548.000,6700,350,0,1,0 -rust,episodes-create-help,277,4216.501,0.000,3988.000,6732,351,0,1,1 -go,episodes-create-help,277,7192.241,6159.000,3695.000,13604,860,0,75,1 -go,episodes-create-help,278,5529.499,1065.000,6391.000,13416,826,0,68,0 -rust,episodes-create-help,278,2681.738,0.000,2596.000,6684,351,0,1,0 -rust,episodes-create-help,279,2365.413,0.000,2248.000,6476,359,0,1,0 -go,episodes-create-help,279,9374.390,4695.000,7043.000,13548,832,0,87,0 -go,episodes-create-help,280,9703.961,8223.000,3524.000,13440,800,0,81,0 -rust,episodes-create-help,280,2903.845,950.000,1900.000,6792,351,0,1,0 -rust,episodes-create-help,281,3522.477,1152.000,2305.000,6776,351,0,1,0 -go,episodes-create-help,281,5240.525,0.000,7050.000,13340,807,0,66,0 -go,episodes-create-help,282,5032.144,1371.000,5486.000,13600,840,0,66,0 -rust,episodes-create-help,282,2765.796,0.000,2686.000,6848,352,0,1,0 -rust,episodes-create-help,283,2880.903,0.000,2811.000,6736,352,0,1,0 -go,episodes-create-help,283,6019.680,1917.000,5753.000,13532,851,0,66,0 -go,episodes-create-help,284,6747.257,3376.000,5065.000,13404,814,0,68,1 -rust,episodes-create-help,284,2914.375,1904.000,952.000,6892,351,0,1,0 -rust,episodes-create-help,285,2687.830,0.000,2597.000,6496,356,0,1,1 -go,episodes-create-help,285,5022.276,5097.000,1274.000,13340,812,0,64,0 -go,episodes-create-help,286,5403.711,2400.000,4801.000,13340,827,0,71,0 -rust,episodes-create-help,286,2039.602,1950.000,0.000,6712,352,0,1,0 -rust,episodes-create-help,287,2963.989,0.000,2835.000,6792,352,0,1,1 -go,episodes-create-help,287,7638.610,4028.000,6042.000,13436,818,0,81,2 -go,episodes-create-help,288,7301.667,3184.000,6369.000,13148,749,0,74,0 -rust,episodes-create-help,288,3007.561,0.000,2905.000,6900,352,0,1,0 -rust,episodes-create-help,289,1863.120,0.000,1724.000,6788,352,0,1,0 -go,episodes-create-help,289,5632.232,2042.000,5106.000,13544,851,0,62,0 -go,episodes-create-help,290,4597.457,1484.000,4454.000,13304,772,0,54,0 -rust,episodes-create-help,290,1900.200,919.000,919.000,6780,351,0,1,0 -rust,episodes-create-help,291,3459.370,842.000,2527.000,6812,350,0,1,1 -go,episodes-create-help,291,6025.139,3160.000,4214.000,13472,862,0,69,0 -go,episodes-create-help,292,7717.499,3690.000,6151.000,13180,769,0,78,0 -rust,episodes-create-help,292,3400.900,2179.000,1089.000,6620,351,0,1,1 -rust,episodes-create-help,293,2873.328,919.000,1839.000,6776,352,0,1,0 -go,episodes-create-help,293,7217.078,0.000,8653.000,13548,827,0,91,0 -go,episodes-create-help,294,7297.178,7831.000,1118.000,13432,829,0,74,0 -rust,episodes-create-help,294,2564.858,820.000,1640.000,6648,351,0,1,0 -rust,episodes-create-help,295,2924.354,1868.000,934.000,6404,358,0,1,0 -go,episodes-create-help,295,5165.334,3404.000,3404.000,13180,769,0,66,0 -go,episodes-create-help,296,5317.089,3923.000,2615.000,13276,788,0,59,0 -rust,episodes-create-help,296,1986.882,950.000,950.000,6684,351,0,1,0 -rust,episodes-create-help,297,4006.016,0.000,3876.000,6708,350,0,1,0 -go,episodes-create-help,297,6062.730,4559.000,3039.000,13532,815,0,71,0 -go,episodes-create-help,298,6046.809,3607.000,4809.000,13312,803,0,69,0 -rust,episodes-create-help,298,3721.962,1201.000,2402.000,6692,351,0,1,1 -rust,episodes-create-help,299,2588.543,0.000,2503.000,6772,351,0,1,0 -go,episodes-create-help,299,5370.369,3019.000,4025.000,13436,798,0,67,0 diff --git a/bench/results/linux-amd64.json b/bench/results/linux-amd64.json deleted file mode 100644 index c9ed665..0000000 --- a/bench/results/linux-amd64.json +++ /dev/null @@ -1,322 +0,0 @@ -[ - { - "implementation": "go", - "command": "root-help", - "samples": 300, - "cpu_percent": 126.77180301202564, - "metrics": { - "wall_us": { - "mean": 6341.147223333333, - "median": 6004.094499999999, - "p95": 9276.939, - "min": 3921.427, - "max": 10478.786, - "stddev": 1533.139337293172 - }, - "user_us": { - "mean": 3449.3766666666666, - "median": 3227.0, - "p95": 6381.0, - "min": 0.0, - "max": 9684.0, - "stddev": 1675.2967343495368 - }, - "system_us": { - "mean": 4589.41, - "median": 4537.0, - "p95": 7356.0, - "min": 0.0, - "max": 9687.0, - "stddev": 1764.9137491390336 - }, - "peak_rss_kib": { - "mean": 13359.8, - "median": 13404.0, - "p95": 13564.0, - "min": 12968.0, - "max": 13604.0, - "stddev": 136.66001609834532 - }, - "minor_faults": { - "mean": 804.0433333333333, - "median": 807.0, - "p95": 843.0, - "min": 746.0, - "max": 864.0, - "stddev": 26.731656431196992 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 71.17666666666666, - "median": 70.0, - "p95": 91.0, - "min": 45.0, - "max": 98.0, - "stddev": 10.672649884426807 - }, - "involuntary_context_switches": { - "mean": 0.47, - "median": 0.0, - "p95": 2.0, - "min": 0.0, - "max": 6.0, - "stddev": 0.9393792276462862 - }, - "total_cpu_us": { - "mean": 8038.786666666667, - "median": 7715.0, - "p95": 11438.0, - "min": 4873.0, - "max": 13286.0, - "stddev": 1906.8806223311994 - } - } - }, - { - "implementation": "rust", - "command": "root-help", - "samples": 300, - "cpu_percent": 95.45116754863707, - "metrics": { - "wall_us": { - "mean": 2690.85935, - "median": 2544.5655, - "p95": 3842.319, - "min": 1387.266, - "max": 5580.644, - "stddev": 693.7030734145224 - }, - "user_us": { - "mean": 638.1666666666666, - "median": 765.0, - "p95": 2095.0, - "min": 0.0, - "max": 3453.0, - "stddev": 718.4236903728112 - }, - "system_us": { - "mean": 1930.29, - "median": 1930.5, - "p95": 3337.0, - "min": 0.0, - "max": 3835.0, - "stddev": 797.9393497628752 - }, - "peak_rss_kib": { - "mean": 6620.133333333333, - "median": 6640.0, - "p95": 6792.0, - "min": 6228.0, - "max": 6860.0, - "stddev": 117.87126687855508 - }, - "minor_faults": { - "mean": 350.24666666666667, - "median": 350.0, - "p95": 353.0, - "min": 348.0, - "max": 358.0, - "stddev": 1.5510713143573451 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 0.9933333333333333, - "median": 1.0, - "p95": 1.0, - "min": 0.0, - "max": 1.0, - "stddev": 0.08137703743822469 - }, - "involuntary_context_switches": { - "mean": 0.20333333333333334, - "median": 0.0, - "p95": 1.0, - "min": 0.0, - "max": 1.0, - "stddev": 0.402478432824529 - }, - "total_cpu_us": { - "mean": 2568.4566666666665, - "median": 2394.0, - "p95": 3711.0, - "min": 1349.0, - "max": 4490.0, - "stddev": 660.8478756785777 - } - } - }, - { - "implementation": "go", - "command": "episodes-create-help", - "samples": 300, - "cpu_percent": 126.4117530395956, - "metrics": { - "wall_us": { - "mean": 6037.204993333334, - "median": 5562.034, - "p95": 9144.379, - "min": 3878.597, - "max": 10719.99, - "stddev": 1492.1623623526116 - }, - "user_us": { - "mean": 3111.5333333333333, - "median": 2913.0, - "p95": 5998.0, - "min": 0.0, - "max": 8223.0, - "stddev": 1667.6253682673723 - }, - "system_us": { - "mean": 4520.203333333333, - "median": 4515.0, - "p95": 7365.0, - "min": 0.0, - "max": 10111.0, - "stddev": 1715.937425623544 - }, - "peak_rss_kib": { - "mean": 13375.946666666667, - "median": 13416.0, - "p95": 13564.0, - "min": 12972.0, - "max": 13628.0, - "stddev": 136.32782971776362 - }, - "minor_faults": { - "mean": 805.6666666666666, - "median": 809.0, - "p95": 849.0, - "min": 740.0, - "max": 862.0, - "stddev": 28.671627477738724 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 70.58333333333333, - "median": 68.0, - "p95": 91.0, - "min": 50.0, - "max": 101.0, - "stddev": 9.655208726669535 - }, - "involuntary_context_switches": { - "mean": 0.28, - "median": 0.0, - "p95": 2.0, - "min": 0.0, - "max": 4.0, - "stddev": 0.649307323229917 - }, - "total_cpu_us": { - "mean": 7631.736666666667, - "median": 7116.0, - "p95": 11176.0, - "min": 4833.0, - "max": 14155.0, - "stddev": 1808.948906774932 - } - } - }, - { - "implementation": "rust", - "command": "episodes-create-help", - "samples": 300, - "cpu_percent": 95.63121746636804, - "metrics": { - "wall_us": { - "mean": 2613.5294166666667, - "median": 2435.2295000000004, - "p95": 3919.653, - "min": 1495.249, - "max": 4612.294, - "stddev": 686.079782003456 - }, - "user_us": { - "mean": 664.0133333333333, - "median": 808.5, - "p95": 1992.0, - "min": 0.0, - "max": 2932.0, - "stddev": 705.4151967616109 - }, - "system_us": { - "mean": 1835.3366666666666, - "median": 1901.5, - "p95": 3286.0, - "min": 0.0, - "max": 4398.0, - "stddev": 837.4302856490337 - }, - "peak_rss_kib": { - "mean": 6725.626666666667, - "median": 6734.0, - "p95": 6876.0, - "min": 6340.0, - "max": 6944.0, - "stddev": 118.40999657498892 - }, - "minor_faults": { - "mean": 351.9033333333333, - "median": 351.0, - "p95": 357.0, - "min": 349.0, - "max": 359.0, - "stddev": 2.010138193148808 - }, - "major_faults": { - "mean": 0.0, - "median": 0.0, - "p95": 0.0, - "min": 0.0, - "max": 0.0, - "stddev": 0.0 - }, - "voluntary_context_switches": { - "mean": 0.9966666666666667, - "median": 1.0, - "p95": 1.0, - "min": 0.0, - "max": 1.0, - "stddev": 0.057638721552635275 - }, - "involuntary_context_switches": { - "mean": 0.22, - "median": 0.0, - "p95": 1.0, - "min": 0.0, - "max": 1.0, - "stddev": 0.4142463035441596 - }, - "total_cpu_us": { - "mean": 2499.35, - "median": 2322.0, - "p95": 3756.0, - "min": 1443.0, - "max": 4398.0, - "stddev": 661.338567477607 - } - } - } -] diff --git a/bench/results/youtube-import.json b/bench/results/youtube-import.json new file mode 100644 index 0000000..1d6de06 --- /dev/null +++ b/bench/results/youtube-import.json @@ -0,0 +1,420 @@ +{ + "command": "listenbox import --slug https://www.youtube.com/watch?v=w", + "warmups_per_build": 4, + "samples_per_build": 20, + "statistic": "median for each metric; CPU time is user + system; CPU utilization is 100 * CPU / wall per sample", + "summary": { + "go": { + "wall_ms": 330.7795065, + "cpu_ms": 197.7985, + "cpu_percent": 58.422114, + "peak_rss_mib": 31.234375, + "binary_mib": 21.590682983398438 + }, + "rust": { + "wall_ms": 445.3642865, + "cpu_ms": 284.9485, + "cpu_percent": 64.6710655, + "peak_rss_mib": 34.988281, + "binary_mib": 19.57439422607422 + } + }, + "binaries": { + "go": { + "commit": "3d0f56927a9fc0ae8a97d1cb64d209505ea2ae14", + "bytes": 22639472, + "sha256": "b14d4f4fe699d2995d21f9da7211a36fab4f1a2c7c2c98c9aa7c1101ed6a9692" + }, + "rust": { + "commit": "caaec943677ce8fc4b32713b12389213d8445b7f", + "bytes": 20525240, + "sha256": "ffd778f8d1dc2ae07678d11ddeedcb1515c2f4f1e93e9b40c990342d135ea26b" + } + }, + "samples": [ + { + "build": "go", + "wall_ms": 469.51108, + "cpu_ms": 214.484, + "cpu_percent": 45.682415, + "peak_rss_mib": 31.757812, + "warmup": true + }, + { + "build": "rust", + "wall_ms": 497.52089, + "cpu_ms": 284.093, + "cpu_percent": 57.101723, + "peak_rss_mib": 35.785156, + "warmup": true + }, + { + "build": "rust", + "wall_ms": 443.780693, + "cpu_ms": 265.213, + "cpu_percent": 59.762176, + "peak_rss_mib": 34.898438, + "warmup": true + }, + { + "build": "go", + "wall_ms": 391.12498, + "cpu_ms": 200.542, + "cpu_percent": 51.273125, + "peak_rss_mib": 31.992188, + "warmup": true + }, + { + "build": "go", + "wall_ms": 396.940115, + "cpu_ms": 246.716, + "cpu_percent": 62.154464, + "peak_rss_mib": 31.167969, + "warmup": true + }, + { + "build": "rust", + "wall_ms": 467.659592, + "cpu_ms": 293.222, + "cpu_percent": 62.699879, + "peak_rss_mib": 35.269531, + "warmup": true + }, + { + "build": "rust", + "wall_ms": 482.621079, + "cpu_ms": 308.494, + "cpu_percent": 63.92054, + "peak_rss_mib": 34.6875, + "warmup": true + }, + { + "build": "go", + "wall_ms": 361.388044, + "cpu_ms": 205.567, + "cpu_percent": 56.882623, + "peak_rss_mib": 31.679688, + "warmup": true + }, + { + "build": "go", + "wall_ms": 321.53827, + "cpu_ms": 206.865, + "cpu_percent": 64.336043, + "peak_rss_mib": 31.113281, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 509.050381, + "cpu_ms": 309.291, + "cpu_percent": 60.758426, + "peak_rss_mib": 34.449219, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 442.303216, + "cpu_ms": 285.869, + "cpu_percent": 64.631906, + "peak_rss_mib": 35.421875, + "warmup": false + }, + { + "build": "go", + "wall_ms": 373.956676, + "cpu_ms": 196.051, + "cpu_percent": 52.426126, + "peak_rss_mib": 31.703125, + "warmup": false + }, + { + "build": "go", + "wall_ms": 299.360155, + "cpu_ms": 173.864, + "cpu_percent": 58.078538, + "peak_rss_mib": 30.097656, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 425.323767, + "cpu_ms": 273.642, + "cpu_percent": 64.33734, + "peak_rss_mib": 35.460938, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 450.82256, + "cpu_ms": 297.899, + "cpu_percent": 66.078991, + "peak_rss_mib": 35.863281, + "warmup": false + }, + { + "build": "go", + "wall_ms": 335.204394, + "cpu_ms": 188.388, + "cpu_percent": 56.200934, + "peak_rss_mib": 31.828125, + "warmup": false + }, + { + "build": "go", + "wall_ms": 358.781297, + "cpu_ms": 206.916, + "cpu_percent": 57.671903, + "peak_rss_mib": 31.652344, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 422.009441, + "cpu_ms": 282.698, + "cpu_percent": 66.988549, + "peak_rss_mib": 34.667969, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 436.235477, + "cpu_ms": 284.028, + "cpu_percent": 65.108872, + "peak_rss_mib": 34.667969, + "warmup": false + }, + { + "build": "go", + "wall_ms": 326.354619, + "cpu_ms": 182.821, + "cpu_percent": 56.019124, + "peak_rss_mib": 31.527344, + "warmup": false + }, + { + "build": "go", + "wall_ms": 348.042703, + "cpu_ms": 197.471, + "cpu_percent": 56.737578, + "peak_rss_mib": 36.335938, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 413.989104, + "cpu_ms": 278.71, + "cpu_percent": 67.323028, + "peak_rss_mib": 35.183594, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 451.719557, + "cpu_ms": 302.143, + "cpu_percent": 66.887297, + "peak_rss_mib": 34.914062, + "warmup": false + }, + { + "build": "go", + "wall_ms": 305.667915, + "cpu_ms": 197.456, + "cpu_percent": 64.598209, + "peak_rss_mib": 31.332031, + "warmup": false + }, + { + "build": "go", + "wall_ms": 323.10832, + "cpu_ms": 198.126, + "cpu_percent": 61.318755, + "peak_rss_mib": 30.742188, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 447.995389, + "cpu_ms": 275.119, + "cpu_percent": 61.411123, + "peak_rss_mib": 34.757812, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 467.634566, + "cpu_ms": 292.664, + "cpu_percent": 62.583911, + "peak_rss_mib": 35.632812, + "warmup": false + }, + { + "build": "go", + "wall_ms": 302.156368, + "cpu_ms": 179.493, + "cpu_percent": 59.40401, + "peak_rss_mib": 30.476562, + "warmup": false + }, + { + "build": "go", + "wall_ms": 372.264437, + "cpu_ms": 220.012, + "cpu_percent": 59.100999, + "peak_rss_mib": 31.511719, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 428.780041, + "cpu_ms": 279.916, + "cpu_percent": 65.281957, + "peak_rss_mib": 34.359375, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 466.943997, + "cpu_ms": 302.855, + "cpu_percent": 64.858956, + "peak_rss_mib": 35.074219, + "warmup": false + }, + { + "build": "go", + "wall_ms": 324.581098, + "cpu_ms": 198.574, + "cpu_percent": 61.178547, + "peak_rss_mib": 31.121094, + "warmup": false + }, + { + "build": "go", + "wall_ms": 361.814846, + "cpu_ms": 207.118, + "cpu_percent": 57.244196, + "peak_rss_mib": 31.535156, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 408.963652, + "cpu_ms": 282.212, + "cpu_percent": 69.006622, + "peak_rss_mib": 34.574219, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 420.689671, + "cpu_ms": 267.222, + "cpu_percent": 63.519981, + "peak_rss_mib": 35.246094, + "warmup": false + }, + { + "build": "go", + "wall_ms": 311.846122, + "cpu_ms": 182.031, + "cpu_percent": 58.372058, + "peak_rss_mib": 30.9375, + "warmup": false + }, + { + "build": "go", + "wall_ms": 339.973373, + "cpu_ms": 217.454, + "cpu_percent": 63.962068, + "peak_rss_mib": 32.089844, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 479.051414, + "cpu_ms": 289.806, + "cpu_percent": 60.495803, + "peak_rss_mib": 35.667969, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 454.132249, + "cpu_ms": 293.87, + "cpu_percent": 64.710225, + "peak_rss_mib": 34.644531, + "warmup": false + }, + { + "build": "go", + "wall_ms": 335.909439, + "cpu_ms": 202.119, + "cpu_percent": 60.170682, + "peak_rss_mib": 31.09375, + "warmup": false + }, + { + "build": "go", + "wall_ms": 384.437585, + "cpu_ms": 224.789, + "cpu_percent": 58.47217, + "peak_rss_mib": 31.539062, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 474.70073, + "cpu_ms": 301.669, + "cpu_percent": 63.549302, + "peak_rss_mib": 34.90625, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 442.733184, + "cpu_ms": 283.348, + "cpu_percent": 63.99972, + "peak_rss_mib": 34.5, + "warmup": false + }, + { + "build": "go", + "wall_ms": 317.975517, + "cpu_ms": 179.65, + "cpu_percent": 56.49806, + "peak_rss_mib": 30.472656, + "warmup": false + }, + { + "build": "go", + "wall_ms": 314.683312, + "cpu_ms": 175.699, + "cpu_percent": 55.833593, + "peak_rss_mib": 31.136719, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 430.248921, + "cpu_ms": 283.835, + "cpu_percent": 65.96995, + "peak_rss_mib": 35.0625, + "warmup": false + }, + { + "build": "rust", + "wall_ms": 482.892779, + "cpu_ms": 310.09, + "cpu_percent": 64.215083, + "peak_rss_mib": 35.386719, + "warmup": false + }, + { + "build": "go", + "wall_ms": 372.374935, + "cpu_ms": 219.402, + "cpu_percent": 58.919648, + "peak_rss_mib": 30.613281, + "warmup": false + } + ] +} diff --git a/bench/startup.c b/bench/startup.c deleted file mode 100644 index ae214fd..0000000 --- a/bench/startup.c +++ /dev/null @@ -1,68 +0,0 @@ -// Linux startup benchmark: no shell, HTTP, JSON response decoding, or terminal rendering. -// Build: cc -O2 -Wall -Wextra -Werror bench/startup.c -o /tmp/cli-startup -// Run: taskset -c CPU /tmp/cli-startup /absolute/go-cli /absolute/rust-cli > samples.csv -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -extern char **environ; - -static double micros(struct timespec time) { - return time.tv_sec * 1000000.0 + time.tv_nsec / 1000.0; -} -static double cpu_micros(struct timeval time) { - return time.tv_sec * 1000000.0 + time.tv_usec; -} -static void sample(char *binary, const char *label, int scenario, int iteration) { - char *root[] = {binary, "--help", NULL}; - char *episode[] = {binary, "episodes", "create", "--help", NULL}; - posix_spawn_file_actions_t actions; - if (posix_spawn_file_actions_init(&actions) || - posix_spawn_file_actions_addopen(&actions, STDIN_FILENO, "/dev/null", O_RDONLY, 0) || - posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, "/dev/null", O_WRONLY, 0) || - posix_spawn_file_actions_addopen(&actions, STDERR_FILENO, "/dev/null", O_WRONLY, 0)) { - fprintf(stderr, "cannot configure benchmark descriptors\n"); exit(1); - } - pid_t child; - struct timespec begin, end; - struct rusage usage; - int status; - clock_gettime(CLOCK_MONOTONIC, &begin); - int error = posix_spawn(&child, binary, &actions, NULL, scenario ? episode : root, environ); - if (error) { fprintf(stderr, "spawn %s: %s\n", binary, strerror(error)); exit(1); } - while (wait4(child, &status, 0, &usage) < 0) { - if (errno != EINTR) { perror("wait4"); exit(1); } - } - clock_gettime(CLOCK_MONOTONIC, &end); - posix_spawn_file_actions_destroy(&actions); - if (!WIFEXITED(status) || WEXITSTATUS(status)) { - fprintf(stderr, "%s scenario %d failed: status %d\n", binary, scenario, status); exit(1); - } - if (iteration >= 0) { - printf("%s,%s,%d,%.3f,%.3f,%.3f,%ld,%ld,%ld,%ld,%ld\n", label, - scenario ? "episodes-create-help" : "root-help", iteration, - micros(end)-micros(begin), cpu_micros(usage.ru_utime), cpu_micros(usage.ru_stime), - usage.ru_maxrss, usage.ru_minflt, usage.ru_majflt, usage.ru_nvcsw, usage.ru_nivcsw); - } -} -int main(int argc, char **argv) { - if (argc != 3) { fprintf(stderr, "usage: %s GO_BINARY RUST_BINARY\n", argv[0]); return 1; } - puts("implementation,command,sample,wall_us,user_us,system_us,peak_rss_kib,minor_faults,major_faults,voluntary_context_switches,involuntary_context_switches"); - for (int scenario = 0; scenario < 2; scenario++) { - // Alternating order counters drift. Both executables get 30 discarded warmups. - for (int i = -30; i < 300; i++) { - for (int order = 0; order < 2; order++) { - int impl = (i + 30 + order) % 2; - sample(argv[impl + 1], impl ? "rust" : "go", scenario, i); - } - } - } - return 0; -} diff --git a/bench/summarize.py b/bench/summarize.py index 4e2372f..60367b7 100644 --- a/bench/summarize.py +++ b/bench/summarize.py @@ -1,41 +1,59 @@ -"""Summarize startup.c CSV output using only the Python standard library.""" -import csv +#!/usr/bin/env python3 +"""Summarize api:benchmark-cli: one median per metric, plus auditable raw samples.""" +import hashlib import json -import math +from pathlib import Path import statistics +import subprocess import sys -from collections import defaultdict +log_path, go_binary, rust_binary = map(Path, sys.argv[1:]) +samples = [] +passed = 0 +suite_passed = False +for line in log_path.read_text().splitlines(): + event = json.loads(line) + if event.get("Action") == "pass": + if event.get("Test") == "TestCLIYouTubeImportBenchmark": + passed += 1 + elif "Test" not in event: + suite_passed = True + _, marker, sample = event.get("Output", "").partition("CLI_IMPORT_SAMPLE ") + if marker: + build, metrics = sample.split(" ", 1) + samples.append({"build": build, **json.loads(metrics)}) +if passed != 12 or not suite_passed or len(samples) != 48: + raise SystemExit("Expected 12 passing measurement cases and 48 successful imports") +if [row["build"] for row in samples] != ["go", "rust", "rust", "go"] * 12: + raise SystemExit("Unexpected import order") -def distribution(values): - values = sorted(values) - return { - "mean": statistics.mean(values), - "median": statistics.median(values), - "p95": values[math.ceil(len(values) * 0.95) - 1], - "min": values[0], - "max": values[-1], - "stddev": statistics.pstdev(values), +summary = {} +binaries = {} +for build, binary in (("go", go_binary), ("rust", rust_binary)): + rows = [row for row in samples if row["build"] == build] + for index, row in enumerate(rows): + row["warmup"] = index < 4 + measured = rows[4:] + metrics = ("wall_ms", "cpu_ms", "cpu_percent", "peak_rss_mib") + if any(row[key] <= 0 for row in rows for key in metrics): + raise SystemExit("Nonpositive process accounting value") + summary[build] = {key: statistics.median(row[key] for row in measured) for key in metrics} + data = binary.read_bytes() + summary[build]["binary_mib"] = len(data) / 1024**2 + binaries[build] = { + "commit": subprocess.check_output( + ["git", "-C", str(binary.parent), "rev-parse", "HEAD"], text=True + ).strip(), + "bytes": len(data), + "sha256": hashlib.sha256(data).hexdigest(), } - - -groups = defaultdict(list) -with open(sys.argv[1], newline="", encoding="utf-8") as source: - for row in csv.DictReader(source): - groups[(row["implementation"], row["command"])].append(row) -results = [] -for (implementation, command), rows in groups.items(): - metrics = { - field: distribution([float(row[field]) for row in rows]) - for field in rows[0] - if field not in ("implementation", "command", "sample") - } - cpu = [float(row["user_us"]) + float(row["system_us"]) for row in rows] - metrics["total_cpu_us"] = distribution(cpu) - # Ratio of totals avoids averaging unstable percentages for short processes. - cpu_percent = 100 * sum(cpu) / sum(float(row["wall_us"]) for row in rows) - results.append({ - "implementation": implementation, "command": command, - "samples": len(rows), "cpu_percent": cpu_percent, "metrics": metrics, - }) -print(json.dumps(results, indent=2)) +json.dump({ + "command": "listenbox import --slug https://www.youtube.com/watch?v=w", + "warmups_per_build": 4, + "samples_per_build": 20, + "statistic": "median for each metric; CPU time is user + system; CPU utilization is 100 * CPU / wall per sample", + "summary": summary, + "binaries": binaries, + "samples": samples, +}, sys.stdout, indent=2) +print() diff --git a/moon.yml b/moon.yml index 8040c09..18bb059 100644 --- a/moon.yml +++ b/moon.yml @@ -15,6 +15,16 @@ fileGroups: - .cargo/config.toml - moon.yml tasks: + benchmark-measurer: + description: Build the Linux child-process accounting wrapper for API import benchmarks. + script: |- + set -euo pipefail + mkdir -p .cache + cc -O2 -Wall -Wextra -Werror bench/measure.c -o .cache/cli-measure + inputs: [bench/measure.c] + outputs: [.cache/cli-measure] + options: + runInCI: false native-ffmpeg: description: Build pinned FFmpeg 9 libraries for static linking through ffmpeg-the-third. inputs: From 637e7c28b36fe1fb7263726987e72f4caf7508ab Mon Sep 17 00:00:00 2001 From: adelnizamutdinov Date: Thu, 24 Sep 2026 23:00:24 +0000 Subject: [PATCH 5/6] Use shared youtubei Git dependency and remove JavaScript tooling --- .github/workflows/ci.yaml | 10 +- .gitignore | 2 +- Cargo.lock | 139 ++++++- Cargo.toml | 5 +- README.md | 19 +- THIRD-PARTY-NOTICES.txt | 298 ++++++++++++++- bench/README.md | 3 + js/youtube.ts | 211 ----------- moon.yml | 23 +- nub.lock | 770 -------------------------------------- package.json | 14 - prepare-ffmpeg.py | 47 +++ prepare-ffmpeg.ts | 74 ---- src/innertube.rs | 304 ++++++++++----- src/youtube.rs | 32 +- tsconfig.json | 13 - 16 files changed, 718 insertions(+), 1246 deletions(-) delete mode 100644 js/youtube.ts delete mode 100644 nub.lock delete mode 100644 package.json create mode 100644 prepare-ffmpeg.py delete mode 100644 prepare-ffmpeg.ts delete mode 100644 tsconfig.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d5d2bc9..9b8ae18 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,13 +13,6 @@ jobs: with: fetch-depth: 0 filter: blob:none - - uses: actions/setup-node@v6 - with: - node-version: "26" - - uses: nubjs/setup-nub@v0 - with: - nub-version: ^0.9.0 - cache: true - uses: pkgxdev/setup@v5 - uses: moonrepo/setup-toolchain@v0 with: @@ -33,8 +26,7 @@ jobs: target .cache/ffmpeg ~/.pkgx - key: rust-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml', 'prepare-ffmpeg.ts') }} - - run: nub i --frozen-lockfile + key: rust-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock', 'rust-toolchain.toml', 'prepare-ffmpeg.py') }} - name: Check affected tasks id: ci run: moon ci diff --git a/.gitignore b/.gitignore index a034d98..341f543 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target/ /.cache/ /dist/ -/node_modules/ /.moon/cache/ +__pycache__/ diff --git a/Cargo.lock b/Cargo.lock index 323304c..bfc335f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,6 +875,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + [[package]] name = "find-msvc-tools" version = "0.1.13" @@ -930,6 +940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1558,17 +1569,13 @@ name = "listenbox" version = "0.1.0" dependencies = [ "anyhow", - "base64", "chrono", "clap", "ffmpeg-the-third", "futures-util", "hex", "image", - "llrt_modules", - "llrt_utils_watts", "reqwest", - "rquickjs", "serde", "serde_json", "serde_yaml", @@ -1578,6 +1585,7 @@ dependencies = [ "tokio-util", "url", "uuid", + "youtubei", ] [[package]] @@ -1693,7 +1701,7 @@ dependencies = [ "rand", "rquickjs", "rsa", - "sha1", + "sha1 0.11.0", "spki", "x25519-dalek", ] @@ -2313,7 +2321,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit", + "toml_edit 0.25.15+spec-1.1.0", ] [[package]] @@ -2504,6 +2512,7 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64", "bytes", + "futures-channel", "futures-core", "futures-util", "http", @@ -2819,6 +2828,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2854,6 +2872,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + [[package]] name = "sha1" version = "0.11.0" @@ -3041,6 +3070,17 @@ dependencies = [ "syn 3.0.6", ] +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -3142,6 +3182,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -3151,6 +3212,20 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + [[package]] name = "toml_edit" version = "0.25.15+spec-1.1.0" @@ -3158,9 +3233,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614" dependencies = [ "indexmap", - "toml_datetime", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.4", ] [[package]] @@ -3169,9 +3244,15 @@ version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow", + "winnow 1.0.4", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower" version = "0.5.3" @@ -3622,6 +3703,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "1.0.4" @@ -3660,6 +3750,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + [[package]] name = "yoke" version = "0.8.3" @@ -3683,6 +3783,27 @@ dependencies = [ "synstructure", ] +[[package]] +name = "youtubei" +version = "0.1.0" +source = "git+https://github.com/listenbox/youtubei.git?rev=a394160a92d3809bde4d4376d480373bc44ae82e#a394160a92d3809bde4d4376d480373bc44ae82e" +dependencies = [ + "flate2", + "futures-util", + "llrt_modules", + "llrt_utils_watts", + "reqwest", + "rquickjs", + "serde", + "serde_json", + "sha1 0.10.7", + "sha2 0.10.9", + "tar", + "tokio", + "toml", + "uuid", +] + [[package]] name = "zerofrom" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index b767a6c..919d70a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,7 @@ publish = false [dependencies] anyhow = "1" -base64 = "0.22" ffmpeg = { package = "ffmpeg-the-third", version = "=6.0.0", default-features = false, features = ["static", "format", "software-resampling"] } -llrt_modules = { version = "=0.8.1-beta", default-features = false, features = ["abort", "buffer", "crypto", "crypto-rust", "events", "exceptions", "fetch", "stream-web", "timers", "url", "util", "path", "intl", "tls-ring"] } -llrt_utils = { package = "llrt_utils_watts", version = "=0.8.1-beta" } -rquickjs = { version = "=0.11.0", features = ["full-async"] } chrono = { version = "0.4", default-features = false, features = ["std"] } clap = { version = "4", features = ["derive"] } futures-util = "0.3" @@ -29,6 +25,7 @@ tokio = { version = "1", features = ["rt", "macros", "signal", "process", "fs", tokio-util = { version = "0.7", features = ["io", "rt"] } url = "2" uuid = { version = "1", features = ["v4"] } +youtubei = { git = "https://github.com/listenbox/youtubei.git", rev = "a394160a92d3809bde4d4376d480373bc44ae82e" } [profile.release] strip = true diff --git a/README.md b/README.md index f314d22..19d94da 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,12 @@ Publish podcasts and manage Listenbox shows, episodes, and members from your ter ## Build and install -Install Rust (rustup), Node.js 26, [Nub](https://nubjs.com), [Moon](https://moonrepo.dev), +Install Rust (rustup), Python 3.12+, [Moon](https://moonrepo.dev), [pkgx](https://pkgx.sh), a C compiler, make, and tar. Rust is pinned by `rust-toolchain.toml`. ```sh git clone https://github.com/listenbox/cli.git cd cli -nub i --frozen-lockfile moon run cli:package mkdir -p "$HOME/.local/bin" cp dist/release/listenbox "$HOME/.local/bin/listenbox" @@ -18,13 +17,17 @@ cp dist/release/listenbox "$HOME/.local/bin/listenbox" Put `$HOME/.local/bin` on your PATH. `dist/listenbox.tar.gz` contains the release executable and license notices. The executable embeds YouTube.js in QuickJS and -links FFmpeg libraries through `ffmpeg-the-third`. Node and Nub are build tools. The installed CLI needs neither Node nor -FFmpeg/FFprobe executables. +links FFmpeg libraries through `ffmpeg-the-third`. The installed CLI is self-contained. FFmpeg 9.0.2 is built from a checksum-verified source archive with the codecs needed -for AAC audio and AVC video packages. See `prepare-ffmpeg.ts` for its configuration -and the packaged `FFmpeg-NOTICE.txt` for source and LGPL terms. The JavaScript -bundle is built with esbuild. No Bun runtime or APIs are used. +for AAC audio and AVC video packages. See `prepare-ffmpeg.py` for its configuration +and the packaged `FFmpeg-NOTICE.txt` for source and LGPL terms. + +YouTube extraction uses the typed Rust bindings in +[listenbox/youtubei](https://github.com/listenbox/youtubei), pinned by commit in +Cargo.toml and Cargo.lock. That crate downloads and verifies the published upstream CF-worker bundle during +Cargo builds. No JavaScript toolchain is needed. Playlist policy, media +selection, HTTP cancellation and upload cleanup remain in Rust here. ## Use @@ -78,6 +81,6 @@ used in E2E tests. Tests supply an isolated YAML config with their dynamic servi origins and `print_trace_ids: true`; no test-only executable or Cargo feature is required. -The [YouTube import benchmark](bench/README.md) compares this implementation +The historical [YouTube import benchmark](bench/README.md) compares the earlier Rust bridge against Go with statically linked go-astiav and kkdai/youtube, measuring the full import's wall time, CPU time, CPU utilization, peak RAM, and binary size. diff --git a/THIRD-PARTY-NOTICES.txt b/THIRD-PARTY-NOTICES.txt index 26ae6fe..fefd925 100644 --- a/THIRD-PARTY-NOTICES.txt +++ b/THIRD-PARTY-NOTICES.txt @@ -1,4 +1,4 @@ -QuickJS bridge and AAC preparation adapted from Mazit (https://github.com/meoyawn/mazit). +AAC preparation adapted from Mazit (https://github.com/meoyawn/mazit). MIT License @@ -21,3 +21,299 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Embedded upstream dependencies from listenbox/youtubei: + +@bufbuild/protobuf@2.15.0 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021-2026 Buf Technologies, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Copyright 2008 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. + + +fflate@0.8.3 +MIT License + +Copyright (c) 2026 Arjun Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +meriyah@7.3.3 +ISC License + +Copyright (c) 2019 and later, KFlash and others. + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +youtubei.js@18.1.0 +MIT License + +Copyright (c) 2021 LuanRT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/bench/README.md b/bench/README.md index 5dd7eff..70b3a61 100644 --- a/bench/README.md +++ b/bench/README.md @@ -1,5 +1,8 @@ # YouTube import benchmark +These are historical results for the commits identified below. They predate the +shared `listenbox/youtubei` crate and do not measure the current implementation. + One command, from process launch through successful publication: ```sh diff --git a/js/youtube.ts b/js/youtube.ts deleted file mode 100644 index ef99cf7..0000000 --- a/js/youtube.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { - Constants, - Innertube, - Log, - Platform, - Player, - YTNodes, -} from "youtubei.js" -import type { Misc } from "youtubei.js" - -declare function hostFetch(request: string): Promise -declare const Buffer: { - from( - value: string | ArrayBuffer, - encoding?: string, - ): { toString(encoding: string): string } -} - -async function fetchThroughRust(input: RequestInfo | URL, init?: RequestInit) { - const request = new Request(input, init) - const body = - request.method === "GET" || request.method === "HEAD" - ? null - : Buffer.from(await request.arrayBuffer()).toString("base64") - const result: { - error?: string - body: string - status: number - headers: Record - } = JSON.parse( - await hostFetch( - JSON.stringify({ - url: request.url, - method: request.method, - headers: [...request.headers], - body, - }), - ), - ) - if (result.error) throw new Error(result.error) - return new Response( - Buffer.from(result.body, "base64") as unknown as BodyInit, - { - status: result.status, - headers: result.headers, - }, - ) -} - -Platform.load({ - runtime: "unknown", - server: true, - fetch: fetchThroughRust, - Request, - Response, - Headers, - FormData, - File, - ReadableStream, - CustomEvent, - uuidv4: () => crypto.randomUUID(), - sha1Hash: async (text: string) => - Buffer.from( - await crypto.subtle.digest("SHA-1", new TextEncoder().encode(text)), - ).toString("hex"), - eval: async (data, env) => - new Function(...Object.keys(env), data.output)(...Object.values(env)), -} as Parameters[0]) -Log.setLevel(Log.Level.NONE) - -let client: Promise | undefined -const cache = new Map() - -function session() { - client ??= Innertube.create({ - lang: "en", - location: "US", - retrieve_player: false, - generate_session_locally: true, - cache: { - cache_dir: "", - get: async key => cache.get(key), - set: async (key, value) => { - cache.set(key, value) - }, - remove: async key => { - cache.delete(key) - }, - }, - }) - return client -} - -async function stream(yt: Innertube, format: Misc.Format, cpn: string) { - if ( - format.cipher || - format.signature_cipher || - (format.url && new URL(format.url).searchParams.has("n")) - ) { - yt.session.player ??= await Player.create( - yt.session.cache, - fetchThroughRust, - ) - } - const url = new URL(await format.decipher(yt.session.player)) - url.searchParams.set("cpn", cpn) - return { - url: url.toString(), - user_agent: Constants.CLIENTS.VISIONOS.USER_AGENT, - } -} - -export async function call(method: string, json: string): Promise { - try { - const args: { id: string } = JSON.parse(json) - const yt = await session() - if (method === "playlist") { - let page = await yt.getPlaylist(args.id) - const title = page.info.title - const videos: string[] = [] - const seen = new Set() - const pages = new Set() - for (;;) { - const ids: string[] = [] - for (const item of page.items) { - let id: string - if (item.is(YTNodes.PlaylistVideo)) { - if ( - !item.is_playable || - item.is_live || - item.is_upcoming || - item.upcoming - ) - continue - id = item.id - } else if ( - item.is(YTNodes.LockupView) && - ["VIDEO", "SHORT"].includes(item.content_type) - ) { - if (!item.metadata?.title.toString()) continue - id = item.content_id - } else { - throw new Error("Unsupported playlist item; listing is incomplete") - } - ids.push(id) - if (!seen.has(id)) { - seen.add(id) - videos.push(id) - } - } - const fingerprint = ids.join(",") - if (pages.has(fingerprint) || pages.size >= 10000) - throw new Error("Repeated YouTube playlist page") - pages.add(fingerprint) - if (!page.has_continuation) break - page = await page.getContinuation() - } - if (!videos.length) - throw new Error("YouTube playlist has no public videos") - return JSON.stringify({ title, videos }) - } - if (method !== "media") throw new Error("Unknown YouTube operation") - const info = await yt.getBasicInfo(args.id, { client: "VISIONOS" }) - if ( - info.playability_status?.status !== "OK" || - info.basic_info.is_live || - info.basic_info.is_upcoming - ) { - throw new Error( - `YouTube playback unavailable: ${info.playability_status?.reason ?? "not a public recorded video"}`, - ) - } - const formats = [ - ...(info.streaming_data?.formats ?? []), - ...(info.streaming_data?.adaptive_formats ?? []), - ].filter( - format => - !format.drm_families?.length && - !format.is_type_otf && - !!(format.url || format.cipher || format.signature_cipher), - ) - const video = formats - .filter( - format => - format.has_video && - format.mime_type.includes("avc1") && - (format.height ?? 0) > 0 && - format.height! <= 1080, - ) - .sort((left, right) => right.height! - left.height!)[0] - if (!video) - throw new Error("YouTube video has no AVC rendition at or below 1080p") - const audio = video.has_audio - ? undefined - : formats - .filter(format => format.has_audio && !format.has_video) - .sort((left, right) => right.bitrate - left.bitrate)[0] - if (!video.has_audio && !audio) - throw new Error("YouTube video has no audio stream") - return JSON.stringify({ - title: info.basic_info.title ?? args.id, - description: info.basic_info.short_description ?? "", - video: await stream(yt, video, info.cpn), - audio: audio ? await stream(yt, audio, info.cpn) : null, - }) - } catch (error) { - return JSON.stringify({ - bridge_error: error instanceof Error ? error.message : String(error), - }) - } -} diff --git a/moon.yml b/moon.yml index 18bb059..567f704 100644 --- a/moon.yml +++ b/moon.yml @@ -7,11 +7,7 @@ fileGroups: - Cargo.lock - rust-toolchain.toml - src/**/*.rs - - prepare-ffmpeg.ts - - js/**/*.ts - - tsconfig.json - - package.json - - nub.lock + - prepare-ffmpeg.py - .cargo/config.toml - moon.yml tasks: @@ -28,26 +24,15 @@ tasks: native-ffmpeg: description: Build pinned FFmpeg 9 libraries for static linking through ffmpeg-the-third. inputs: - - prepare-ffmpeg.ts - command: node prepare-ffmpeg.ts + - prepare-ffmpeg.py + command: python3 prepare-ffmpeg.py outputs: - .cache/ffmpeg/**/* options: cache: true - bundle-youtube: - inputs: - - js/**/*.ts - - package.json - - nub.lock - command: nubx esbuild js/youtube.ts --bundle --platform=browser --format=esm --keep-names --outfile=.cache/youtube.js - outputs: - - .cache/youtube.js - options: - cache: true prerequisites: deps: - ~:native-ffmpeg - - ~:bundle-youtube inputs: [] options: internal: true @@ -72,8 +57,6 @@ tasks: - "@group(sources)" script: |- set -euo pipefail - nubx oxfmt js/ prepare-ffmpeg.ts tsconfig.json package.json - nubx tsc -p tsconfig.json cargo fmt --all pkgx +llvm.org +cmake.org cargo clippy --locked --all-targets -- -D warnings test: diff --git a/nub.lock b/nub.lock deleted file mode 100644 index 9ec9c7a..0000000 --- a/nub.lock +++ /dev/null @@ -1,770 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - youtubei.js: - specifier: 18.0.0 - version: 18.0.0 - devDependencies: - '@types/node': - specifier: ^26.6.2 - version: 26.6.2 - esbuild: - specifier: 0.28.1 - version: 0.28.1 - oxfmt: - specifier: ^0.68.0 - version: 0.68.0 - typescript: - specifier: 7.0.2 - version: 7.0.2 - -packages: - - '@bufbuild/protobuf@2.15.0': - resolution: {integrity: sha512-DAheWUkVr/SJTWCc+lg9dhY0eN4SaWlf4+bG1KzHeXbnqt0AfB/NX0Z+VunGlM1ki1B4zVvye27MpKh/svySUA==} - - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@oxfmt/binding-android-arm-eabi@0.68.0': - resolution: {integrity: sha512-dhfYPbzv/h9JgHjNkl2R6sOjUfxDyLGOZVb3g8/ScaTNwwJcYgmHh8kcYFDUhinuy1QAoANCWUvw1jlk+z6gAg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - - '@oxfmt/binding-android-arm64@0.68.0': - resolution: {integrity: sha512-v3Njdi6qY0O/5eGfg01ww2w6gTn2mUvZ72Bnx1/UN53A9wruh3Nk6otc3WkgJLkXD4Qgz1SOcVQieH1oD03V9Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@oxfmt/binding-darwin-arm64@0.68.0': - resolution: {integrity: sha512-ei4MCMzHFREmZwPJ7KuWUB4kBuHdsgDrnXGJVcEAopU7fj7S42I8BChdFILWdHvhFqR08FLJtOfbZIr2CDw0cA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@oxfmt/binding-darwin-x64@0.68.0': - resolution: {integrity: sha512-UrKgzZxYhwB9DSvTX+vdgl9M32wLUNKJcAKIoiyx/Kzn/zveqi7W6kYVcRroFMhS3Kwz0KhTk3WBeSuQn4YCTg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@oxfmt/binding-freebsd-x64@0.68.0': - resolution: {integrity: sha512-6jrEKgpJbilM1QaRv7hEtKXr4p4AK4jvvyOtajwyhu0kOz3e0O7OLnSTk6tBotRqCcUC4ehZRJ1Zx+Y99wieLw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': - resolution: {integrity: sha512-YOIVnKOBaLeGullskS179N12hjSAdFYnzLjOaKiLhAKNWgnShq9w4xRdtmUm6BlnP65l2/EA9Aw/KlftNxDM7Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxfmt/binding-linux-arm-musleabihf@0.68.0': - resolution: {integrity: sha512-xW5XoEHVNqydPBv2KXvk9lmEzyAlOQHVEazKoXUuAacqekjya+OdiaFjjEBl0oJD02raG8g3TRl9OVCh9PDIHA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxfmt/binding-linux-arm64-gnu@0.68.0': - resolution: {integrity: sha512-QCvYwVVQieu6oyJglAgV9vH/YMDxZyR4cwVSYtoq9oOXd5N+D3TDUBjNwxFrLn5AdcJZOcvn/7IB17vJGp+2Og==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@oxfmt/binding-linux-arm64-musl@0.68.0': - resolution: {integrity: sha512-4TVz5iFQ8ndrHnhX50UXiz9BIWAtUSOHJ6Nus4qWFfJBXq/Ed/krXbF/ehJu42BXM5tIvBs99jNIM22s9agY5A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@oxfmt/binding-linux-ppc64-gnu@0.68.0': - resolution: {integrity: sha512-qLe3ao0RP84bnPxBvRI+GnlK/jybo538NWu0Xrm+zYeTmJtpzqLhnnd5BH21NafqKnplbGZjtN1cnrOlWF73lw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@oxfmt/binding-linux-riscv64-gnu@0.68.0': - resolution: {integrity: sha512-Yvyl7a6gbb0vM6r925KW2dO+/CmXySO5TVbcX7o/uZJ+d108HFOG0TxIyHApmn5USuj5mhDPxzhiVwOlGP7uSA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@oxfmt/binding-linux-riscv64-musl@0.68.0': - resolution: {integrity: sha512-mJlFuFVCxzrYM5sFStN433D/s/mb6Wq4aCQAM02vs/OudHywnaSAd2rb1vlYUJtqdYIciJtiasuxvfbYkv5fLg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@oxfmt/binding-linux-s390x-gnu@0.68.0': - resolution: {integrity: sha512-RlfSg++qs1hbKltRR6lYvV9EoI3MdlfSQD9w1hdHVYjHqjIn1tkH4FWOpMSmjKGN20zr+nI+W9o4ARogCDudGQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@oxfmt/binding-linux-x64-gnu@0.68.0': - resolution: {integrity: sha512-nyzRB9U+dlYUKu3pMo3afHzZBUv/oTHZMG36ZfJViNVfOIzp70Q4GS8FFRGgYJ/p0zcyDCgpBvYISOdJOMh+jQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@oxfmt/binding-linux-x64-musl@0.68.0': - resolution: {integrity: sha512-iCx3sbZRIvGrL1RafphEiUKBaW1lc0/tAjKOIB/Wjw2+STRBEdu5+fH1Gc1faEWEmc2k5Ks4iUUV54Zd5C9a1A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@oxfmt/binding-openharmony-arm64@0.68.0': - resolution: {integrity: sha512-x2X5AZez7OgyLLFpwIgItoXBUqudDM7yiaTsxv8R8vKQ6e81l0jVw0NFeUCXzcl1sAJq8h+tC8N4mY8EiMeL4w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@oxfmt/binding-win32-arm64-msvc@0.68.0': - resolution: {integrity: sha512-AHVPjXkenLPQUh6kB8zSC8pX2ct9r4T1Edk9r/RNJyov6wPsS5uAfYtipwG4chn6+3bPFG5rI/3DxEeH8vib1w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@oxfmt/binding-win32-ia32-msvc@0.68.0': - resolution: {integrity: sha512-n09SjEk5VH7z8Hl4WVP7hho+cCwGViENkQFiM45vbW85dJd7kEhWaHRUobaPzEmrWyu6uumd4EuNfNyDKLtzDA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@oxfmt/binding-win32-x64-msvc@0.68.0': - resolution: {integrity: sha512-gPe+dJLXaPuWPWqlpklDAJp0k+K9KhQPYiQLHfb+i2rmFuUGfJ/5Qlj6tr1mO6of5g0DiLjG/XCFHIaPhotqqA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@types/node@26.6.2': - resolution: {integrity: sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==} - - '@typescript/typescript-aix-ppc64@7.0.2': - resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} - engines: {node: '>=16.20.0'} - cpu: [ppc64] - os: [aix] - - '@typescript/typescript-darwin-arm64@7.0.2': - resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [darwin] - - '@typescript/typescript-darwin-x64@7.0.2': - resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [darwin] - - '@typescript/typescript-freebsd-arm64@7.0.2': - resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [freebsd] - - '@typescript/typescript-freebsd-x64@7.0.2': - resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [freebsd] - - '@typescript/typescript-linux-arm64@7.0.2': - resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [linux] - - '@typescript/typescript-linux-arm@7.0.2': - resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} - engines: {node: '>=16.20.0'} - cpu: [arm] - os: [linux] - - '@typescript/typescript-linux-loong64@7.0.2': - resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} - engines: {node: '>=16.20.0'} - cpu: [loong64] - os: [linux] - - '@typescript/typescript-linux-mips64el@7.0.2': - resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} - engines: {node: '>=16.20.0'} - cpu: [mips64el] - os: [linux] - - '@typescript/typescript-linux-ppc64@7.0.2': - resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} - engines: {node: '>=16.20.0'} - cpu: [ppc64] - os: [linux] - - '@typescript/typescript-linux-riscv64@7.0.2': - resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} - engines: {node: '>=16.20.0'} - cpu: [riscv64] - os: [linux] - - '@typescript/typescript-linux-s390x@7.0.2': - resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} - engines: {node: '>=16.20.0'} - cpu: [s390x] - os: [linux] - - '@typescript/typescript-linux-x64@7.0.2': - resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [linux] - - '@typescript/typescript-netbsd-arm64@7.0.2': - resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [netbsd] - - '@typescript/typescript-netbsd-x64@7.0.2': - resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [netbsd] - - '@typescript/typescript-openbsd-arm64@7.0.2': - resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [openbsd] - - '@typescript/typescript-openbsd-x64@7.0.2': - resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [openbsd] - - '@typescript/typescript-sunos-x64@7.0.2': - resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [sunos] - - '@typescript/typescript-win32-arm64@7.0.2': - resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} - engines: {node: '>=16.20.0'} - cpu: [arm64] - os: [win32] - - '@typescript/typescript-win32-x64@7.0.2': - resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} - engines: {node: '>=16.20.0'} - cpu: [x64] - os: [win32] - - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - pnpapi: '*' - peerDependenciesMeta: - pnpapi: - optional: true - - fflate@0.8.3: - resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} - - meriyah@7.3.3: - resolution: {integrity: sha512-uE5cnoNj+UYhoMdZDuymCzr5TzEuE3ZF2C4kn6Z76rLhkwnvZ/+6dUZnM41T3fifyuhW0+n8vK7eq6DKJxUfPA==} - engines: {node: '>=20.0.0'} - - oxfmt@0.68.0: - resolution: {integrity: sha512-Z0XMofcXCGUXbcpBHnWyUiX93BGiw1B+lcHNbQDWEtOhX06ewoFfu4zXkyiLhRrNnMq0twqXRHUcJetf+GsiQQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - svelte: ^5.0.0 - vite-plus: '*' - peerDependenciesMeta: - svelte: - optional: true - vite-plus: - optional: true - - tinypool@2.1.2: - resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} - engines: {node: ^20.0.0 || >=22.0.0} - - typescript@7.0.2: - resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} - engines: {node: '>=16.20.0'} - hasBin: true - - undici-types@8.9.0: - resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - - youtubei.js@18.0.0: - resolution: {integrity: sha512-7Tztl5QzNL4nIhiAxa4P/0C8cDDsUe0j4iaUeNiaA+ZfSe/l+HkHxXyoWlQXNW7IPi2n6AW23ysVMJLDh+AwWQ==} - -snapshots: - - '@bufbuild/protobuf@2.15.0': {} - - '@esbuild/aix-ppc64@0.28.1': - optional: true - - '@esbuild/android-arm64@0.28.1': - optional: true - - '@esbuild/android-arm@0.28.1': - optional: true - - '@esbuild/android-x64@0.28.1': - optional: true - - '@esbuild/darwin-arm64@0.28.1': - optional: true - - '@esbuild/darwin-x64@0.28.1': - optional: true - - '@esbuild/freebsd-arm64@0.28.1': - optional: true - - '@esbuild/freebsd-x64@0.28.1': - optional: true - - '@esbuild/linux-arm64@0.28.1': - optional: true - - '@esbuild/linux-arm@0.28.1': - optional: true - - '@esbuild/linux-ia32@0.28.1': - optional: true - - '@esbuild/linux-loong64@0.28.1': - optional: true - - '@esbuild/linux-mips64el@0.28.1': - optional: true - - '@esbuild/linux-ppc64@0.28.1': - optional: true - - '@esbuild/linux-riscv64@0.28.1': - optional: true - - '@esbuild/linux-s390x@0.28.1': - optional: true - - '@esbuild/linux-x64@0.28.1': - optional: true - - '@esbuild/netbsd-arm64@0.28.1': - optional: true - - '@esbuild/netbsd-x64@0.28.1': - optional: true - - '@esbuild/openbsd-arm64@0.28.1': - optional: true - - '@esbuild/openbsd-x64@0.28.1': - optional: true - - '@esbuild/openharmony-arm64@0.28.1': - optional: true - - '@esbuild/sunos-x64@0.28.1': - optional: true - - '@esbuild/win32-arm64@0.28.1': - optional: true - - '@esbuild/win32-ia32@0.28.1': - optional: true - - '@esbuild/win32-x64@0.28.1': - optional: true - - '@oxfmt/binding-android-arm-eabi@0.68.0': - optional: true - - '@oxfmt/binding-android-arm64@0.68.0': - optional: true - - '@oxfmt/binding-darwin-arm64@0.68.0': - optional: true - - '@oxfmt/binding-darwin-x64@0.68.0': - optional: true - - '@oxfmt/binding-freebsd-x64@0.68.0': - optional: true - - '@oxfmt/binding-linux-arm-gnueabihf@0.68.0': - optional: true - - '@oxfmt/binding-linux-arm-musleabihf@0.68.0': - optional: true - - '@oxfmt/binding-linux-arm64-gnu@0.68.0': - optional: true - - '@oxfmt/binding-linux-arm64-musl@0.68.0': - optional: true - - '@oxfmt/binding-linux-ppc64-gnu@0.68.0': - optional: true - - '@oxfmt/binding-linux-riscv64-gnu@0.68.0': - optional: true - - '@oxfmt/binding-linux-riscv64-musl@0.68.0': - optional: true - - '@oxfmt/binding-linux-s390x-gnu@0.68.0': - optional: true - - '@oxfmt/binding-linux-x64-gnu@0.68.0': - optional: true - - '@oxfmt/binding-linux-x64-musl@0.68.0': - optional: true - - '@oxfmt/binding-openharmony-arm64@0.68.0': - optional: true - - '@oxfmt/binding-win32-arm64-msvc@0.68.0': - optional: true - - '@oxfmt/binding-win32-ia32-msvc@0.68.0': - optional: true - - '@oxfmt/binding-win32-x64-msvc@0.68.0': - optional: true - - '@types/node@26.6.2': - dependencies: - undici-types: 8.9.0 - - '@typescript/typescript-aix-ppc64@7.0.2': - optional: true - - '@typescript/typescript-darwin-arm64@7.0.2': - optional: true - - '@typescript/typescript-darwin-x64@7.0.2': - optional: true - - '@typescript/typescript-freebsd-arm64@7.0.2': - optional: true - - '@typescript/typescript-freebsd-x64@7.0.2': - optional: true - - '@typescript/typescript-linux-arm64@7.0.2': - optional: true - - '@typescript/typescript-linux-arm@7.0.2': - optional: true - - '@typescript/typescript-linux-loong64@7.0.2': - optional: true - - '@typescript/typescript-linux-mips64el@7.0.2': - optional: true - - '@typescript/typescript-linux-ppc64@7.0.2': - optional: true - - '@typescript/typescript-linux-riscv64@7.0.2': - optional: true - - '@typescript/typescript-linux-s390x@7.0.2': - optional: true - - '@typescript/typescript-linux-x64@7.0.2': - optional: true - - '@typescript/typescript-netbsd-arm64@7.0.2': - optional: true - - '@typescript/typescript-netbsd-x64@7.0.2': - optional: true - - '@typescript/typescript-openbsd-arm64@7.0.2': - optional: true - - '@typescript/typescript-openbsd-x64@7.0.2': - optional: true - - '@typescript/typescript-sunos-x64@7.0.2': - optional: true - - '@typescript/typescript-win32-arm64@7.0.2': - optional: true - - '@typescript/typescript-win32-x64@7.0.2': - optional: true - - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 - - fflate@0.8.3: {} - - meriyah@7.3.3: {} - - oxfmt@0.68.0: - dependencies: - tinypool: 2.1.2 - optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.68.0 - '@oxfmt/binding-android-arm64': 0.68.0 - '@oxfmt/binding-darwin-arm64': 0.68.0 - '@oxfmt/binding-darwin-x64': 0.68.0 - '@oxfmt/binding-freebsd-x64': 0.68.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.68.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.68.0 - '@oxfmt/binding-linux-arm64-gnu': 0.68.0 - '@oxfmt/binding-linux-arm64-musl': 0.68.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.68.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.68.0 - '@oxfmt/binding-linux-riscv64-musl': 0.68.0 - '@oxfmt/binding-linux-s390x-gnu': 0.68.0 - '@oxfmt/binding-linux-x64-gnu': 0.68.0 - '@oxfmt/binding-linux-x64-musl': 0.68.0 - '@oxfmt/binding-openharmony-arm64': 0.68.0 - '@oxfmt/binding-win32-arm64-msvc': 0.68.0 - '@oxfmt/binding-win32-ia32-msvc': 0.68.0 - '@oxfmt/binding-win32-x64-msvc': 0.68.0 - - tinypool@2.1.2: {} - - typescript@7.0.2: - optionalDependencies: - '@typescript/typescript-aix-ppc64': 7.0.2 - '@typescript/typescript-darwin-arm64': 7.0.2 - '@typescript/typescript-darwin-x64': 7.0.2 - '@typescript/typescript-freebsd-arm64': 7.0.2 - '@typescript/typescript-freebsd-x64': 7.0.2 - '@typescript/typescript-linux-arm': 7.0.2 - '@typescript/typescript-linux-arm64': 7.0.2 - '@typescript/typescript-linux-loong64': 7.0.2 - '@typescript/typescript-linux-mips64el': 7.0.2 - '@typescript/typescript-linux-ppc64': 7.0.2 - '@typescript/typescript-linux-riscv64': 7.0.2 - '@typescript/typescript-linux-s390x': 7.0.2 - '@typescript/typescript-linux-x64': 7.0.2 - '@typescript/typescript-netbsd-arm64': 7.0.2 - '@typescript/typescript-netbsd-x64': 7.0.2 - '@typescript/typescript-openbsd-arm64': 7.0.2 - '@typescript/typescript-openbsd-x64': 7.0.2 - '@typescript/typescript-sunos-x64': 7.0.2 - '@typescript/typescript-win32-arm64': 7.0.2 - '@typescript/typescript-win32-x64': 7.0.2 - - undici-types@8.9.0: {} - - youtubei.js@18.0.0: - dependencies: - '@bufbuild/protobuf': 2.15.0 - fflate: 0.8.3 - meriyah: 7.3.3 diff --git a/package.json b/package.json deleted file mode 100644 index f1ead99..0000000 --- a/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "@listenbox/cli", - "private": true, - "type": "module", - "dependencies": { - "youtubei.js": "18.0.0" - }, - "devDependencies": { - "@types/node": "^26.6.2", - "esbuild": "0.28.1", - "oxfmt": "^0.68.0", - "typescript": "7.0.2" - } -} diff --git a/prepare-ffmpeg.py b/prepare-ffmpeg.py new file mode 100644 index 0000000..5bd403e --- /dev/null +++ b/prepare-ffmpeg.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Build the pinned static FFmpeg libraries with Python's standard library.""" + +import hashlib +import os +from pathlib import Path +import shutil +import subprocess +import tarfile +import urllib.request + + +cache = Path(__file__).resolve().parent / ".cache" +prefix = cache / "ffmpeg" +version = "9.0.2" +checksum = "8c3850283eb25fa026482078a04051e0be17347b09ef81a0849bec15a96e002e" +archive = cache / f"ffmpeg-{version}.tar.xz" +source = cache / f"ffmpeg-{version}" +url = f"https://ffmpeg.org/releases/ffmpeg-{version}.tar.xz" + +cache.mkdir(parents=True, exist_ok=True) +with urllib.request.urlopen(url, timeout=30) as response: + data = response.read() +if hashlib.sha256(data).hexdigest() != checksum: + raise ValueError("FFmpeg source checksum mismatch") +archive.write_bytes(data) +with tarfile.open(archive) as compressed: + compressed.extractall(cache, filter="data") +subprocess.run([ + "./configure", f"--prefix={prefix}", + "--disable-everything", "--disable-autodetect", "--disable-programs", + "--disable-doc", "--disable-debug", "--disable-network", "--disable-shared", + "--enable-static", "--enable-pic", "--disable-avdevice", "--disable-avfilter", + "--disable-swscale", "--enable-swresample", "--enable-protocol=file", + "--enable-demuxer=mov,matroska,ogg,aac", "--enable-muxer=ipod,mp4,hls,mpegts", + "--enable-decoder=aac,opus,vorbis,h264", "--enable-encoder=aac", + "--enable-parser=aac,opus,vorbis,h264", "--enable-bsf=aac_adtstoasc,h264_mp4toannexb", + "--disable-x86asm", +], cwd=source, check=True) +subprocess.run(["make", f"-j{os.cpu_count() or 1}"], cwd=source, check=True) +subprocess.run(["make", "install"], cwd=source, check=True) +shutil.copyfile(source / "COPYING.LGPLv2.1", prefix / "COPYING.LGPLv2.1") +(prefix / "NOTICE.txt").write_text( + f"FFmpeg {version}\nSource: {url}\nSHA-256: {checksum}\n" + "Configuration: see prepare-ffmpeg.py\n" + + (source / "COPYING.LGPLv2.1").read_text() +) diff --git a/prepare-ffmpeg.ts b/prepare-ffmpeg.ts deleted file mode 100644 index 45c1b57..0000000 --- a/prepare-ffmpeg.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { spawn } from "node:child_process" -import { createHash } from "node:crypto" -import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises" -import { availableParallelism } from "node:os" -import { join, resolve } from "node:path" - -const cache = resolve(import.meta.dirname, ".cache") -const prefix = join(cache, "ffmpeg") -const version = "9.0.2" -const checksum = - "8c3850283eb25fa026482078a04051e0be17347b09ef81a0849bec15a96e002e" -const archive = join(cache, `ffmpeg-${version}.tar.xz`) -const source = join(cache, `ffmpeg-${version}`) - -async function run(command: string, args: string[], cwd: string) { - await new Promise((resolve, reject) => { - const child = spawn(command, args, { cwd, stdio: "inherit" }) - child.once("error", reject) - child.once("exit", (code, signal) => { - if (code === 0) resolve() - else reject(new Error(`${command} failed: ${signal ?? code}`)) - }) - }) -} - -await mkdir(cache, { recursive: true }) -const response = await fetch( - `https://ffmpeg.org/releases/ffmpeg-${version}.tar.xz`, -) -if (!response.ok) throw new Error(`Download FFmpeg: HTTP ${response.status}`) -const bytes = Buffer.from(await response.arrayBuffer()) -if (createHash("sha256").update(bytes).digest("hex") !== checksum) { - throw new Error("FFmpeg source checksum mismatch") -} -await writeFile(archive, bytes) -await run("tar", ["-xf", archive, "-C", cache], cache) -await run( - "./configure", - [ - `--prefix=${prefix}`, - "--disable-everything", - "--disable-autodetect", - "--disable-programs", - "--disable-doc", - "--disable-debug", - "--disable-network", - "--disable-shared", - "--enable-static", - "--enable-pic", - "--disable-avdevice", - "--disable-avfilter", - "--disable-swscale", - "--enable-swresample", - "--enable-protocol=file", - "--enable-demuxer=mov,matroska,ogg,aac", - "--enable-muxer=ipod,mp4,hls,mpegts", - "--enable-decoder=aac,opus,vorbis,h264", - "--enable-encoder=aac", - "--enable-parser=aac,opus,vorbis,h264", - "--enable-bsf=aac_adtstoasc,h264_mp4toannexb", - "--disable-x86asm", - ], - source, -) -await run("make", [`-j${availableParallelism()}`], source) -await run("make", ["install"], source) -await copyFile( - join(source, "COPYING.LGPLv2.1"), - join(prefix, "COPYING.LGPLv2.1"), -) -await writeFile( - join(prefix, "NOTICE.txt"), - `FFmpeg ${version}\nSource: https://ffmpeg.org/releases/ffmpeg-${version}.tar.xz\nSHA-256: ${checksum}\nConfiguration: see apps/cli/prepare-ffmpeg.ts\n${await readFile(join(source, "COPYING.LGPLv2.1"), "utf8")}`, -) diff --git a/src/innertube.rs b/src/innertube.rs index 505d694..b090485 100644 --- a/src/innertube.rs +++ b/src/innertube.rs @@ -1,108 +1,238 @@ -//! YouTube.js runs inside QuickJS; Rust owns cancellation and all HTTP transfers. +//! Application policy over the shared typed youtubei bindings. use crate::api::{Api, read_bounded}; -use anyhow::{Context as _, Result, anyhow, bail, ensure}; -use base64::{Engine, prelude::BASE64_STANDARD}; -use llrt_modules::module_builder::ModuleBuilder; -use rquickjs::{ - AsyncContext, AsyncRuntime, Function, Module, Promise, async_with, - function::{Async, Func}, +use anyhow::{Context, Result, bail, ensure}; +use std::collections::HashSet; +use youtubei::{ + Client, Engine, EngineOptions, FetchRequest, FetchResponse, Format, GetVideoInfoOptions, + Innertube, Player, SessionOptions, UniversalCache, + models::{LockupContentType, PlaylistItem}, }; -use serde::Deserialize; -use serde_json::{Value, json}; pub struct YouTube { - context: AsyncContext, - _runtime: AsyncRuntime, + client: Innertube, +} + +pub struct Media { + pub title: String, + pub description: String, + pub video: Stream, + pub audio: Option, +} + +pub struct Stream { + pub url: String, + pub user_agent: String, } impl YouTube { pub async fn new(api: &Api) -> Result { - let runtime = AsyncRuntime::new()?; - runtime.set_memory_limit(256 * 1024 * 1024).await; - runtime.set_max_stack_size(4 * 1024 * 1024).await; + let engine = Engine::with_options(EngineOptions { + memory_limit: 256 * 1024 * 1024, + stack_size: 4 * 1024 * 1024, + }) + .await?; let cancel = api.cancel.clone(); - runtime - .set_interrupt_handler(Some(Box::new(move || cancel.is_cancelled()))) + engine + .set_interrupt_handler(move || cancel.is_cancelled()) .await; - let (resolver, loader, globals) = ModuleBuilder::default().build(); - runtime.set_loader(resolver, loader).await; - let context = AsyncContext::full(&runtime).await?; let api = api.clone(); - context - .with(|ctx| { - use llrt_utils::primordials::Primordial; - llrt_utils::primordials::BasePrimordials::init(&ctx)?; - globals.attach(&ctx)?; - ctx.globals() - .set("structuredClone", Func::from(clone_value))?; - ctx.globals().set( - "hostFetch", - Func::from(Async(move |request: String| { - let api = api.clone(); - async move { - match fetch(&api, &request).await { - Ok(value) => value.to_string(), - Err(error) => json!({"error": error.to_string()}).to_string(), + let fetch = engine + .fetch_with(move |request| { + let api = api.clone(); + async move { + fetch(&api, request) + .await + .map_err(|error| youtubei::Error::new(error.to_string())) + } + }) + .await?; + let cache = UniversalCache::new(&engine, false, None).await?; + let client = Innertube::create_in( + &engine, + SessionOptions { + lang: Some("en".into()), + location: Some("US".into()), + cache: Some(cache.as_cache()), + fetch: Some(fetch), + ..SessionOptions::local() + }, + ) + .await + .context("initialize YouTube")?; + Ok(Self { client }) + } + + pub async fn playlist(&self, api: &Api, id: &str) -> Result<(String, Vec)> { + api.wait(async { + let mut page = self.client.get_playlist(id).await?; + let mut title = None; + let mut videos = Vec::new(); + let mut seen = HashSet::new(); + let mut pages = HashSet::new(); + loop { + let data = page.data().await?; + if title.is_none() { + title = data.info.title; + } + let mut ids = Vec::new(); + for item in data.items { + let id = match item { + PlaylistItem::PlaylistVideo(video) => { + if !video.is_playable + || video.is_live + || video.is_upcoming + || video.upcoming.is_some() + { + continue; } + video.id } - })), - )?; - ctx.eval::<(), _>( - "var console = {log(){},info(){},warn(){},error(){},debug(){}};", - )?; - let module = - Module::declare(ctx.clone(), "youtube", include_str!("../.cache/youtube.js"))?; - let (module, evaluated) = module.eval()?; - evaluated.finish::<()>()?; - ctx.globals().set("YouTubeBridge", module.namespace()?) - }) - .await - .context("initialize YouTube.js")?; - Ok(Self { - context, - _runtime: runtime, + PlaylistItem::LockupView(video) + if matches!( + video.content_type, + LockupContentType::Video | LockupContentType::Short + ) => + { + if !video + .metadata + .and_then(|m| m.title) + .is_some_and(|title| !title.as_str().is_empty()) + { + continue; + } + video.content_id + } + _ => bail!("Unsupported playlist item; listing is incomplete"), + }; + ids.push(id.clone()); + if seen.insert(id.clone()) { + videos.push(id); + } + } + ensure!( + pages.len() < 10000 && pages.insert(ids), + "Repeated YouTube playlist page" + ); + if !data.has_continuation { + break; + } + page = page.get_continuation().await?; + } + ensure!(!videos.is_empty(), "YouTube playlist has no public videos"); + Ok(( + title + .filter(|title| !title.is_empty()) + .context("YouTube playlist missing title")?, + videos, + )) }) + .await } - pub async fn call(&self, api: &Api, method: &str, args: Value) -> Result { - let result = api - .wait(async { - let text = async_with!(self.context => |ctx| { - let bridge: rquickjs::Object = ctx.globals().get("YouTubeBridge")?; - let function: Function = bridge.get("call")?; - let promise: Promise = function.call((method, args.to_string()))?; - promise.into_future::().await + pub async fn media(&self, api: &Api, id: &str) -> Result { + api.wait(async { + let info = self + .client + .get_basic_info( + id, + GetVideoInfoOptions { + client: Some(Client::VisionOs), + ..Default::default() + }, + ) + .await?; + let data = info.data().await?; + ensure!( + data.playability_status + .as_ref() + .is_some_and(|s| s.status == "OK") + && !data.basic_info.is_live.unwrap_or(false) + && !data.basic_info.is_upcoming.unwrap_or(false), + "YouTube playback unavailable: {}", + data.playability_status + .and_then(|s| s.reason) + .unwrap_or_else(|| "not a public recorded video".into()) + ); + let mut formats = info.formats().await?; + formats.extend(info.adaptive_formats().await?); + formats.retain(|format| { + let f = format.info(); + f.drm_families.as_ref().is_none_or(Vec::is_empty) + && !f.is_type_otf + && (f.url.is_some() || f.cipher.is_some() || f.signature_cipher.is_some()) + }); + let video = formats + .iter() + .filter(|format| { + let f = format.info(); + f.has_video + && f.mime_type.contains("avc1") + && f.height + .is_some_and(|height| height > 0.0 && height <= 1080.0) + }) + .max_by(|a, b| { + a.info() + .height + .unwrap() + .total_cmp(&b.info().height.unwrap()) }) - .await - .map_err(|error| anyhow!("YouTube.js: {error}"))?; - Ok(serde_json::from_str::(&text)?) + .context("YouTube video has no AVC rendition at or below 1080p")?; + let audio = if video.info().has_audio { + None + } else { + Some( + formats + .iter() + .filter(|format| format.info().has_audio && !format.info().has_video) + .max_by(|a, b| a.info().bitrate.total_cmp(&b.info().bitrate)) + .context("YouTube video has no audio stream")?, + ) + }; + Ok(Media { + title: data.basic_info.title.unwrap_or_else(|| id.into()), + description: data.basic_info.short_description.unwrap_or_default(), + video: self.stream(video, &data.cpn).await?, + audio: match audio { + Some(format) => Some(self.stream(format, &data.cpn).await?), + None => None, + }, }) + }) + .await + } + + async fn stream(&self, format: &Format, cpn: &str) -> Result { + let session = self.client.session().await?; + let info = format.info(); + let needs_player = info.cipher.is_some() + || info.signature_cipher.is_some() + || info.url.as_ref().is_some_and(|url| { + url::Url::parse(url).is_ok_and(|url| url.query_pairs().any(|(key, _)| key == "n")) + }); + if needs_player && session.player().await?.is_none() { + let player = Player::create( + self.client.engine(), + session.cache().await?.as_ref(), + Some(&session.fetch().await?), + None, + None, + ) .await?; - if let Some(error) = result["bridge_error"].as_str() { - bail!("YouTube: {error}"); + session.set_player(&player).await?; } - Ok(result) + let mut url = url::Url::parse(&format.decipher(session.player().await?.as_ref()).await?)?; + url.query_pairs_mut().append_pair("cpn", cpn); + Ok(Stream { + url: url.into(), + user_agent: Client::VisionOs + .user_agent(self.client.engine()) + .await? + .context("YouTube VISIONOS user agent missing")?, + }) } } -fn clone_value<'js>( - ctx: rquickjs::Ctx<'js>, - value: rquickjs::Value<'js>, - options: rquickjs::function::Opt>, -) -> rquickjs::Result> { - llrt_utils::clone::structured_clone(&ctx, value, options) -} - -#[derive(Deserialize)] -struct Fetch { - url: String, - method: String, - headers: Vec<(String, String)>, - body: Option, -} - -async fn fetch(api: &Api, request: &str) -> Result { - let input: Fetch = serde_json::from_str(request)?; +async fn fetch(api: &Api, input: FetchRequest) -> Result { let url = url::Url::parse(&input.url)?; let host = url.host_str().unwrap_or(""); ensure!( @@ -125,7 +255,7 @@ async fn fetch(api: &Api, request: &str) -> Result { } } if let Some(body) = input.body { - request = request.body(BASE64_STANDARD.decode(body)?); + request = request.body(body); } let response = api.send(request).await?; let status = response.status().as_u16(); @@ -138,7 +268,11 @@ async fn fetch(api: &Api, request: &str) -> Result { .ok() .map(|value| (key.to_string(), value.to_owned())) }) - .collect::>(); + .collect(); let body = api.wait(read_bounded(response, 32 << 20)).await?; - Ok(json!({"status": status, "headers": headers, "body": BASE64_STANDARD.encode(body)})) + Ok(FetchResponse { + status, + headers, + body, + }) } diff --git a/src/youtube.rs b/src/youtube.rs index a393fb0..6472ef1 100644 --- a/src/youtube.rs +++ b/src/youtube.rs @@ -1,11 +1,10 @@ use crate::{ api::{Api, string}, events::Progress, - innertube::YouTube, + innertube::{Stream, YouTube}, publicapi as p, }; use anyhow::{Context, Result, ensure}; -use serde::Deserialize; use serde_json::json; use sha2::{Digest, Sha256}; use std::{collections::HashSet, path::Path, time::Duration}; @@ -21,20 +20,6 @@ pub fn is_source(source: &str) -> bool { }) } -#[derive(Deserialize)] -struct Media { - title: String, - description: String, - video: Stream, - audio: Option, -} - -#[derive(Deserialize)] -struct Stream { - url: String, - user_agent: String, -} - pub async fn import(api: &Api, source: &str, requested_slug: Option<&str>) -> Result<()> { let url = Url::parse(source)?; ensure!( @@ -48,13 +33,7 @@ pub async fn import(api: &Api, source: &str, requested_slug: Option<&str>) -> Re let (title, videos) = if let Some((_, playlist)) = url.query_pairs().find(|(key, _)| key == "list") { ensure!(valid_youtube_id(&playlist), "invalid playlist ID"); - let page = youtube - .call(api, "playlist", json!({"id": playlist})) - .await?; - ( - string(&page, "title")?.to_owned(), - serde_json::from_value::>(page["videos"].clone())?, - ) + youtube.playlist(api, &playlist).await? } else { let id = if url.host_str() == Some("youtu.be") { url.path().trim_matches('/').to_owned() @@ -71,8 +50,8 @@ pub async fn import(api: &Api, source: &str, requested_slug: Option<&str>) -> Re id.len() == 11 && valid_youtube_id(&id), "invalid YouTube video ID" ); - let media = youtube.call(api, "media", json!({"id": id})).await?; - (string(&media, "title")?.into(), vec![id]) + let media = youtube.media(api, &id).await?; + (media.title, vec![id]) }; ensure!(!videos.is_empty(), "YouTube playlist has no public videos"); let slug = requested_slug @@ -139,8 +118,7 @@ fn valid_youtube_id(id: &str) -> bool { } async fn import_video(api: &Api, youtube: &YouTube, slug: &str, id: &str) -> Result<()> { - let media: Media = - serde_json::from_value(youtube.call(api, "media", json!({"id": id})).await?)?; + let media = youtube.media(api, id).await?; let directory = tempfile::tempdir()?; download(api, &media.video, &directory.path().join("source-video")).await?; if let Some(audio) = &media.audio { diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 81f82d1..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "noEmit": true, - "lib": ["ESNext", "DOM", "DOM.Iterable"], - "types": ["node"] - }, - "include": ["js/**/*.ts", "prepare-ffmpeg.ts"] -} From fb820150c51df4313529a9f4687887a8d46f6c25 Mon Sep 17 00:00:00 2001 From: adelnizamutdinov Date: Thu, 24 Sep 2026 23:13:52 +0000 Subject: [PATCH 6/6] Rerun YouTube import benchmark with the shared youtubei crate --- bench/README.md | 29 ++- bench/results/environment.json | 9 +- bench/results/youtube-import.json | 408 +++++++++++++++--------------- 3 files changed, 226 insertions(+), 220 deletions(-) diff --git a/bench/README.md b/bench/README.md index 70b3a61..3dd67d9 100644 --- a/bench/README.md +++ b/bench/README.md @@ -1,7 +1,7 @@ # YouTube import benchmark -These are historical results for the commits identified below. They predate the -shared `listenbox/youtubei` crate and do not measure the current implementation. +Results for the Rust implementation using the shared `listenbox/youtubei` crate, +compared with the Go baseline from PR #7. Exact revisions are recorded below. One command, from process launch through successful publication: @@ -9,17 +9,18 @@ One command, from process launch through successful publication: listenbox import --slug 'https://www.youtube.com/watch?v=w' ``` -| Metric | Go | Rust | -|---|---:|---:| -| Wall time, including startup | 331 ms | 445 ms | -| CPU time (user + system) | 198 ms | 285 ms | -| CPU utilization | 58.4% | 64.7% | -| Peak RAM (RSS) | 31.2 MiB | 35.0 MiB | -| Binary size | 21.6 MiB | 19.6 MiB | +| Metric | Go | Rust | Rust vs. Go | +|---|---:|---:|---:| +| Wall time, including startup | 350 ms | 484 ms | +38.2% | +| CPU time (user + system) | 200 ms | 313 ms | +56.5% | +| CPU utilization | 56.3% | 65.6% | +9.2 pp | +| Peak RAM (RSS) | 30.1 MiB | 34.0 MiB | +12.8% | +| Binary size | 21.6 MiB | 19.9 MiB | -7.8% | Measured on 2026-09-24. Each runtime figure is the median of 20 imports after four -warmups per build. On this fixture, Rust takes longer and uses more CPU time and -RAM; its executable is smaller. +warmups per build. On this fixture, Rust takes 38.2% longer, uses 56.5% more CPU time and +12.8% more peak RAM, and has a 7.8% smaller executable. Deltas use unrounded +medians; CPU utilization is reported in percentage points (pp). ## What runs @@ -32,8 +33,10 @@ that did not download the Opus fixture. Go is [3d0f569](https://github.com/listenbox/cli/commit/3d0f56927a9fc0ae8a97d1cb64d209505ea2ae14): **go-astiav 0.43.0 + kkdai/youtube 2.10.6**, from [PR #7](https://github.com/listenbox/cli/pull/7). -Rust is [caaec94](https://github.com/listenbox/cli/commit/caaec943677ce8fc4b32713b12389213d8445b7f): -**ffmpeg-the-third 6.0.0 + YouTube.js 18.0.0 through rquickjs 0.11.0**. +Rust is [637e7c2](https://github.com/listenbox/cli/commit/637e7c28b36fe1fb7263726987e72f4caf7508ab): +**ffmpeg-the-third 6.0.0 + YouTube.js 18.1.0 through rquickjs 0.11.0**, using +[`listenbox/youtubei` at a394160](https://github.com/listenbox/youtubei/commit/a394160a92d3809bde4d4376d480373bc44ae82e) +and the published CF-worker bundle. Both are optimized, stripped release executables with statically linked FFmpeg 9.0.2. No external FFmpeg or JavaScript process runs during the import. diff --git a/bench/results/environment.json b/bench/results/environment.json index 91f5e93..b8d94df 100644 --- a/bench/results/environment.json +++ b/bench/results/environment.json @@ -11,7 +11,7 @@ "go_astiav": "v0.43.0", "go_youtube": "github.com/kkdai/youtube/v2 v2.10.6", "rust_ffmpeg": "ffmpeg-the-third 6.0.0+ffmpeg-9.0", - "rust_youtube": "youtubei.js 18.0.0 through rquickjs 0.11.0", + "rust_youtube": "youtubei.js 18.1.0 published CF-worker bundle through listenbox/youtubei and rquickjs 0.11.0", "go_flags": "CGO_ENABLED=1 go build -trimpath -ldflags=\"-s -w\"", "rust_flags": "cargo build --locked --release; opt-level=3, strip=true, lto=thin", "default_affinity": "0-7", @@ -34,7 +34,7 @@ "samples_per_build": 20, "workload": "One fresh YouTube video import: local Innertube responses, 1080p AVC + Opus download, AAC transcode, MP4/HLS packaging, upload and publication", "measurement": "CLOCK_MONOTONIC and wait4 for the CLI child only; supervisor, fixture setup and backend CPU/RAM excluded", - "fixture_workspace_commit": "674cadc3bb543a5bf41bd5b59c7d46cf606c58e4", + "fixture_workspace_commit": "cd978227eb7ad6ac9f7d01189dea8aaf7fd33dbc", "fixture_files": { "apps/fixtures/e2e/youtube/dash-range/1080/manifest-stream0.mp4": { "bytes": 1570121, @@ -44,5 +44,8 @@ "bytes": 44326, "sha256": "0a5eaec5205f2add1314c71362e1b0720a49eaec41a28a77d655dc53ff8a56ae" } - } + }, + "measurement_started_utc": "2026-09-24T23:12:03.040679982Z", + "measurement_finished_utc": "2026-09-24T23:12:33.126200161Z", + "rust_youtubei_commit": "a394160a92d3809bde4d4376d480373bc44ae82e" } diff --git a/bench/results/youtube-import.json b/bench/results/youtube-import.json index 1d6de06..ddf99ac 100644 --- a/bench/results/youtube-import.json +++ b/bench/results/youtube-import.json @@ -5,18 +5,18 @@ "statistic": "median for each metric; CPU time is user + system; CPU utilization is 100 * CPU / wall per sample", "summary": { "go": { - "wall_ms": 330.7795065, - "cpu_ms": 197.7985, - "cpu_percent": 58.422114, - "peak_rss_mib": 31.234375, + "wall_ms": 350.198122, + "cpu_ms": 199.74200000000002, + "cpu_percent": 56.3455515, + "peak_rss_mib": 30.1464845, "binary_mib": 21.590682983398438 }, "rust": { - "wall_ms": 445.3642865, - "cpu_ms": 284.9485, - "cpu_percent": 64.6710655, - "peak_rss_mib": 34.988281, - "binary_mib": 19.57439422607422 + "wall_ms": 483.86568350000005, + "cpu_ms": 312.5295, + "cpu_percent": 65.5943135, + "peak_rss_mib": 34.019531, + "binary_mib": 19.900833129882812 } }, "binaries": { @@ -26,394 +26,394 @@ "sha256": "b14d4f4fe699d2995d21f9da7211a36fab4f1a2c7c2c98c9aa7c1101ed6a9692" }, "rust": { - "commit": "caaec943677ce8fc4b32713b12389213d8445b7f", - "bytes": 20525240, - "sha256": "ffd778f8d1dc2ae07678d11ddeedcb1515c2f4f1e93e9b40c990342d135ea26b" + "commit": "637e7c28b36fe1fb7263726987e72f4caf7508ab", + "bytes": 20867536, + "sha256": "8e98348e150d69701e87db2ace3409c773b5cee997aa8bcfd27a8d4acfb8c16f" } }, "samples": [ { "build": "go", - "wall_ms": 469.51108, - "cpu_ms": 214.484, - "cpu_percent": 45.682415, - "peak_rss_mib": 31.757812, + "wall_ms": 566.890734, + "cpu_ms": 276.849, + "cpu_percent": 48.836395, + "peak_rss_mib": 30.191406, "warmup": true }, { "build": "rust", - "wall_ms": 497.52089, - "cpu_ms": 284.093, - "cpu_percent": 57.101723, - "peak_rss_mib": 35.785156, + "wall_ms": 493.682221, + "cpu_ms": 328.171, + "cpu_percent": 66.474138, + "peak_rss_mib": 33.796875, "warmup": true }, { "build": "rust", - "wall_ms": 443.780693, - "cpu_ms": 265.213, - "cpu_percent": 59.762176, - "peak_rss_mib": 34.898438, + "wall_ms": 437.512006, + "cpu_ms": 305.193, + "cpu_percent": 69.756486, + "peak_rss_mib": 34.542969, "warmup": true }, { "build": "go", - "wall_ms": 391.12498, - "cpu_ms": 200.542, - "cpu_percent": 51.273125, - "peak_rss_mib": 31.992188, + "wall_ms": 346.540331, + "cpu_ms": 174.064, + "cpu_percent": 50.229074, + "peak_rss_mib": 30.417969, "warmup": true }, { "build": "go", - "wall_ms": 396.940115, - "cpu_ms": 246.716, - "cpu_percent": 62.154464, - "peak_rss_mib": 31.167969, + "wall_ms": 397.396702, + "cpu_ms": 209.732, + "cpu_percent": 52.776482, + "peak_rss_mib": 29.664062, "warmup": true }, { "build": "rust", - "wall_ms": 467.659592, - "cpu_ms": 293.222, - "cpu_percent": 62.699879, - "peak_rss_mib": 35.269531, + "wall_ms": 493.302278, + "cpu_ms": 286.589, + "cpu_percent": 58.096022, + "peak_rss_mib": 34.328125, "warmup": true }, { "build": "rust", - "wall_ms": 482.621079, - "cpu_ms": 308.494, - "cpu_percent": 63.92054, - "peak_rss_mib": 34.6875, + "wall_ms": 478.671711, + "cpu_ms": 319.18, + "cpu_percent": 66.680356, + "peak_rss_mib": 34.394531, "warmup": true }, { "build": "go", - "wall_ms": 361.388044, - "cpu_ms": 205.567, - "cpu_percent": 56.882623, - "peak_rss_mib": 31.679688, + "wall_ms": 321.654088, + "cpu_ms": 176.034, + "cpu_percent": 54.727736, + "peak_rss_mib": 29.773438, "warmup": true }, { "build": "go", - "wall_ms": 321.53827, - "cpu_ms": 206.865, - "cpu_percent": 64.336043, - "peak_rss_mib": 31.113281, + "wall_ms": 374.371557, + "cpu_ms": 210.866, + "cpu_percent": 56.325326, + "peak_rss_mib": 30.78125, "warmup": false }, { "build": "rust", - "wall_ms": 509.050381, - "cpu_ms": 309.291, - "cpu_percent": 60.758426, - "peak_rss_mib": 34.449219, + "wall_ms": 472.360875, + "cpu_ms": 315.546, + "cpu_percent": 66.801892, + "peak_rss_mib": 33.960938, "warmup": false }, { "build": "rust", - "wall_ms": 442.303216, - "cpu_ms": 285.869, - "cpu_percent": 64.631906, - "peak_rss_mib": 35.421875, + "wall_ms": 509.836489, + "cpu_ms": 324.802, + "cpu_percent": 63.707092, + "peak_rss_mib": 34.503906, "warmup": false }, { "build": "go", - "wall_ms": 373.956676, - "cpu_ms": 196.051, - "cpu_percent": 52.426126, - "peak_rss_mib": 31.703125, + "wall_ms": 421.890348, + "cpu_ms": 195.042, + "cpu_percent": 46.230496, + "peak_rss_mib": 29.558594, "warmup": false }, { "build": "go", - "wall_ms": 299.360155, - "cpu_ms": 173.864, - "cpu_percent": 58.078538, - "peak_rss_mib": 30.097656, + "wall_ms": 364.600731, + "cpu_ms": 225.97, + "cpu_percent": 61.977385, + "peak_rss_mib": 30.898438, "warmup": false }, { "build": "rust", - "wall_ms": 425.323767, - "cpu_ms": 273.642, - "cpu_percent": 64.33734, - "peak_rss_mib": 35.460938, + "wall_ms": 487.754444, + "cpu_ms": 294.835, + "cpu_percent": 60.447425, + "peak_rss_mib": 34.277344, "warmup": false }, { "build": "rust", - "wall_ms": 450.82256, - "cpu_ms": 297.899, - "cpu_percent": 66.078991, - "peak_rss_mib": 35.863281, + "wall_ms": 474.217802, + "cpu_ms": 282.084, + "cpu_percent": 59.48406, + "peak_rss_mib": 33.9375, "warmup": false }, { "build": "go", - "wall_ms": 335.204394, - "cpu_ms": 188.388, - "cpu_percent": 56.200934, - "peak_rss_mib": 31.828125, + "wall_ms": 347.938077, + "cpu_ms": 196.118, + "cpu_percent": 56.365777, + "peak_rss_mib": 30.503906, "warmup": false }, { "build": "go", - "wall_ms": 358.781297, - "cpu_ms": 206.916, - "cpu_percent": 57.671903, - "peak_rss_mib": 31.652344, + "wall_ms": 406.044559, + "cpu_ms": 186.727, + "cpu_percent": 45.986825, + "peak_rss_mib": 30.847656, "warmup": false }, { "build": "rust", - "wall_ms": 422.009441, - "cpu_ms": 282.698, - "cpu_percent": 66.988549, - "peak_rss_mib": 34.667969, + "wall_ms": 445.987678, + "cpu_ms": 300.962, + "cpu_percent": 67.482134, + "peak_rss_mib": 33.917969, "warmup": false }, { "build": "rust", - "wall_ms": 436.235477, - "cpu_ms": 284.028, - "cpu_percent": 65.108872, - "peak_rss_mib": 34.667969, + "wall_ms": 503.753612, + "cpu_ms": 332.076, + "cpu_percent": 65.920321, + "peak_rss_mib": 33.917969, "warmup": false }, { "build": "go", - "wall_ms": 326.354619, - "cpu_ms": 182.821, - "cpu_percent": 56.019124, - "peak_rss_mib": 31.527344, + "wall_ms": 346.803945, + "cpu_ms": 195.14, + "cpu_percent": 56.268103, + "peak_rss_mib": 29.316406, "warmup": false }, { "build": "go", - "wall_ms": 348.042703, - "cpu_ms": 197.471, - "cpu_percent": 56.737578, - "peak_rss_mib": 36.335938, + "wall_ms": 384.447966, + "cpu_ms": 203.681, + "cpu_percent": 52.980122, + "peak_rss_mib": 30.054688, "warmup": false }, { "build": "rust", - "wall_ms": 413.989104, - "cpu_ms": 278.71, - "cpu_percent": 67.323028, - "peak_rss_mib": 35.183594, + "wall_ms": 479.885352, + "cpu_ms": 316.016, + "cpu_percent": 65.852395, + "peak_rss_mib": 34.773438, "warmup": false }, { "build": "rust", - "wall_ms": 451.719557, - "cpu_ms": 302.143, - "cpu_percent": 66.887297, - "peak_rss_mib": 34.914062, + "wall_ms": 485.37252, + "cpu_ms": 294.375, + "cpu_percent": 60.649293, + "peak_rss_mib": 34.570312, "warmup": false }, { "build": "go", - "wall_ms": 305.667915, - "cpu_ms": 197.456, - "cpu_percent": 64.598209, - "peak_rss_mib": 31.332031, + "wall_ms": 355.474996, + "cpu_ms": 198.839, + "cpu_percent": 55.936142, + "peak_rss_mib": 30.371094, "warmup": false }, { "build": "go", - "wall_ms": 323.10832, - "cpu_ms": 198.126, - "cpu_percent": 61.318755, - "peak_rss_mib": 30.742188, + "wall_ms": 372.135717, + "cpu_ms": 203.533, + "cpu_percent": 54.693218, + "peak_rss_mib": 29.617188, "warmup": false }, { "build": "rust", - "wall_ms": 447.995389, - "cpu_ms": 275.119, - "cpu_percent": 61.411123, - "peak_rss_mib": 34.757812, + "wall_ms": 505.91411, + "cpu_ms": 334.422, + "cpu_percent": 66.102525, + "peak_rss_mib": 33.882812, "warmup": false }, { "build": "rust", - "wall_ms": 467.634566, - "cpu_ms": 292.664, - "cpu_percent": 62.583911, - "peak_rss_mib": 35.632812, + "wall_ms": 511.042363, + "cpu_ms": 349.241, + "cpu_percent": 68.338953, + "peak_rss_mib": 33.828125, "warmup": false }, { "build": "go", - "wall_ms": 302.156368, - "cpu_ms": 179.493, - "cpu_percent": 59.40401, - "peak_rss_mib": 30.476562, + "wall_ms": 358.865365, + "cpu_ms": 215.936, + "cpu_percent": 60.17187, + "peak_rss_mib": 29.980469, "warmup": false }, { "build": "go", - "wall_ms": 372.264437, - "cpu_ms": 220.012, - "cpu_percent": 59.100999, - "peak_rss_mib": 31.511719, + "wall_ms": 319.176533, + "cpu_ms": 183.732, + "cpu_percent": 57.564382, + "peak_rss_mib": 29.722656, "warmup": false }, { "build": "rust", - "wall_ms": 428.780041, - "cpu_ms": 279.916, - "cpu_percent": 65.281957, - "peak_rss_mib": 34.359375, + "wall_ms": 477.181471, + "cpu_ms": 304.426, + "cpu_percent": 63.796693, + "peak_rss_mib": 34.03125, "warmup": false }, { "build": "rust", - "wall_ms": 466.943997, - "cpu_ms": 302.855, - "cpu_percent": 64.858956, - "peak_rss_mib": 35.074219, + "wall_ms": 463.010398, + "cpu_ms": 327.131, + "cpu_percent": 70.653057, + "peak_rss_mib": 33.835938, "warmup": false }, { "build": "go", - "wall_ms": 324.581098, - "cpu_ms": 198.574, - "cpu_percent": 61.178547, - "peak_rss_mib": 31.121094, + "wall_ms": 339.632163, + "cpu_ms": 209.037, + "cpu_percent": 61.548058, + "peak_rss_mib": 30.859375, "warmup": false }, { "build": "go", - "wall_ms": 361.814846, - "cpu_ms": 207.118, - "cpu_percent": 57.244196, - "peak_rss_mib": 31.535156, + "wall_ms": 348.717731, + "cpu_ms": 200.645, + "cpu_percent": 57.53794, + "peak_rss_mib": 30.730469, "warmup": false }, { "build": "rust", - "wall_ms": 408.963652, - "cpu_ms": 282.212, - "cpu_percent": 69.006622, - "peak_rss_mib": 34.574219, + "wall_ms": 459.089252, + "cpu_ms": 297.49, + "cpu_percent": 64.800036, + "peak_rss_mib": 34.003906, "warmup": false }, { "build": "rust", - "wall_ms": 420.689671, - "cpu_ms": 267.222, - "cpu_percent": 63.519981, - "peak_rss_mib": 35.246094, + "wall_ms": 482.358847, + "cpu_ms": 320.036, + "cpu_percent": 66.348114, + "peak_rss_mib": 34.730469, "warmup": false }, { "build": "go", - "wall_ms": 311.846122, - "cpu_ms": 182.031, - "cpu_percent": 58.372058, - "peak_rss_mib": 30.9375, + "wall_ms": 330.77345, + "cpu_ms": 183.59, + "cpu_percent": 55.503245, + "peak_rss_mib": 30.667969, "warmup": false }, { "build": "go", - "wall_ms": 339.973373, - "cpu_ms": 217.454, - "cpu_percent": 63.962068, - "peak_rss_mib": 32.089844, + "wall_ms": 338.709059, + "cpu_ms": 203.153, + "cpu_percent": 59.978614, + "peak_rss_mib": 30.582031, "warmup": false }, { "build": "rust", - "wall_ms": 479.051414, - "cpu_ms": 289.806, - "cpu_percent": 60.495803, - "peak_rss_mib": 35.667969, + "wall_ms": 471.605094, + "cpu_ms": 308.129, + "cpu_percent": 65.336232, + "peak_rss_mib": 34.628906, "warmup": false }, { "build": "rust", - "wall_ms": 454.132249, - "cpu_ms": 293.87, - "cpu_percent": 64.710225, - "peak_rss_mib": 34.644531, + "wall_ms": 430.996455, + "cpu_ms": 300.511, + "cpu_percent": 69.724703, + "peak_rss_mib": 34.320312, "warmup": false }, { "build": "go", - "wall_ms": 335.909439, - "cpu_ms": 202.119, - "cpu_percent": 60.170682, - "peak_rss_mib": 31.09375, + "wall_ms": 337.654557, + "cpu_ms": 203.717, + "cpu_percent": 60.332963, + "peak_rss_mib": 29.914062, "warmup": false }, { "build": "go", - "wall_ms": 384.437585, - "cpu_ms": 224.789, - "cpu_percent": 58.47217, - "peak_rss_mib": 31.539062, + "wall_ms": 356.244833, + "cpu_ms": 184.089, + "cpu_percent": 51.674855, + "peak_rss_mib": 29.453125, "warmup": false }, { "build": "rust", - "wall_ms": 474.70073, - "cpu_ms": 301.669, - "cpu_percent": 63.549302, - "peak_rss_mib": 34.90625, + "wall_ms": 493.267973, + "cpu_ms": 338.054, + "cpu_percent": 68.533539, + "peak_rss_mib": 33.84375, "warmup": false }, { "build": "rust", - "wall_ms": 442.733184, - "cpu_ms": 283.348, - "cpu_percent": 63.99972, - "peak_rss_mib": 34.5, + "wall_ms": 486.189113, + "cpu_ms": 307.476, + "cpu_percent": 63.242058, + "peak_rss_mib": 34.304688, "warmup": false }, { "build": "go", - "wall_ms": 317.975517, - "cpu_ms": 179.65, - "cpu_percent": 56.49806, - "peak_rss_mib": 30.472656, + "wall_ms": 351.678513, + "cpu_ms": 183.28, + "cpu_percent": 52.11578, + "peak_rss_mib": 29.246094, "warmup": false }, { "build": "go", - "wall_ms": 314.683312, - "cpu_ms": 175.699, - "cpu_percent": 55.833593, - "peak_rss_mib": 31.136719, + "wall_ms": 305.517302, + "cpu_ms": 197.765, + "cpu_percent": 64.731195, + "peak_rss_mib": 30.238281, "warmup": false }, { "build": "rust", - "wall_ms": 430.248921, - "cpu_ms": 283.835, - "cpu_percent": 65.96995, - "peak_rss_mib": 35.0625, + "wall_ms": 486.141846, + "cpu_ms": 312.563, + "cpu_percent": 64.294609, + "peak_rss_mib": 34.878906, "warmup": false }, { "build": "rust", - "wall_ms": 482.892779, - "cpu_ms": 310.09, - "cpu_percent": 64.215083, - "peak_rss_mib": 35.386719, + "wall_ms": 490.380277, + "cpu_ms": 312.496, + "cpu_percent": 63.725238, + "peak_rss_mib": 34.007812, "warmup": false }, { "build": "go", - "wall_ms": 372.374935, - "cpu_ms": 219.402, - "cpu_percent": 58.919648, - "peak_rss_mib": 30.613281, + "wall_ms": 338.778489, + "cpu_ms": 209.768, + "cpu_percent": 61.918925, + "peak_rss_mib": 29.933594, "warmup": false } ]