dbConn = $dbConn; $this->cache = new DbStatementCache($dbConn); } public function pruneExpiredPeers(): void { $this->dbConn->execute('DELETE FROM ser_torrents_peers WHERE peer_expires < NOW()'); } public function getPeers(TorrentInfo|string $torrentInfo): array { $stmt = $this->cache->get('SELECT peer_id, torrent_id, user_id, INET6_NTOA(peer_address), peer_port, UNIX_TIMESTAMP(peer_updated), UNIX_TIMESTAMP(peer_expires), peer_agent, peer_key, peer_uploaded, peer_downloaded, peer_left FROM ser_torrents_peers WHERE torrent_id = ?'); $stmt->addParameter(1, $torrentInfo instanceof TorrentInfo ? $torrentInfo->getId() : $torrentInfo); $stmt->execute(); $peers = []; $result = $stmt->getResult(); while($result->next()) $peers[] = new TorrentPeerInfo($result); return $peers; } public function getPeer(TorrentInfo|string $torrentInfo, string $peerId): ?TorrentPeerInfo { $stmt = $this->cache->get('SELECT peer_id, torrent_id, user_id, INET6_NTOA(peer_address), peer_port, UNIX_TIMESTAMP(peer_updated), UNIX_TIMESTAMP(peer_expires), peer_agent, peer_key, peer_uploaded, peer_downloaded, peer_left FROM ser_torrents_peers WHERE torrent_id = ? AND peer_id = ?'); $stmt->addParameter(1, $torrentInfo instanceof TorrentInfo ? $torrentInfo->getId() : $torrentInfo); $stmt->addParameter(2, $peerId); $stmt->execute(); $result = $stmt->getResult(); if(!$result->next()) return null; return new TorrentPeerInfo($result); } public function createPeer( TorrentInfo|string $torrentInfo, UserInfo|string|null $userInfo, string $peerId, string $remoteAddr, int $remotePort, int $interval, string $peerAgent, string $peerKey, int $bytesUploaded, int $bytesDownloaded, int $bytesRemaining ): TorrentPeerInfo { $stmt = $this->cache->get('INSERT INTO ser_torrents_peers (torrent_id, user_id, peer_id, peer_address, peer_port, peer_updated, peer_expires, peer_agent, peer_key, peer_uploaded, peer_downloaded, peer_left) VALUES (?, ?, ?, INET6_ATON(?), ?, NOW(), NOW() + INTERVAL ? SECOND, ?, ?, ?, ?, ?)'); $stmt->addParameter(1, $torrentInfo instanceof TorrentInfo ? $torrentInfo->getId() : $torrentInfo); $stmt->addParameter(2, $userInfo === null ? null : ($userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo)); $stmt->addParameter(3, $peerId); $stmt->addParameter(4, $remoteAddr); $stmt->addParameter(5, $remotePort); $stmt->addParameter(6, $interval); $stmt->addParameter(7, $peerAgent); $stmt->addParameter(8, $peerKey); $stmt->addParameter(9, $bytesUploaded); $stmt->addParameter(10, $bytesDownloaded); $stmt->addParameter(11, $bytesRemaining); $stmt->execute(); return $this->getPeer($torrentInfo, $peerId) ?? throw new RuntimeException('Failed to record peer information.'); } public function updatePeer( TorrentInfo|string $torrentInfo, TorrentPeerInfo|string $peerInfo, string $remoteAddr, int $remotePort, int $interval, string $peerAgent, int $bytesUploaded, int $bytesDownloaded, int $bytesRemaining ): void { $stmt = $this->cache->get('UPDATE ser_torrents_peers SET peer_address = INET6_ATON(?), peer_port = ?, peer_updated = NOW(), peer_expires = NOW() + INTERVAL ? SECOND, peer_agent = ?, peer_uploaded = ?, peer_downloaded = ?, peer_left = ? WHERE torrent_id = ? AND peer_id = ?'); $stmt->addParameter(1, $remoteAddr); $stmt->addParameter(2, $remotePort); $stmt->addParameter(3, $interval); $stmt->addParameter(4, $peerAgent); $stmt->addParameter(5, $bytesUploaded); $stmt->addParameter(6, $bytesDownloaded); $stmt->addParameter(7, $bytesRemaining); $stmt->addParameter(8, $torrentInfo instanceof TorrentInfo ? $torrentInfo->getId() : $torrentInfo); $stmt->addParameter(9, $peerInfo instanceof TorrentPeerInfo ? $peerInfo->getId() : $peerInfo); $stmt->execute(); } public function deletePeer(TorrentInfo|string $torrentInfo, TorrentPeerInfo|string $peerInfo): void { $stmt = $this->cache->get('DELETE FROM ser_torrents_peers WHERE torrent_id = ? AND peer_id = ?'); $stmt->addParameter(1, $torrentInfo instanceof TorrentInfo ? $torrentInfo->getId() : $torrentInfo); $stmt->addParameter(2, $peerInfo instanceof TorrentPeerInfo ? $peerInfo->getId() : $peerInfo); $stmt->execute(); } public function countIncompletePeers(TorrentInfo|string $torrentInfo): int { $stmt = $this->cache->get('SELECT COUNT(*) FROM ser_torrents_peers WHERE torrent_id = ? AND peer_left > 0'); $stmt->addParameter(1, $torrentInfo instanceof TorrentInfo ? $torrentInfo->getId() : $torrentInfo); $stmt->execute(); $result = $stmt->getResult(); return $result->next() ? $result->getInteger(0) : 0; } public function countCompletePeers(TorrentInfo|string $torrentInfo): int { $stmt = $this->cache->get('SELECT COUNT(*) FROM ser_torrents_peers WHERE torrent_id = ? AND peer_left <= 0'); $stmt->addParameter(1, $torrentInfo instanceof TorrentInfo ? $torrentInfo->getId() : $torrentInfo); $stmt->execute(); $result = $stmt->getResult(); return $result->next() ? $result->getInteger(0) : 0; } public function countUserDownloading(UserInfo|string $userInfo): int { $stmt = $this->cache->get('SELECT COUNT(*) FROM ser_torrents_peers WHERE user_id = ? AND peer_left > 0'); $stmt->addParameter(1, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo); $stmt->execute(); $result = $stmt->getResult(); return $result->next() ? $result->getInteger(0) : 0; } public function countUserUploading(UserInfo|string $userInfo): int { $stmt = $this->cache->get('SELECT COUNT(*) FROM ser_torrents_peers WHERE user_id = ? AND peer_left <= 0'); $stmt->addParameter(1, $userInfo instanceof UserInfo ? $userInfo->getId() : $userInfo); $stmt->execute(); $result = $stmt->getResult(); return $result->next() ? $result->getInteger(0) : 0; } }