option('tld') ? strtolower(ltrim((string) $this->option('tld'), '.')) : null; $shardStart = max(0, (int) $this->option('shard-start')); $allShards = (bool) $this->option('all-shards'); $shardCount = $allShards ? null : max(1, (int) $this->option('shard-count')); $withMetadata = (bool) $this->option('with-metadata'); $collection = $this->option('collection') ?: $this->service->resolveLatestCollection(); if (! $collection) { $this->error('Could not resolve a Common Crawl collection id. Pass one explicitly with --collection.'); return self::FAILURE; } $this->info("Listing CDX shards for collection {$collection}..."); $shardPaths = $this->service->listCdxShardPaths($collection); if ($shardPaths === null) { $this->error('Could not download or parse cc-index.paths.gz for this collection.'); return self::FAILURE; } $totalShards = count($shardPaths); if ($totalShards === 0) { $this->error('cc-index.paths.gz for this collection listed no CDX shards.'); return self::FAILURE; } if ($shardStart >= $totalShards) { $this->error("--shard-start ({$shardStart}) is beyond the last shard index ({$totalShards} shards total, 0-".($totalShards - 1).').'); return self::FAILURE; } $shardEnd = $allShards ? $totalShards - 1 : min($totalShards - 1, $shardStart + $shardCount - 1); $this->info("Collection has {$totalShards} CDX shard(s). Processing shard(s) {$shardStart}-{$shardEnd}".($tld ? " filtered to .{$tld}" : '').'.'); $totals = ['sitesCreated' => 0, 'sitesUpdated' => 0, 'pagesCreated' => 0, 'pagesUpdated' => 0]; $contentCache = []; for ($shardIndex = $shardStart; $shardIndex <= $shardEnd; $shardIndex++) { $path = $shardPaths[$shardIndex]; $this->info("Fetching shard {$shardIndex}/{$shardEnd}: {$path}"); $lines = $this->service->fetchShardLines($path); if ($lines === null) { $this->warn(" Failed to download or decompress shard {$shardIndex}, skipping."); continue; } if ($tld) { $prefix = $tld.','; $lines = array_values(array_filter($lines, fn (string $line) => str_starts_with($line, $prefix))); } $this->line(' '.count($lines).' matching raw record(s) in this shard.'); foreach (array_chunk($lines, self::CHUNK_SIZE) as $chunk) { $jsonLines = array_values(array_filter(array_map( fn (string $line) => $this->service->parseShardLine($line), $chunk ))); if (! empty($jsonLines)) { $this->saveCaptures($jsonLines, $collection, $withMetadata, $contentCache, $totals); } } } $this->newLine(); $this->info("Done. Processed shard(s) {$shardStart}-{$shardEnd}. Sites: {$totals['sitesCreated']} new, {$totals['sitesUpdated']} updated. Pages: {$totals['pagesCreated']} new, {$totals['pagesUpdated']} updated."); return self::SUCCESS; } }