/* * Copyright © Jordan Lee, Dave Perrett and Malcolm Jarvis * This code is licensed under the GPL version 2. * For details, see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Class Torrent */ function Torrent(controller, data) { this.initialize(controller, data) } /*** **** **** Constants **** ***/ // Torrent.fields.status Torrent._StatusStopped = 0 Torrent._StatusCheckWait = 1 Torrent._StatusCheck = 2 Torrent._StatusDownloadWait = 3 Torrent._StatusDownload = 4 Torrent._StatusSeedWait = 5 Torrent._StatusSeed = 6 // Torrent.fields.seedRatioMode Torrent._RatioUseGlobal = 0 Torrent._RatioUseLocal = 1 Torrent._RatioUnlimited = 2 // Torrent.fields.error Torrent._ErrNone = 0 Torrent._ErrTrackerWarning = 1 Torrent._ErrTrackerError = 2 Torrent._ErrLocalError = 3 // TrackerStats' announceState Torrent._TrackerInactive = 0 Torrent._TrackerWaiting = 1 Torrent._TrackerQueued = 2 Torrent._TrackerActive = 3 // fields whose values never change and are always known Torrent._StaticFields = [ 'hashString', 'id' ] // fields whose values never change and are known upon constructon OR // when a magnet torrent finishes downloading its metadata Torrent._MetaDataFields = [ 'addedDate', 'comment', 'creator', 'dateCreated', 'isPrivate', 'name', 'totalSize', 'pieceCount', 'pieceSize' ] // torrent fields whose values change all the time Torrent._DynamicFields = [ 'desiredAvailable', 'downloadDir', 'downloadedEver', 'error', 'errorString', 'eta', 'haveUnchecked', 'haveValid', 'isFinished', 'leftUntilDone', 'metadataPercentComplete', 'peers', 'peersConnected', 'peersGettingFromUs', 'peersSendingToUs', 'queuePosition', 'rateDownload', 'rateUpload', 'recheckProgress', 'seedRatioLimit', 'seedRatioMode', 'sizeWhenDone', 'status', 'trackerStats', 'uploadedEver', 'uploadRatio', 'webseedsSendingToUs' ] /*** **** **** Methods **** ***/ Torrent.prototype = { initialize: function(controller, data) { this.fields = {} this._files = [] // these fields are set in the ctor and never change for(var i=0, key; key=Torrent._StaticFields[i]; ++i) if(key in data) this.fields[key] = data[key] this.initMetaData(data) this._trackerStats = this.buildTrackerStats(data.trackerStats) this.refresh(data) }, buildTrackerStats: function(trackerStats) { result = [] for(var i=0, tracker; tracker=trackerStats[i]; ++i) { tier = result[tracker.tier] || [] tier.push(tracker) result[tracker.tier] = tier } return result }, initMetaData: function(data) { var f = this.fields // populate the metadata fields for(var i=0, key; key=Torrent._MetaDataFields[i]; ++i) { if(key in data) { f[key] = data[key] if(key === 'name') f.collatedName = data.name.toLowerCase() } } // populate the files array if(data.files) { for(var i=0, row; row=data.files[i]; ++i) { this._files[i] = { 'index': i, 'torrent': this, 'length': row.length, 'name': row.name } } } }, refreshMetaData: function(data) { this.initMetaData(data) this.fireDataChanged() }, refresh: function(data) { // FIXME: unnecessary coupling... this should be handled by transmission.js if(this.needsMetaData() && (data.metadataPercentComplete >= 1)) transmission.refreshMetaData([ this.getId() ]) var f = this.fields // refresh the dynamic fields for(var i=0, key; key=Torrent._DynamicFields[i]; ++i) if(key in data) f[key] = data[key] this._trackerStats = this.buildTrackerStats(data.trackerStats) if (data.fileStats) this.refreshFiles(data) this.fireDataChanged() }, refreshFiles: function(data) { for(var i=0; i 0 || this.getPeersSendingToUs() > 0 || this.getWebseedsSendingToUs() > 0 || this.isChecking() break case Prefs._FilterSeeding: pass = (s === Torrent._StatusSeed) || (s === Torrent._StatusSeedWait) break case Prefs._FilterDownloading: pass = (s === Torrent._StatusDownload) || (s === Torrent._StatusDownloadWait) break case Prefs._FilterPaused: pass = this.isStopped() break case Prefs._FilterFinished: pass = this.isFinished() break default: pass = true break } if(!pass) return false if(!search || !search.length) return pass return this.getCollatedName().indexOf(search.toLowerCase()) !== -1 } } /*** **** **** SORTING **** ***/ Torrent.compareById = function(ta, tb) { return ta.getId() - tb.getId() } Torrent.compareByName = function(ta, tb) { return ta.getCollatedName().compareTo(tb.getCollatedName()) || Torrent.compareById(ta, tb) } Torrent.compareByQueue = function(ta, tb) { return ta.getQueuePosition() - tb.getQueuePosition() } Torrent.compareByAge = function(ta, tb) { var a = ta.getDateAdded() var b = tb.getDateAdded() return (b - a) || Torrent.compareByQueue(ta, tb) } Torrent.compareByState = function(ta, tb) { var a = ta.getStatus() var b = tb.getStatus() return (b - a) || Torrent.compareByQueue(ta, tb) } Torrent.compareByActivity = function(ta, tb) { var a = ta.getActivity() var b = tb.getActivity() return (a - b) || Torrent.compareByState(ta, tb) } Torrent.compareByRatio = function(a, b) { var a = Math.ratio(ta.getUploadedEver(), ta.getDownloadedEver()) var b = Math.ratio(tb.getUploadedEver(), tb.getDownloadedEver()) return (a - b) || Torrent.compareByState(ta, tb) } Torrent.compareByProgress = function(ta, tb) { var a = ta.getPercentDone() var b = tb.getPercentDone() return (a - b) || Torrent.compareByRatio(ta, tb) } /** * @param torrents an array of Torrent objects * @param sortMethod one of Prefs._SortBy* * @param sortDirection Prefs._SortAscending or Prefs._SortDescending */ Torrent.sortTorrents = function(torrents, sortMethod, sortDirection) { switch(sortMethod) { case Prefs._SortByActivity: torrents.sort(this.compareByActivity) break case Prefs._SortByAge: torrents.sort(this.compareByAge) break case Prefs._SortByQueue: torrents.sort(this.compareByQueue) break case Prefs._SortByProgress: torrents.sort(this.compareByProgress) break case Prefs._SortByState: torrents.sort(this.compareByState) break case Prefs._SortByName: torrents.sort(this.compareByName) break case Prefs._SortByRatio: torrents.sort(this.compareByRatio) break default: console.warn("unknown sort method: " + sortMethod) break } if(sortDirection === Prefs._SortDescending) torrents.reverse() return torrents }