Add media-streaming category with 20 P2P streaming modules.

Introduces peer-assisted live/VOD primitives (chunking, manifests, crazy trees, bandwidth aggregation, FEC, enterprise controls) with shared media-streaming-base, registry entries, and brittle tests. Four core modules ship as production tier; sixteen as scaffold for deepen passes.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-21 01:16:44 -04:00
co-authored by Cursor
parent 44b8a80907
commit a1f56dc27b
186 changed files with 39707 additions and 4 deletions
+60
View File
@@ -0,0 +1,60 @@
# Media streaming
**Path:** `modules/media-streaming/` · **Modules:** 20
Peer-assisted, bandwidth-pooled, tree/mesh-hybrid **live + VOD** streaming. A viewer maintains **one logical high-quality stream**; the overlay (crazy trees + helper swarms) amplifies bandwidth through cooperative peers.
Shared types: [`../_shared/media-streaming-base.js`](../_shared/media-streaming-base.js)
## Philosophy
| Principle | Modules |
|-----------|---------|
| One logical stream | `bandwidth-aggregator`, `helper-swarm-coordinator`, `stream-manifest` |
| Crazy trees | `media-tree-orchestrator`, `peer-selector-streaming`, `latency-optimizer` |
| Extreme quality (4K@144+) | `quality-ladder`, `chunk-scheduler-media`, `fec-video`, `adaptive-streaming-engine` |
| Enterprise | `stream-access-control`, `stream-telemetry`, `origin-hybrid-bridge`, `enterprise-orchestrator`, `content-protection` |
## Tiers
| Tier | Modules |
|------|---------|
| **production** | `media-tree-orchestrator`, `peer-selector-streaming`, `bandwidth-aggregator`, `chunk-scheduler-media` |
| **scaffold** | remaining 16 (full API + tests; deepen in later waves) |
## Composition
Typical stack:
```text
media-chunker → stream-manifest + quality-ladder
media-tree-orchestrator → peer-selector-streaming → helper-swarm-coordinator
chunk-scheduler-media → bandwidth-aggregator → buffer-health-predictor
fec-video + retransmission-media + adaptive-streaming-engine
contribution-ledger + stream-telemetry + stream-access-control
```
Registry `composes_with` links to `network-stack`, `core-infrastructure`, `scheduling-queues`, `measurement-rate-control`, and `trust-security`.
## Quick start
```js
const { HyperP2PMediaChunker } = require('hyper-p2p-media-chunker')
const { HyperP2PBandwidthAggregator } = require('hyper-p2p-bandwidth-aggregator')
const { HyperP2PMediaTreeOrchestrator } = require('hyper-p2p-media-tree-orchestrator')
const chunker = new HyperP2PMediaChunker({ chunkSize: 256 * 1024 })
const tree = new HyperP2PMediaTreeOrchestrator({ maxFanout: 8 })
const agg = new HyperP2PBandwidthAggregator({ streamId: 'live-1' })
const chunks = chunker.segment(Buffer.from('...'), { streamId: 'live-1', keyframe: true })
for (const c of chunks) agg.ingest(c.seq, 'peer-a', c.data)
const view = agg.logicalView()
```
## Test
```bash
cd hyper-p2p-bandwidth-aggregator && npm test
for d in hyper-p2p-*/; do (cd "$d" && npm test) || exit 1; done
```
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-adaptive-streaming-engine
**Protocol:** `adaptive-streaming-engine/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-adaptive-streaming-engine
**Protocol:** `adaptive-streaming-engine/v1`
**Export:** `{ HyperP2PAdaptiveStreamingEngine, PROTOCOL }`
## Overview
`HyperP2PAdaptiveStreamingEngine` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'adaptive-streaming-engine/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-adaptive-streaming-engine
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PAdaptiveStreamingEngine } = require('../index.js')
async function main () {
const m = new HyperP2PAdaptiveStreamingEngine()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,72 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const { mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'adaptive-streaming-engine/v1'
class HyperP2PAdaptiveStreamingEngine extends EventEmitter {
constructor (opts = {}) {
super()
this.targetFps = opts.targetFps ?? 144
this._bufferMs = 0
this._swarmHealth = 1
this._recommendation = { bitrate: 0, repId: 'auto', reason: 'init' }
this._stats = { reports: 0, adjustments: 0 }
}
reportBuffer (levelMs) {
this._bufferMs = Math.max(0, levelMs | 0)
this._stats.reports++
this._recompute()
return this._recommendation
}
reportSwarmHealth (score) {
this._swarmHealth = Math.min(1, Math.max(0, Number(score) || 0))
this._recompute()
return this._recommendation
}
_recompute () {
const health = this._swarmHealth
const buffer = this._bufferMs
let bitrate = 5_000_000
if (buffer < 500) bitrate = 1_000_000
else if (buffer < 2000) bitrate = 8_000_000
else bitrate = 25_000_000
bitrate = Math.floor(bitrate * health)
const repId = bitrate >= 20_000_000 ? '4k144' : bitrate >= 8_000_000 ? '4k60' : 'hd'
const prev = this._recommendation.bitrate
this._recommendation = {
bitrate,
repId,
fps: this.targetFps,
reason: buffer < 500 ? 'low-buffer' : health < 0.5 ? 'swarm-stress' : 'max-quality'
}
if (prev !== bitrate) {
this._stats.adjustments++
this.emit('recommend', this._recommendation)
}
}
recommendBitrate (swarmHealth = null) {
if (swarmHealth != null) this.reportSwarmHealth(swarmHealth)
return this._recommendation
}
currentRecommendation () {
return { ...this._recommendation, bufferMs: this._bufferMs, swarmHealth: this._swarmHealth }
}
getStats () {
return mediaStats(this._stats, PROTOCOL, { recommendation: this._recommendation })
}
async ready () { return this }
async close () {
this.emit('closed')
}
}
module.exports = { HyperP2PAdaptiveStreamingEngine, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-adaptive-streaming-engine",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — adaptive streaming engine.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,13 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PAdaptiveStreamingEngine, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PAdaptiveStreamingEngine); t.is(PROTOCOL, 'adaptive-streaming-engine/v1') })
test('recommend', async (t) => {
const m = new HyperP2PAdaptiveStreamingEngine()
m.reportBuffer(3000)
m.reportSwarmHealth(0.9)
t.ok(m.currentRecommendation().bitrate > 0)
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.3.1
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-bandwidth-aggregator
**Protocol:** `bandwidth-aggregator/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-bandwidth-aggregator
**Protocol:** `bandwidth-aggregator/v1`
**Export:** `{ HyperP2PBandwidthAggregator, PROTOCOL }`
## Overview
`HyperP2PBandwidthAggregator` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'bandwidth-aggregator/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-bandwidth-aggregator
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PBandwidthAggregator } = require('../index.js')
async function main () {
const m = new HyperP2PBandwidthAggregator()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,90 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const b4a = require('b4a')
const { assertStreamId, chunkKey, mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'bandwidth-aggregator/v1'
class HyperP2PBandwidthAggregator extends EventEmitter {
constructor (opts = {}) {
super()
this.streamId = assertStreamId(opts.streamId || 'default')
this._chunks = new Map()
this._sources = new Map()
this._nextSeq = 0
this._stats = { ingested: 0, duplicates: 0, bytes: 0 }
}
ingest (seq, peerId, data) {
const key = chunkKey(this.streamId, seq)
if (this._chunks.has(key)) {
this._stats.duplicates++
return false
}
const buf = b4a.isBuffer(data) ? data : b4a.from(data)
this._chunks.set(key, { seq, peerId, data: buf, at: Date.now() })
const src = this._sources.get(peerId) || { peerId, chunks: 0, bytes: 0 }
src.chunks++
src.bytes += buf.length
this._sources.set(peerId, src)
this._nextSeq = Math.max(this._nextSeq, seq + 1)
this._stats.ingested++
this._stats.bytes += buf.length
this.emit('ingest', { seq, peerId, bytes: buf.length })
return true
}
hasSeq (seq) {
return this._chunks.has(chunkKey(this.streamId, seq))
}
missingRanges () {
const seqs = [...this._chunks.values()].map((c) => c.seq).sort((a, b) => a - b)
if (!seqs.length) return [{ from: 0, to: 0 }]
const gaps = []
for (let i = 1; i < seqs.length; i++) {
if (seqs[i] - seqs[i - 1] > 1) {
gaps.push({ from: seqs[i - 1] + 1, to: seqs[i] - 1 })
}
}
return gaps
}
logicalView (maxSeq = null) {
const limit = maxSeq == null ? this._nextSeq : maxSeq
const parts = []
for (let s = 0; s < limit; s++) {
const c = this._chunks.get(chunkKey(this.streamId, s))
if (c) parts.push(c.data)
}
return parts.length ? b4a.concat(parts) : null
}
sourceStats () {
return [...this._sources.values()]
}
coverage () {
if (!this._nextSeq) return 0
return this._chunks.size / this._nextSeq
}
getStats () {
return mediaStats(this._stats, PROTOCOL, {
streamId: this.streamId,
chunks: this._chunks.size,
coverage: this.coverage(),
sources: this._sources.size
})
}
async ready () { return this }
async close () {
this._chunks.clear()
this._sources.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PBandwidthAggregator, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-bandwidth-aggregator",
"version": "0.3.1",
"description": "P2P media streaming — bandwidth aggregator.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,14 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PBandwidthAggregator, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PBandwidthAggregator); t.is(PROTOCOL, 'bandwidth-aggregator/v1') })
test('aggregate chunks', async (t) => {
const m = new HyperP2PBandwidthAggregator({ streamId: 's1' })
t.ok(m.ingest(0, 'p1', Buffer.from('aa')))
t.ok(m.ingest(1, 'p2', Buffer.from('bb')))
const view = m.logicalView()
t.is(view.toString(), 'aabb')
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-buffer-health-predictor
**Protocol:** `buffer-health-predictor/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-buffer-health-predictor
**Protocol:** `buffer-health-predictor/v1`
**Export:** `{ HyperP2PBufferHealthPredictor, PROTOCOL }`
## Overview
`HyperP2PBufferHealthPredictor` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'buffer-health-predictor/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-buffer-health-predictor
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PBufferHealthPredictor } = require('../index.js')
async function main () {
const m = new HyperP2PBufferHealthPredictor()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,69 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const { mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'buffer-health-predictor/v1'
class HyperP2PBufferHealthPredictor extends EventEmitter {
constructor (opts = {}) {
super()
this._samples = []
this.maxSamples = opts.maxSamples ?? 64
this._topologyScore = 1
this._contribScore = 1
this._stats = { samples: 0, warnings: 0 }
}
reportBuffer (levelMs) {
const v = Math.max(0, levelMs | 0)
this._samples.push({ levelMs: v, at: Date.now() })
if (this._samples.length > this.maxSamples) this._samples.shift()
this._stats.samples++
const risk = this.predictStallRisk()
if (risk > 0.7) {
this._stats.warnings++
this.emit('stall-risk', { risk, levelMs: v })
}
return risk
}
setSignals ({ topologyScore, contribScore } = {}) {
if (topologyScore != null) this._topologyScore = Math.min(1, Math.max(0, topologyScore))
if (contribScore != null) this._contribScore = Math.min(1, Math.max(0, contribScore))
}
predictStallRisk () {
if (!this._samples.length) return 0
const recent = this._samples.slice(-8)
const avg = recent.reduce((s, x) => s + x.levelMs, 0) / recent.length
const trend = recent.length > 1
? recent[recent.length - 1].levelMs - recent[0].levelMs
: 0
let risk = avg < 800 ? 0.8 : avg < 2000 ? 0.35 : 0.1
if (trend < -200) risk = Math.min(1, risk + 0.25)
risk = risk * (2 - this._topologyScore) * (2 - this._contribScore)
return Math.min(1, Math.max(0, risk))
}
criticalChunksNeeded () {
const risk = this.predictStallRisk()
if (risk < 0.4) return []
return [{ type: 'keyframe', count: risk > 0.7 ? 3 : 1 }, { type: 'audio', count: 1 }]
}
getStats () {
return mediaStats(this._stats, PROTOCOL, {
stallRisk: this.predictStallRisk(),
sampleCount: this._samples.length
})
}
async ready () { return this }
async close () {
this._samples = []
this.emit('closed')
}
}
module.exports = { HyperP2PBufferHealthPredictor, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-buffer-health-predictor",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — buffer health predictor.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,12 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PBufferHealthPredictor, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PBufferHealthPredictor); t.is(PROTOCOL, 'buffer-health-predictor/v1') })
test('stall risk', async (t) => {
const m = new HyperP2PBufferHealthPredictor()
m.reportBuffer(400)
t.ok(m.predictStallRisk() > 0.5)
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.3.1
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-chunk-scheduler-media
**Protocol:** `chunk-scheduler-media/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-chunk-scheduler-media
**Protocol:** `chunk-scheduler-media/v1`
**Export:** `{ HyperP2PChunkSchedulerMedia, PROTOCOL }`
## Overview
`HyperP2PChunkSchedulerMedia` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'chunk-scheduler-media/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-chunk-scheduler-media
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PChunkSchedulerMedia } = require('../index.js')
async function main () {
const m = new HyperP2PChunkSchedulerMedia()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,79 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const { CHUNK_TYPES, mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'chunk-scheduler-media/v1'
const PRIORITY = {
[CHUNK_TYPES.KEYFRAME]: 100,
[CHUNK_TYPES.BASE]: 80,
[CHUNK_TYPES.AUDIO]: 70,
[CHUNK_TYPES.ENHANCEMENT]: 50,
[CHUNK_TYPES.DELTA]: 40
}
class HyperP2PChunkSchedulerMedia extends EventEmitter {
constructor (opts = {}) {
super()
this._queue = []
this._stats = { enqueued: 0, scheduled: 0 }
}
enqueue (chunk, priority = null) {
if (!chunk || chunk.seq == null) throw new Error('chunk with seq required')
const p = priority != null ? priority : (PRIORITY[chunk.type] ?? 30)
const entry = { chunk, priority: p, enqueuedAt: Date.now() }
this._queue.push(entry)
this._stats.enqueued++
this._sort()
this.emit('enqueue', entry)
return entry
}
_sort () {
this._queue.sort((a, b) => b.priority - a.priority || a.chunk.seq - b.chunk.seq)
}
nextChunks (limit = 8) {
const out = this._queue.splice(0, Math.max(0, limit | 0))
this._stats.scheduled += out.length
if (out.length) this.emit('schedule', { count: out.length })
return out.map((e) => e.chunk)
}
prioritizeKeyframes () {
for (const e of this._queue) {
if (e.chunk.type === CHUNK_TYPES.KEYFRAME || e.chunk.keyframe) {
e.priority = PRIORITY[CHUNK_TYPES.KEYFRAME]
}
}
this._sort()
return this._queue.length
}
schedulePlan () {
const plan = { keyframes: 0, audio: 0, enhancement: 0, other: 0 }
for (const e of this._queue) {
if (e.chunk.type === CHUNK_TYPES.KEYFRAME) plan.keyframes++
else if (e.chunk.type === CHUNK_TYPES.AUDIO) plan.audio++
else if (e.chunk.type === CHUNK_TYPES.ENHANCEMENT) plan.enhancement++
else plan.other++
}
return plan
}
pendingCount () { return this._queue.length }
getStats () {
return mediaStats(this._stats, PROTOCOL, { pending: this._queue.length })
}
async ready () { return this }
async close () {
this._queue = []
this.emit('closed')
}
}
module.exports = { HyperP2PChunkSchedulerMedia, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-chunk-scheduler-media",
"version": "0.3.1",
"description": "P2P media streaming — chunk scheduler media.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,14 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PChunkSchedulerMedia, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PChunkSchedulerMedia); t.is(PROTOCOL, 'chunk-scheduler-media/v1') })
test('priority schedule', async (t) => {
const m = new HyperP2PChunkSchedulerMedia()
m.enqueue({ seq: 2, type: 'delta' })
m.enqueue({ seq: 0, type: 'keyframe', keyframe: true })
const next = m.nextChunks(1)
t.is(next[0].type, 'keyframe')
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-content-protection
**Protocol:** `content-protection/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-content-protection
**Protocol:** `content-protection/v1`
**Export:** `{ HyperP2PContentProtection, PROTOCOL }`
## Overview
`HyperP2PContentProtection` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'content-protection/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-content-protection
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PContentProtection } = require('../index.js')
async function main () {
const m = new HyperP2PContentProtection()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,70 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const b4a = require('b4a')
const crypto = require('hypercore-crypto')
const { assertStreamId, mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'content-protection/v1'
class HyperP2PContentProtection extends EventEmitter {
constructor (opts = {}) {
super()
this._keys = new Map()
this._activeKeyId = opts.keyId || 'default'
this._stats = { encrypted: 0, decrypted: 0, rotations: 0 }
this._ensureKey(this._activeKeyId)
}
_ensureKey (keyId) {
if (!this._keys.has(keyId)) {
this._keys.set(keyId, crypto.hash(b4a.from('media-key:' + keyId)))
}
return this._keys.get(keyId)
}
encryptSegment (payload, keyId = null) {
if (payload == null) throw new Error('payload required')
const kid = keyId || this._activeKeyId
const key = this._ensureKey(kid)
const buf = b4a.isBuffer(payload) ? payload : b4a.from(payload)
const out = b4a.alloc(buf.length)
for (let i = 0; i < buf.length; i++) out[i] = buf[i] ^ key[i % key.length]
this._stats.encrypted++
return { keyId: kid, data: out, at: Date.now() }
}
decryptSegment (payload, keyId = null) {
const res = this.encryptSegment(payload, keyId)
this._stats.decrypted++
return res.data
}
rotateKeys (newKeyId = null) {
const kid = newKeyId || b4a.toString(crypto.hash(b4a.from(String(Date.now()))), 'hex').slice(0, 12)
this._activeKeyId = kid
this._ensureKey(kid)
this._stats.rotations++
this.emit('rotate', { keyId: kid })
return kid
}
bindStream (streamId, keyId = null) {
const sid = assertStreamId(streamId)
const kid = keyId || this._activeKeyId
this._ensureKey(kid)
return { streamId: sid, keyId: kid }
}
getStats () {
return mediaStats(this._stats, PROTOCOL, { activeKeyId: this._activeKeyId, keys: this._keys.size })
}
async ready () { return this }
async close () {
this._keys.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PContentProtection, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-content-protection",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — content protection.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,13 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PContentProtection, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PContentProtection); t.is(PROTOCOL, 'content-protection/v1') })
test('encrypt decrypt', async (t) => {
const m = new HyperP2PContentProtection()
const enc = m.encryptSegment(Buffer.from('secret'))
const dec = m.decryptSegment(enc.data, enc.keyId)
t.is(dec.toString(), 'secret')
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-contribution-ledger
**Protocol:** `contribution-ledger/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-contribution-ledger
**Protocol:** `contribution-ledger/v1`
**Export:** `{ HyperP2PContributionLedger, PROTOCOL }`
## Overview
`HyperP2PContributionLedger` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'contribution-ledger/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-contribution-ledger
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PContributionLedger } = require('../index.js')
async function main () {
const m = new HyperP2PContributionLedger()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,78 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const b4a = require('b4a')
const { initModuleSwarm, gossipSend } = require('../../_shared/p2p-bare.js')
const { assertPeerId, mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'contribution-ledger/v1'
class HyperP2PContributionLedger extends EventEmitter {
constructor (opts = {}) {
super()
this.topic = opts.topic || null
this.keyPair = opts.keyPair || require('hypercore-crypto').keyPair()
this._ledger = new Map()
this._stats = { recorded: 0, gossipOut: 0 }
this.swarm = null
this._peerMsgs = null
}
recordUpload (peerId, bytes, streamId = null) {
const id = assertPeerId(peerId)
const n = Math.max(0, bytes | 0)
const cur = this._ledger.get(id) || { peerId: id, bytes: 0, streams: new Set() }
cur.bytes += n
if (streamId) cur.streams.add(String(streamId))
cur.updatedAt = Date.now()
this._ledger.set(id, cur)
this._stats.recorded++
this._gossip({ type: 'contrib', peerId: id, bytes: n, streamId })
this.emit('upload', { peerId: id, bytes: n })
return cur.bytes
}
balance (peerId) {
const cur = this._ledger.get(assertPeerId(peerId))
return cur ? cur.bytes : 0
}
topContributors (limit = 10) {
return [...this._ledger.values()]
.sort((a, b) => b.bytes - a.bytes)
.slice(0, Math.max(0, limit | 0))
.map((r) => ({ peerId: r.peerId, bytes: r.bytes }))
}
_gossip (payload) {
if (this._peerMsgs) {
gossipSend(this, payload)
this._stats.gossipOut++
}
}
getStats () {
return mediaStats(this._stats, PROTOCOL, { peers: this._ledger.size })
}
async ready () {
if (this.swarm || !this.topic) return this
await initModuleSwarm(this, {
keyPair: this.keyPair,
topic: this.topic,
protocol: PROTOCOL,
onmessage: (d) => {
if (d?.type === 'contrib') this.recordUpload(d.peerId, d.bytes, d.streamId)
}
})
return this
}
async close () {
if (this.swarm) await this.swarm.destroy().catch(() => {})
this.swarm = null
this._ledger.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PContributionLedger, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-contribution-ledger",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — contribution ledger.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,12 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PContributionLedger, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PContributionLedger); t.is(PROTOCOL, 'contribution-ledger/v1') })
test('record upload', async (t) => {
const m = new HyperP2PContributionLedger()
m.recordUpload('peer-a', 1000)
t.is(m.balance('peer-a'), 1000)
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-enterprise-orchestrator
**Protocol:** `enterprise-orchestrator/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-enterprise-orchestrator
**Protocol:** `enterprise-orchestrator/v1`
**Export:** `{ HyperP2PEnterpriseOrchestrator, PROTOCOL }`
## Overview
`HyperP2PEnterpriseOrchestrator` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'enterprise-orchestrator/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-enterprise-orchestrator
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PEnterpriseOrchestrator } = require('../index.js')
async function main () {
const m = new HyperP2PEnterpriseOrchestrator()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,81 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const { mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'enterprise-orchestrator/v1'
class HyperP2PEnterpriseOrchestrator extends EventEmitter {
constructor (opts = {}) {
super()
this._regions = new Map()
this._sla = { targetUptime: opts.targetUptime ?? 0.999, maxStallRate: opts.maxStallRate ?? 0.01 }
this._stats = { deploys: 0, violations: 0 }
}
deployRegion (region, seedPeers = []) {
if (!region) throw new Error('region required')
const rec = {
region: String(region),
seedPeers: seedPeers.map(String),
deployedAt: Date.now(),
trees: 0,
viewers: 0
}
this._regions.set(rec.region, rec)
this._stats.deploys++
this.emit('deploy', rec)
return rec
}
updateRegionStats (region, patch = {}) {
const r = this._regions.get(String(region))
if (!r) return false
Object.assign(r, patch)
return true
}
slaStatus () {
let ok = true
const regions = [...this._regions.values()]
for (const r of regions) {
if (r.stallRate != null && r.stallRate > this._sla.maxStallRate) {
ok = false
this._stats.violations++
}
}
return {
ok,
regions: regions.length,
sla: { ...this._sla },
violations: this._stats.violations
}
}
coordinateSwarm (config = {}) {
const plan = {
regions: [...this._regions.keys()],
maxFanout: config.maxFanout ?? 8,
seedPeers: config.seedPeers || [],
at: Date.now()
}
this.emit('coordinate', plan)
return plan
}
listRegions () {
return [...this._regions.values()]
}
getStats () {
return mediaStats(this._stats, PROTOCOL, { regions: this._regions.size })
}
async ready () { return this }
async close () {
this._regions.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PEnterpriseOrchestrator, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-enterprise-orchestrator",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — enterprise orchestrator.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,12 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PEnterpriseOrchestrator, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PEnterpriseOrchestrator); t.is(PROTOCOL, 'enterprise-orchestrator/v1') })
test('deploy region', async (t) => {
const m = new HyperP2PEnterpriseOrchestrator()
m.deployRegion('us-east', ['seed-1'])
t.ok(m.slaStatus().ok)
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-fec-video
**Protocol:** `fec-video/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-fec-video
**Protocol:** `fec-video/v1`
**Export:** `{ HyperP2PFecVideo, PROTOCOL }`
## Overview
`HyperP2PFecVideo` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'fec-video/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-fec-video
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PFecVideo } = require('../index.js')
async function main () {
const m = new HyperP2PFecVideo()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,71 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const b4a = require('b4a')
const crypto = require('hypercore-crypto')
const { mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'fec-video/v1'
class HyperP2PFecVideo extends EventEmitter {
constructor (opts = {}) {
super()
this.redundancy = opts.redundancy ?? 0.25
this._groups = new Map()
this._stats = { encoded: 0, recovered: 0 }
}
encodeGroup (groupId, shards) {
if (!Array.isArray(shards) || !shards.length) throw new Error('shards required')
const gid = String(groupId)
const dataShards = shards.map((s) => (b4a.isBuffer(s) ? s : b4a.from(s)))
const parityCount = Math.max(1, Math.ceil(dataShards.length * this.redundancy))
const parities = []
for (let i = 0; i < parityCount; i++) {
let acc = b4a.alloc(dataShards[0].length)
for (let j = 0; j < dataShards.length; j++) {
const mix = crypto.hash(b4a.concat([dataShards[j], b4a.from(String(i + j))]))
for (let k = 0; k < acc.length; k++) acc[k] ^= mix[k % mix.length]
}
parities.push(acc)
}
const group = {
groupId: gid,
dataCount: dataShards.length,
parityCount,
shards: dataShards,
parities,
at: Date.now()
}
this._groups.set(gid, group)
this._stats.encoded++
this.emit('encode', { groupId: gid, parityCount })
return { groupId: gid, parities }
}
decodeGroup (groupId, received = {}) {
const g = this._groups.get(String(groupId))
if (!g) return null
const have = (received.data || []).filter(Boolean).length + (received.parity || []).filter(Boolean).length
if (have < g.dataCount) return null
this._stats.recovered++
this.emit('recover', { groupId })
return { groupId, ok: true, dataShards: g.shards.length }
}
recover (groupId) {
return this.decodeGroup(groupId, { parity: [1] }) != null
}
getStats () {
return mediaStats(this._stats, PROTOCOL, { groups: this._groups.size })
}
async ready () { return this }
async close () {
this._groups.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PFecVideo, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-fec-video",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — fec video.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,12 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PFecVideo, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PFecVideo); t.is(PROTOCOL, 'fec-video/v1') })
test('encode group', async (t) => {
const m = new HyperP2PFecVideo()
const r = m.encodeGroup('g1', [Buffer.from('a'), Buffer.from('b')])
t.ok(r.parities.length >= 1)
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-helper-swarm-coordinator
**Protocol:** `helper-swarm-coordinator/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-helper-swarm-coordinator
**Protocol:** `helper-swarm-coordinator/v1`
**Export:** `{ HyperP2PHelperSwarmCoordinator, PROTOCOL }`
## Overview
`HyperP2PHelperSwarmCoordinator` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'helper-swarm-coordinator/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-helper-swarm-coordinator
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PHelperSwarmCoordinator } = require('../index.js')
async function main () {
const m = new HyperP2PHelperSwarmCoordinator()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,95 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const b4a = require('b4a')
const { initModuleSwarm, gossipSend } = require('../../_shared/p2p-bare.js')
const { assertPeerId, assertStreamId, ROLES, mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'helper-swarm-coordinator/v1'
class HyperP2PHelperSwarmCoordinator extends EventEmitter {
constructor (opts = {}) {
super()
this.topic = opts.topic || null
this.keyPair = opts.keyPair || require('hypercore-crypto').keyPair()
this.peerHex = b4a.toString(this.keyPair.publicKey, 'hex')
this._helpers = new Map()
this._assignments = new Map()
this._stats = { assigned: 0, relayBytes: 0 }
this.swarm = null
this._peerMsgs = null
}
registerHelper (peerId, capacityBps = 0) {
const id = assertPeerId(peerId)
const rec = { peerId: id, capacityBps: capacityBps | 0, usedBps: 0, role: ROLES.HELPER, at: Date.now() }
this._helpers.set(id, rec)
this.emit('helper', rec)
return rec
}
assignViewer (viewerId, helperIds = []) {
const vid = assertPeerId(viewerId)
const helpers = helperIds.map((h) => assertPeerId(h)).filter((h) => this._helpers.has(h))
const assignment = { viewerId: vid, helpers, assignedAt: Date.now() }
this._assignments.set(vid, assignment)
this._stats.assigned++
this._gossip({ type: 'assign', assignment })
this.emit('assign', assignment)
return assignment
}
recordRelay (helperId, bytes) {
const h = this._helpers.get(assertPeerId(helperId))
if (!h) return false
const n = Math.max(0, bytes | 0)
h.usedBps += n
this._stats.relayBytes += n
this.emit('relay', { helperId, bytes: n })
return true
}
helpersForViewer (viewerId) {
const a = this._assignments.get(assertPeerId(viewerId))
return a ? [...a.helpers] : []
}
availableHelpers () {
return [...this._helpers.values()].filter((h) => h.usedBps < h.capacityBps)
}
_gossip (payload) {
if (this._peerMsgs) gossipSend(this, payload)
}
getStats () {
return mediaStats(this._stats, PROTOCOL, {
helpers: this._helpers.size,
viewers: this._assignments.size
})
}
async ready () {
if (this.swarm || !this.topic) return this
await initModuleSwarm(this, {
keyPair: this.keyPair,
topic: this.topic,
protocol: PROTOCOL,
onmessage: (d) => {
if (d?.type === 'assign' && d.assignment) {
this._assignments.set(d.assignment.viewerId, d.assignment)
}
}
})
return this
}
async close () {
if (this.swarm) await this.swarm.destroy().catch(() => {})
this.swarm = null
this._helpers.clear()
this._assignments.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PHelperSwarmCoordinator, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-helper-swarm-coordinator",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — helper swarm coordinator.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,13 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PHelperSwarmCoordinator, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PHelperSwarmCoordinator); t.is(PROTOCOL, 'helper-swarm-coordinator/v1') })
test('assign helpers', async (t) => {
const m = new HyperP2PHelperSwarmCoordinator()
m.registerHelper('h1', 50_000_000)
const a = m.assignViewer('viewer-1', ['h1'])
t.is(a.helpers[0], 'h1')
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-latency-optimizer
**Protocol:** `latency-optimizer/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-latency-optimizer
**Protocol:** `latency-optimizer/v1`
**Export:** `{ HyperP2PLatencyOptimizer, PROTOCOL }`
## Overview
`HyperP2PLatencyOptimizer` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'latency-optimizer/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-latency-optimizer
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PLatencyOptimizer } = require('../index.js')
async function main () {
const m = new HyperP2PLatencyOptimizer()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,60 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const { mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'latency-optimizer/v1'
class HyperP2PLatencyOptimizer extends EventEmitter {
constructor (opts = {}) {
super()
this.targetGlassMs = opts.targetGlassMs ?? 1500
this._paths = new Map()
this._stats = { measured: 0, optimized: 0 }
}
measurePath (pathId, hops = []) {
if (!pathId) throw new Error('pathId required')
const rtt = hops.reduce((s, h) => s + (h.rttMs || 20), 0)
const rec = { pathId: String(pathId), hops, rttMs: rtt, at: Date.now() }
this._paths.set(rec.pathId, rec)
this._stats.measured++
return rec
}
optimizeRoute (treeSnapshot = {}) {
const nodes = treeSnapshot.nodes || []
let bestRoot = treeSnapshot.root
let bestRtt = Infinity
for (const n of nodes) {
const p = this._paths.get(n.peerId)
const rtt = p ? p.rttMs : 50 * (n.childCount + 1)
if (rtt < bestRtt) {
bestRtt = rtt
bestRoot = n.peerId
}
}
this._stats.optimized++
const plan = { root: bestRoot, estimatedGlassMs: bestRtt + 200, targetGlassMs: this.targetGlassMs }
this.emit('optimize', plan)
return plan
}
suggestParent (candidates = [], tree = {}) {
const plan = this.optimizeRoute(tree)
if (candidates.includes(plan.root)) return plan.root
return candidates[0] || plan.root
}
getStats () {
return mediaStats(this._stats, PROTOCOL, { paths: this._paths.size })
}
async ready () { return this }
async close () {
this._paths.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PLatencyOptimizer, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-latency-optimizer",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — latency optimizer.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,13 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PLatencyOptimizer, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PLatencyOptimizer); t.is(PROTOCOL, 'latency-optimizer/v1') })
test('optimize', async (t) => {
const m = new HyperP2PLatencyOptimizer()
m.measurePath('p1', [{ rttMs: 10 }, { rttMs: 15 }])
const plan = m.optimizeRoute({ root: 'a', nodes: [{ peerId: 'a', childCount: 1 }] })
t.ok(plan.estimatedGlassMs > 0)
await m.close()
})
@@ -0,0 +1,5 @@
# Changelog
## 0.0.0-scaffold
- Initial media-streaming category implementation.
@@ -0,0 +1,17 @@
# hyper-p2p-live-edge-manager
**Protocol:** `live-edge-manager/v1`
Media-streaming module for peer-assisted live/VOD delivery. See [`docs/api.md`](docs/api.md).
## Install
```bash
npm install
```
## Test
```bash
npm test
```
@@ -0,0 +1,21 @@
# API: hyper-p2p-live-edge-manager
**Protocol:** `live-edge-manager/v1`
**Export:** `{ HyperP2PLiveEdgeManager, PROTOCOL }`
## Overview
`HyperP2PLiveEdgeManager` — see [`index.js`](../index.js) for methods, events, and `getStats()`.
## Lifecycle
- `async ready()` — optional Hyperswarm join when `topic` is set
- `async close()` — teardown
- `getStats()` — metrics + `protocol: 'live-edge-manager/v1'`
## Testing
```bash
npm install && npm test
```
@@ -0,0 +1,3 @@
# Architecture: hyper-p2p-live-edge-manager
Part of the **media-streaming** category. Composes with network-stack, core-infrastructure, scheduling, measurement, and trust modules per `MODULE_REGISTRY.yaml`.
@@ -0,0 +1,14 @@
require('bare-process/global')
const { HyperP2PLiveEdgeManager } = require('../index.js')
async function main () {
const m = new HyperP2PLiveEdgeManager()
await m.ready()
console.log(m.getStats())
await m.close()
}
main().catch((err) => {
console.error(err)
process.exit(1)
})
@@ -0,0 +1,64 @@
require('bare-process/global')
const EventEmitter = require('bare-events')
const { assertStreamId, mediaStats } = require('../../_shared/media-streaming-base.js')
const PROTOCOL = 'live-edge-manager/v1'
class HyperP2PLiveEdgeManager extends EventEmitter {
constructor (opts = {}) {
super()
this.maxLagMs = opts.maxLagMs ?? 3000
this._edges = new Map()
this._stats = { updates: 0, stale: 0 }
}
setLiveEdge (streamId, seq, wallAt = Date.now()) {
const sid = assertStreamId(streamId)
const edge = { streamId: sid, seq: seq | 0, wallAt, updatedAt: Date.now() }
this._edges.set(sid, edge)
this._stats.updates++
this.emit('edge', edge)
return edge
}
getLiveEdge (streamId) {
return this._edges.get(assertStreamId(streamId)) || null
}
edgeLagMs (streamId, now = Date.now()) {
const e = this.getLiveEdge(streamId)
if (!e) return null
return Math.max(0, now - e.wallAt)
}
isFresh (streamId, chunkWallAt, now = Date.now()) {
const lag = this.edgeLagMs(streamId, now)
if (lag == null) return false
const behind = now - chunkWallAt
return behind <= this.maxLagMs + lag
}
listStaleStreams (now = Date.now()) {
const out = []
for (const e of this._edges.values()) {
if (now - e.updatedAt > this.maxLagMs) {
out.push(e.streamId)
this._stats.stale++
}
}
return out
}
getStats () {
return mediaStats(this._stats, PROTOCOL, { streams: this._edges.size })
}
async ready () { return this }
async close () {
this._edges.clear()
this.emit('closed')
}
}
module.exports = { HyperP2PLiveEdgeManager, PROTOCOL }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"name": "hyper-p2p-live-edge-manager",
"version": "0.0.0-scaffold",
"description": "P2P media streaming — live edge manager.",
"main": "index.js",
"type": "commonjs",
"license": "Apache-2.0",
"scripts": { "test": "brittle-bare test/test.js" },
"dependencies": {
"bare-events": "^2.8.0",
"bare-process": "^4.4.0",
"bare-timers": "^2.0.0",
"b4a": "^1.6.7",
"hypercore-crypto": "^3.0.0",
"protomux": "^3.0.0",
"compact-encoding": "^2.0.0"
},
"peerDependencies": { "hyperswarm": "^4.0.0", "bare": ">=1.0.0" },
"devDependencies": { "brittle": "^3.0.0" },
"imports": {
"process": { "bare": "bare-process", "default": "process" },
"events": { "bare": "bare-events", "default": "events" },
"timers": { "bare": "bare-timers", "default": "timers" }
}
}
@@ -0,0 +1,12 @@
require('bare-process/global')
const test = require('brittle')
const { HyperP2PLiveEdgeManager, PROTOCOL } = require('../index.js')
test('exports', (t) => { t.ok(HyperP2PLiveEdgeManager); t.is(PROTOCOL, 'live-edge-manager/v1') })
test('live edge', async (t) => {
const m = new HyperP2PLiveEdgeManager()
m.setLiveEdge('live', 100, Date.now())
t.is(m.getLiveEdge('live').seq, 100)
await m.close()
})

Some files were not shown because too many files have changed in this diff Show More