tv-anarchy/Sources/TVAnarchyCore/BandwidthPolicy.swift

72 lines
3.6 KiB
Swift

import Foundation
/// What your home machine is doing right now, which sets the upload-priority tier.
public enum BandwidthMode: String, Sendable, Equatable {
/// You're actively pulling content TO yourself (Travel Mode / offline fetch).
/// Your experience dominates public + friends yield all upload to you.
case userFetch
/// You're actively using the machine (streaming, browsing) but not fetching.
case userActive
/// You're away / the machine is idle serve friends generously, public as leftover.
case idle
}
/// Configurable knobs for the priority tiers (your 3rd boundary's "options").
public struct BandwidthOptions: Sendable, Equatable {
/// Tier 2: prioritize friends' streaming experiences while you're idle.
public var serveFriendsWhenIdle: Bool
/// Tier 3: be a good public-swarm citizen while you're idle.
public var seedPublicWhenIdle: Bool
/// Your home upload capacity in KB/s, used to split idle bandwidth. nil = unmetered.
public var totalUploadKBps: Int?
public init(serveFriendsWhenIdle: Bool = true, seedPublicWhenIdle: Bool = true,
totalUploadKBps: Int? = nil) {
self.serveFriendsWhenIdle = serveFriendsWhenIdle
self.seedPublicWhenIdle = seedPublicWhenIdle
self.totalUploadKBps = totalUploadKBps
}
}
/// Upload allocation across the three tiers. `nil` KB/s = unlimited; `0` = blocked.
public struct BandwidthAllocation: Sendable, Equatable {
public var userKBps: Int? // upload dedicated to your own fetch
public var friendsKBps: Int? // upload for friends' streams (mesh / F2F custody)
public var publicKBps: Int? // upload for the public swarm
public init(userKBps: Int?, friendsKBps: Int?, publicKBps: Int?) {
self.userKBps = userKBps; self.friendsKBps = friendsKBps; self.publicKBps = publicKBps
}
}
/// The bandwidth-arbitration brain: allocate upload across **you > friends > public**.
/// Pure decision logic (the governor actuates it onto transmission). This is what
/// makes Travel Mode work: in `.userFetch`, public + friends drop to 0 so home's
/// whole upload pipe is dedicated to your fetch (which the laptop further augments
/// from public peers UX over public good).
public enum BandwidthPolicy {
public static func allocate(mode: BandwidthMode, options: BandwidthOptions) -> BandwidthAllocation {
switch mode {
case .userFetch:
// Travel Mode: everything to you; stop seeding public AND friends.
return BandwidthAllocation(userKBps: nil, friendsKBps: 0, publicKBps: 0)
case .userActive:
// You're using the pipe don't fight it; trickle others (friends > public).
let total = options.totalUploadKBps
return BandwidthAllocation(userKBps: nil,
friendsKBps: options.serveFriendsWhenIdle ? total.map { max(1, $0 / 8) } ?? nil : 0,
publicKBps: 0)
case .idle:
// Tier 2 (friends) ahead of tier 3 (public). Split the pipe when both on.
let total = options.totalUploadKBps
let friends = options.serveFriendsWhenIdle ? total : 0
let pub: Int?
if !options.seedPublicWhenIdle {
pub = 0
} else if options.serveFriendsWhenIdle {
pub = total.map { max(0, $0 / 4) } // friends get the lion's share
} else {
pub = total // no friends public gets it all
}
return BandwidthAllocation(userKBps: 0, friendsKBps: friends, publicKBps: pub)
}
}
}