Command line tool to fetch video IDs from niconico user pages and mylists.
Fetches video IDs from one or more nicovideo.jp/user/<id> or nicovideo.jp/mylist/<id> pages, filters them by comment count and date range, sorts the results, and prints them to stdout.
go install github.com/sh4869221b/go-nico-list@latestPrebuilt binaries are available on the GitHub Releases page.
go-nico-list [nicovideo.jp/user/<id>|nicovideo.jp/mylist/<id>...] [flags]Examples:
go-nico-list nicovideo.jp/user/12345
go-nico-list https://www.nicovideo.jp/user/12345/video --url
go-nico-list nicovideo.jp/user/1 nicovideo.jp/mylist/847130 --concurrency 10
go-nico-list --input-file users.txt
cat users.txt | go-nico-list --stdin- One video ID per line (example:
sm123). - With
--url, each line is prefixed withhttps://www.nicovideo.jp/watch/. - With
--json, stdout is a single JSON object (line output is disabled).
0: no fetch errors (invalid inputs are skipped; may produce no output).- non-zero: at least one fetch failed (any successfully retrieved IDs are still printed).
- Validation errors (for example,
--concurrency < 1) return non-zero. context.Canceled/context.DeadlineExceededduring fetch is treated as a successful empty result.
| Flag | Description | Default |
|---|---|---|
-c, --comment |
lower comment limit number | 0 |
-a, --dateafter |
date YYYYMMDD after |
10000101 |
-b, --datebefore |
date YYYYMMDD before |
99991231 |
-u, --url |
output id add url | false |
-n, --concurrency |
number of concurrent requests | 3 |
--page-concurrency |
number of concurrent page requests per target | 1 |
--http-concurrency |
maximum command-wide HTTP requests (0 means no additional maximum) | 0 |
--adaptive-http-concurrency |
adapt HTTP concurrency with an optional --http-concurrency maximum |
false |
--http-metrics |
log aggregate HTTP performance metrics | false |
--rate-limit |
maximum requests per second (0 disables) | 0 |
--min-interval |
minimum interval between requests | 0s |
--timeout |
HTTP client timeout | 10s |
--retries |
number of retries for requests | 10 |
--input-file |
read inputs from file (newline-separated) | "" |
--stdin |
read inputs from stdin (newline-separated) | false |
--logfile |
log output file path | "" |
--progress |
force enable progress output | false |
--no-progress |
disable progress output | false |
--strict |
return non-zero if any input is invalid | false |
--best-effort |
always exit 0 while logging fetch errors | false |
--dedupe |
remove duplicate output IDs before output | false |
--no-sort |
skip sorting output IDs for faster output | false |
--json |
emit JSON output to stdout | false |
Notes:
- Inputs can be provided via arguments,
--input-file, and--stdin(newline-separated). - Input lines from
--input-fileand--stdinare limited to 1 MiB per line; longer lines fail with an input read error. - Each input must contain
nicovideo.jp/user/<id>ornicovideo.jp/mylist/<id>(scheme optional). Plain digits or paths without the domain are treated as invalid inputs and skipped. - Results are written to stdout; progress and logs are written to stderr. Use
--logfileto redirect logs to a file. - Setting
concurrency,page-concurrency, orretriesto a value less than 1, ortimeoutto a value less than or equal to 0, will cause a runtime error. --dateaftermust be on or before--datebefore; inverted ranges return a validation error.- Each target is fetched without a user-configurable page or video limit until the API's natural termination condition.
- When the API reports
totalCount, page 1 defines a bounded page range and--page-concurrencycontrols concurrent requests for the remaining pages. WhentotalCountis unavailable, pages are fetched sequentially until an empty page or HTTP 404. - There are no replacement fetch limits. Large targets can therefore take longer, issue more requests, and produce more output; global rate limiting, retry handling, and context cancellation still apply.
- Responses with HTTP status other than 200/404 after retries are treated as fetch errors.
- HTTP 200 responses with
meta.status != 200are logged as warnings but still processed. --page-concurrencycontrols concurrent page requests inside each input target only when the API reportstotalCount. Without an additional HTTP cap, the maximum in-flight request count is roughly--concurrency * --page-concurrencyin that bounded-page path.- Rate limiting applies globally to all requests (including retries). HTTP 429
Retry-Afteris honored when present. Use--rate-limitor--min-intervalwith high concurrency to reduce API load. - Progress is auto-disabled when stderr is not a TTY. Use
--progressto force-enable or--no-progressto disable (takes precedence). - A run summary is printed to stderr after processing (even when the exit code is non-zero).
--strictmakes invalid inputs return a non-zero exit code while still outputting valid results.--best-effortforces exit code 0 even when fetch errors occur (errors are still logged).- Normal line output sorts IDs by numeric video ID unless
--no-sortis set. --deduperemoves duplicate video IDs before sorting/output. With--no-sort, the first occurrence that reaches the writer is kept.--no-sortis an unordered fast mode for line output: input target order, page order, and API item order are not guaranteed. Results are written as soon as target fetches finish.--jsonemits a single JSON object to stdout.--urldoes not affect JSONitems, and the summary still prints to stderr.- In JSON output,
targetsincludetype(userormylist) andid, sorted by type and numeric id in ascending order.
# Measure the existing settings without adding an HTTP cap.
go-nico-list --input-file users.txt --http-metrics
# Bound HTTP work across all targets/pages/retries and save diagnostics in the log.
go-nico-list --input-file users.txt -n 8 --page-concurrency 4 \
--http-concurrency 8 --http-metrics --logfile run.jsonl--http-concurrency 0 means no additional maximum; negative values are invalid. Without adaptive mode, zero preserves the existing uncapped behavior. A positive cap includes response body reading and closing. Retry waits release their slots, and existing rate limits still apply. Without --adaptive-http-concurrency, this is a fixed cap. It does not increase the supply of target/page workers.
--http-metrics adds one http_metrics structured log event at the end of an initialized execution. It contains attempts, dispatched retries, status/error counts, connection reuse, in-flight/reservation peaks, elapsed time and wait/network/body/decode timings. It does not change stdout, JSON results, the summary or exit status. The metrics event contains no request URLs, target IDs or response content; ordinary existing error logs are unchanged.
Timing percentiles use the most recent 1024 samples per metric, with sample counts; p95 requires at least 20 samples and p99 at least 100. Trace intervals overlap and must not be added to obtain elapsed time. See measurement definitions and reproducible local benchmarks.
go-nico-list --input-file users.txt -n 16 --page-concurrency 8 \
--adaptive-http-concurrency --http-metricsAdaptive mode works without a configured ceiling: omit --http-concurrency or set it to 0. It starts at 8 and retains a positive, automatically adjusted admission limit. A positive --http-concurrency remains an optional hard ceiling and lowers the initial limit if it is below 8. The controller grows gradually with healthy, sufficiently supplied work and decreases after sustained latency or retry pressure. It never changes target/page concurrency or your rate/min-interval settings. Low worker supply, explicit rate limits and short runs can leave the limit unchanged; automatic tuning is not a promise of faster completion.
Removing the configured ceiling adds no hidden replacement cap or memory/file-descriptor resource guard. Available workers still bound actual work; unlimited adaptive mode does not guarantee prevention of out-of-memory or file-descriptor exhaustion.
A 429 in adaptive mode pauses new requests command-wide for the applicable Retry-After/retry deadline, lets existing requests finish, and resumes with paced recovery. Fixed mode keeps the existing per-request retry policy. Output, filtering, partial results and exit behavior stay unchanged.
The controller runs even without --http-metrics. Only that diagnostics flag adds adaptive state and decision reasons to the final http_metrics event; adaptive mode alone adds no diagnostic output. State is per command, with no background probes or persisted learning. This feature is experimental and opt-in; local synthetic comparisons do not establish the best setting for the live API. See policy, evaluation and limitations.
This project separates the CLI layer from the domain logic so each part is easier to test and maintain.
main.go: resolves the version and bootstraps the CLI with a cancellation-aware context.cmd/: Cobra command definitions, flags, and input/output handling (stdout/stderr separation).internal/niconico/: core domain logic (fetching video lists, retries, sorting) and API response types.
- The CLI parses flags and user/mylist IDs.
- The command layer calls
internal/niconicoto fetch and filter video IDs from each target. - Results are sorted and printed; progress is written to stderr.
GitHub Actions runs on pull requests to master and pushes to master, and enforces:
- repository ruleset protection on
master(PR-only updates with requiredgo-cistatus checks) - generated file checks (
go mod tidy,go generate ./...,git diff --exit-code) gofmt(format + diff check)go vet ./...golangci-lint run ./...go test -count=1 ./...go test -race -count=1 ./...- GitHub Actions references are pinned to commit SHAs in workflow files.
- Integration-style command wiring tests:
cmd/root_*_test.go(httptest+ stdout/stderr/exit-code checks). - Contract tests:
internal/niconico/nico_data_contract_test.go(fixture decode frominternal/niconico/testdata/). - Fuzz tests:
internal/niconico/fuzz_test.go,cmd/root_fuzz_test.go(sorting/url-parse panic safety). - E2E tests (opt-in):
internal/niconico/e2e_test.gowith-tags=e2e. - Benchmarks (opt-in):
cmd/root_benchmark_test.go,internal/niconico/benchmark_test.go.
Opt-in commands:
go test ./internal/niconico -run TestNicoDataContract -count=1
go test ./cmd -run=^$ -fuzz=FuzzParseInputTargetNoPanic -fuzztime=10s
go test ./internal/niconico -run=^$ -fuzz=FuzzNiconicoSortNoPanic -fuzztime=10s
GO_NICO_LIST_E2E_USER_ID=<user-id> go test -tags=e2e ./internal/niconico -run TestGetVideoListE2E -count=1
go test ./cmd -run=^$ -bench='^BenchmarkHTTPCommand$' -benchmem -benchtime=10x -count=5
go test ./internal/niconico -run=^$ -bench=BenchmarkNiconicoSort -benchmem -count=1See CONTRIBUTING.md.
Releases are published by tagging a version and pushing it to GitHub.
- Create a tag like
vX.Y.Z. - Push the tag to GitHub.
- GitHub Actions runs the release workflow (verifies
go mod tidy/go generate ./...and runs gofmt/go vet/golangci-lint/go test/go test -race). - GoReleaser generates
THIRD_PARTY_NOTICES.md, publishes the GitHub Release, and uploads artifacts. - Close the milestone after the release workflow succeeds.
Notes:
- When a versioned milestone is complete, release using the same version number.
- Release tags (
vX.Y.Z) are governed by a repository tag ruleset.