andrade-soulseek-downloader
Version:
Simple, safe Soulseek download library with built-in rate limiting to prevent bans
147 lines • 4.42 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Track = void 0;
/**
* Domain entity representing a music track available on Soulseek.
* Encapsulates track metadata and quality scoring business logic.
*/
class Track {
id;
artist;
title;
bitrate;
fileSize;
filePath;
user;
slots;
speed;
matchScore;
/**
* Creates a new Track entity.
* @param id - Unique identifier for the track
* @param artist - Artist name
* @param title - Track title
* @param bitrate - Audio bitrate value object
* @param fileSize - File size in bytes
* @param filePath - Path to file on remote user's system
* @param user - Soulseek username of the file owner
* @param slots - Whether user has available download slots
* @param speed - User's upload speed in bytes/second
* @param matchScore - Search relevance score (0-1)
* @throws {Error} If validation fails
*/
constructor(id, artist, title, bitrate, fileSize, filePath, user, slots, speed, matchScore) {
this.id = id;
this.artist = artist;
this.title = title;
this.bitrate = bitrate;
this.fileSize = fileSize;
this.filePath = filePath;
this.user = user;
this.slots = slots;
this.speed = speed;
this.matchScore = matchScore;
this.validateTrack();
}
/** Validates track invariants */
validateTrack() {
if (!this.artist || !this.title) {
throw new Error('Track must have artist and title');
}
if (this.fileSize <= 0) {
throw new Error('File size must be positive');
}
if (this.matchScore < 0 || this.matchScore > 1) {
throw new Error('Match score must be between 0 and 1');
}
}
/** @returns Track unique identifier */
getId() {
return this.id;
}
/** @returns Artist name */
getArtist() {
return this.artist;
}
/** @returns Track title */
getTitle() {
return this.title;
}
/** @returns Bitrate value object */
getBitrate() {
return this.bitrate;
}
/** @returns File size in bytes */
getFileSize() {
return this.fileSize;
}
/** @returns Remote file path */
getFilePath() {
return this.filePath;
}
/** @returns Soulseek username */
getUser() {
return this.user;
}
/** @returns Whether user has download slots available */
hasAvailableSlots() {
return this.slots;
}
/** @returns Upload speed in bytes/second */
getSpeed() {
return this.speed;
}
/** @returns Search relevance score (0-1) */
getMatchScore() {
return this.matchScore;
}
/**
* Calculates quality score based on bitrate, availability, speed, and match.
* Core business logic for track prioritization.
* @returns Quality score from 0-100 (higher is better)
*/
calculateQualityScore() {
let score = 0;
// Bitrate is the most important (50 points)
score += this.bitrate.toQualityPoints();
// Slot availability (25 points)
if (this.slots) {
score += 25;
}
// Connection speed (15 points)
const speedScore = Math.min(15, (this.speed / 5000000) * 15);
score += speedScore;
// Match accuracy (10 points)
score += this.matchScore * 10;
return score;
}
/**
* Compares quality against another track.
* @param other - Track to compare against
* @returns True if this track has higher quality score
*/
isBetterThan(other) {
return this.calculateQualityScore() > other.calculateQualityScore();
}
/**
* Serializes track to JSON representation.
* @returns Plain object with all track properties
*/
toJSON() {
return {
id: this.id.getValue(),
artist: this.artist,
title: this.title,
bitrate: this.bitrate.getValue(),
fileSize: this.fileSize,
filePath: this.filePath,
user: this.user,
slots: this.slots,
speed: this.speed,
matchScore: this.matchScore,
qualityScore: this.calculateQualityScore()
};
}
}
exports.Track = Track;
//# sourceMappingURL=track.js.map