import jsonpatch, {Operation} from 'fast-json-patch'; import type {TorrentStatus} from '../../shared/constants/torrentStatusMap'; import torrentStatusMap from '../../shared/constants/torrentStatusMap'; import type {Taxonomy} from '../../shared/types/Taxonomy'; import type {TorrentList, TorrentProperties} from '../../shared/types/Torrent'; import BaseService from './BaseService'; type TaxonomyServiceEvents = { TAXONOMY_DIFF_CHANGE: (payload: {id: number; diff: Operation[]}) => void; }; class TaxonomyService extends BaseService { taxonomy: Taxonomy = { statusCounts: {'': 0}, tagCounts: {'': 0, untagged: 0}, tagSizes: {}, trackerCounts: {'': 0}, trackerSizes: {}, }; lastTaxonomy: Taxonomy = this.taxonomy; constructor(...args: ConstructorParameters) { super(...args); this.onServicesUpdated = () => { if (this.services?.clientGatewayService == null) { return; } const {clientGatewayService} = this.services; clientGatewayService.on('PROCESS_TORRENT_LIST_START', this.handleProcessTorrentListStart); clientGatewayService.on('PROCESS_TORRENT_LIST_END', this.handleProcessTorrentListEnd); clientGatewayService.on('PROCESS_TORRENT', this.handleProcessTorrent); }; } async destroy(drop: boolean) { if (this.services?.clientGatewayService == null) { return; } const {clientGatewayService} = this.services; clientGatewayService.removeListener('PROCESS_TORRENT_LIST_START', this.handleProcessTorrentListStart); clientGatewayService.removeListener('PROCESS_TORRENT_LIST_END', this.handleProcessTorrentListEnd); clientGatewayService.removeListener('PROCESS_TORRENT', this.handleProcessTorrent); super.destroy(drop); } getTaxonomy() { return { id: Date.now(), taxonomy: this.taxonomy, }; } handleProcessTorrentListStart = () => { this.lastTaxonomy = { statusCounts: {...this.taxonomy.statusCounts}, tagCounts: {...this.taxonomy.tagCounts}, tagSizes: {...this.taxonomy.tagSizes}, trackerCounts: {...this.taxonomy.trackerCounts}, trackerSizes: {...this.taxonomy.trackerSizes}, }; torrentStatusMap.forEach((status) => { this.taxonomy.statusCounts[status] = 0; }); this.taxonomy.statusCounts[''] = 0; this.taxonomy.tagCounts = {'': 0, untagged: 0}; this.taxonomy.tagSizes = {}; this.taxonomy.trackerCounts = {'': 0}; this.taxonomy.trackerSizes = {}; }; handleProcessTorrentListEnd = ({torrents}: {torrents: TorrentList}) => { const {length} = Object.keys(torrents); this.taxonomy.statusCounts[''] = length; this.taxonomy.tagCounts[''] = length; this.taxonomy.trackerCounts[''] = length; const taxonomyDiffs = jsonpatch.compare(this.lastTaxonomy, this.taxonomy); if (taxonomyDiffs.length > 0) { this.emit('TAXONOMY_DIFF_CHANGE', { diff: taxonomyDiffs, id: Date.now(), }); } }; handleProcessTorrent = (torrentProperties: TorrentProperties) => { this.incrementStatusCounts(torrentProperties.status); this.incrementTagCounts(torrentProperties.tags); this.incrementTagSizes(torrentProperties.tags, torrentProperties.sizeBytes); this.incrementTrackerCounts(torrentProperties.trackerURIs); this.incrementTrackerSizes(torrentProperties.trackerURIs, torrentProperties.sizeBytes); }; incrementStatusCounts(statuses: Array) { statuses.forEach((status) => { this.taxonomy.statusCounts[status] += 1; }); } incrementTagCounts(tags: TorrentProperties['tags']) { if (tags.length === 0) { this.taxonomy.tagCounts.untagged += 1; } tags.forEach((tag) => { if (this.taxonomy.tagCounts[tag] != null) { this.taxonomy.tagCounts[tag] += 1; } else { this.taxonomy.tagCounts[tag] = 1; } }); } incrementTagSizes(tags: TorrentProperties['tags'], sizeBytes: TorrentProperties['sizeBytes']) { tags.forEach((tag) => { if (this.taxonomy.tagSizes[tag] != null) { this.taxonomy.tagSizes[tag] += sizeBytes; } else { this.taxonomy.tagSizes[tag] = sizeBytes; } }); } incrementTrackerCounts(trackers: TorrentProperties['trackerURIs']) { trackers.forEach((tracker) => { if (this.taxonomy.trackerCounts[tracker] != null) { this.taxonomy.trackerCounts[tracker] += 1; } else { this.taxonomy.trackerCounts[tracker] = 1; } }); } incrementTrackerSizes(trackers: TorrentProperties['trackerURIs'], sizeBytes: TorrentProperties['sizeBytes']) { trackers.forEach((tracker) => { if (this.taxonomy.trackerSizes[tracker] != null) { this.taxonomy.trackerSizes[tracker] += sizeBytes; } else { this.taxonomy.trackerSizes[tracker] = sizeBytes; } }); } } export default TaxonomyService;