def test_addTorrentsToQueue_torrentExists_deleteTorrent(self): self.removeDatabase() testTorrent = Torrents.TV( torrentTitle='some.show.s01e01.720p.junk.here', url='http://testurl.com/test') testTorrent2 = Torrents.TV( torrentTitle='some.show.s01e02.720p.junk.here', url='http://testurl.com/test2') torrentQueue = Queue() torrentQueue.append(testTorrent) dbObject = Databases( dbType="SQLITE3", databaseSettings={'databaseLocation': self.testDatabaseFile}) dbObject.addTorrentsToQueue(torrentQueue) self.assertTrue(dbObject.torrentExists(testTorrent)) dbObject.updateHashString({'hashString': 'abc123'}, {'url': 'http://testurl.com/test'}) self.assertTrue( len( dbObject.getTorrentInfo(hashString='abc123', selectors=['torrentTitle', 'url'])) == 1) torrentQueue = Queue() torrentQueue.append(testTorrent2) dbObject.addTorrentsToQueue(torrentQueue) self.assertTrue(dbObject.getQueuedTorrentsCount() == 2) self.assertTrue(len(dbObject.getQueuedTorrents()) == 2) dbObject.deleteTorrent(url='http://testurl.com/test') self.assertFalse(dbObject.torrentExists(testTorrent)) self.assertTrue(dbObject.getQueuedTorrentsCount() == 1) dbObject.deleteTorrent(url='http://testurl.com/test2') self.assertFalse(dbObject.torrentExists(testTorrent)) self.assertTrue(dbObject.getQueuedTorrentsCount() == 0) self.removeDatabase()
class TorrentClient(): ''' This should be a generic class that torrent clients can extend from and use as a roadmap all the functions in here need to be defined in the client module in order for it to work TODO: instantiate a class of the given client type based on the config setup. We will also need to return it when this is instantiated. ''' logger = logging.getLogger(__name__) # Setup the database object database = None defaultDatabaseType = settings['database']['defaultDatabaseEngine'] client = None def __init__(self): # Setup the database object self.database = Databases(dbType=self.defaultDatabaseType) self.logger.info('TransmissionClient INIT') if settings['client']['type'] == 'transmission': self.logger.debug('TorrentClient Setup') self.client = Transmission.Client() else: self.logger.info('TorrentClient Not Defined') raise ValueError('Torrent client type not defined!') def __updateHashString(self, data=None, where=None): ''' TODO: This should be used instead of the function in the Transmission Class Updates the hash string for a torrent in the database Takes: using - A list of database properties that are used to identify the torrent to be updated update - Field to update in the database ''' self.database.updateHashString(data=data, where=where) def updateQueue(self): ''' Updates the class variable queue with the latest torrent queue info Returns: Tuple (transmissionResponseCode, httpResponseCode) ''' return self.client.updateQueue() def getQueue(self): ''' Returns: List [torrents] ''' return self.client.getQueue() def verifyTorrent(self, hashString=None): ''' Verifies a corrupted torrent Takes: hashString - Hash of the specific torrent to remove Returns: bool True is action completed ''' return self.client.verifyTorrent(hashString=hashString) def stopTorrent(self, hashString=None): ''' Stops a torrent Takes: hashString - Hash of the specific torrent to remove Returns: bool True is action completed ''' return self.client.stopTorrent(hashString=hashString) def startTorrent(self, hashString=None): ''' Starts a torrent Takes: hashString - Hash of the specific torrent to remove Returns: bool True is action completed ''' return self.client.startTorrent(hashString=hashString) def removeBadTorrent(self, hashString=None, reason='No Reason Given'): ''' Removes a torrent from both transmission and the database this should be called when there is a bad torrent. Takes: hashString - Hash of the specific torrent to remove ''' # Remove the torrent from the client self.client.removeTorrent(hashString=hashString, deleteData=True, reason=reason) # Remove the torrent from the DB self.database.deleteTorrent(hashString=hashString, reason=reason) def removeDupeTorrent(self, hashString=None, url=None): ''' Removes a duplicate torrent from transmission if it does not exist and is in the database but does in transmission. This is checked via the url and the bashString. TODO: Handle hash collision Takes: hashString - Hash of the specific torrent to remove url - Url of the torrent we are adding ''' if not self.database.torrentExists(hashString=hashString, url=url): # Remove the torrent from the client self.removeTorrent(hashString=hashString, deleteData=False, reason='Duplicate Torrent') return True else: # Remove the torrent from the DB # TODO: Perhaps this should be changed to just mark the torrent as added # or blacklisted self.database.deleteTorrent(url=url, reason='Duplicate Torrent') return True def removeTorrent(self, hashString=None, deleteData=False, reason='No Reason Given'): ''' Removes a torrent from transmission Takes: hashString - Hash of the specific torrent to remove deleteData - bool, tells if the torrent data should be removed TODO: if hashString is not specified then we should remove the torrent that has the longest time since active. Returns: bool True is action completed ''' return self.client.removeTorrent(hashString=hashString, deleteData=deleteData, reason=reason) def deleteTorrent(self, hashString=None, reason='No Reason Given'): ''' Removes a torrent from transmission and deletes the associated data Takes: hashString - Hash of the specific torrent to remove TODO: if hashString is not specified then we should remove the torrent that has the longest time since active. Returns: bool True is action completed ''' # Logging is skipped here and put into the removeTorrent function to prevent # duplicate logging return self.client.removeTorrent(hashString=hashString, deleteData=True, reason=reason) def addTorrentURL(self, url=None, destination=settings['files']['defaultTorrentLocation']): ''' Attempts to load the torrent at the given url into transmission Takes: url - url of the torrent file to be added destination - where the torrent should be saved Returns: bool True is action completed successfully ''' self.logger.info('TorrentClient adding torrent1') result, response = self.client.addTorrentURL(url=url, destination=destination) self.logger.info('T {0}'.format(result)) self.logger.info('T {0}'.format(response)) self.logger.info('TorrentClient responded with ({0}, {1})'.format( result, response)) if result == 0: # Get Current Time sinceEpoch = int(time.time()) # update hash, addedOn, added in DB self.logger.info('TorrentClient added torrent') self.__updateHashString(where={'url': url}, data={ 'hashString': response, 'addedOn': sinceEpoch, 'added': 1 }) time.sleep(10) return True elif result == 1: # Torrent is broken so lets delete it from the DB, this leaves the opportunity # for the torrent to later be added again self.logger.info('TorrentClient duplicate torrent') self.removeDupeTorrent(url=url, hashString=response) time.sleep(10) return False elif result == 2: self.logger.info('TorrentClient bad torrent, but we can retry') self.database.deleteTorrent(url=url, reason=response) return False elif result == 3: self.logger.info('TorrentClient bad torrent, so blacklist it') self.database.addBlacklistedTorrent(url=url, reason=response) self.database.deleteTorrent(url=url, reason=response) return False def getSlowestSeeds(self, num=None): ''' Look for the slowest seeding torrents, slowest first Takes: num - Int, the number of torrent objects to return ''' return self.client.getSlowestSeeds(num=num) def getDormantSeeds(self, num=None): ''' Looks for a seeding torrent with the longest time since active, returns torrents, oldest first ''' return self.client.getDormantSeeds(num=num) def getDownloading(self, num=None): ''' Returns a list of torrents that are downloading Takes: num - Int, the number of torrents to return ''' return self.client.getDownloading(num=num) def getSeeding(self, num=None): ''' Returns a list of torrents that are Seeding Takes: num - Int, the number of torrents to return ''' return self.client.getSeeding(num=num) def getFinishedSeeding(self, num=None): ''' Returns a list of torrents that are finished seeding Takes: num - Int, the number of torrents to return ''' return self.client.getFinishedSeeding(num=num) def restart(self): return self.client.restart() def start(self): return self.client.start() def stop(self): return self.client.stop()
class TorrentClient(object): ''' This should be a generic class that torrent clients can extend from and use as a roadmap all the functions in here need to be defined in the client module in order for it to work TODO: instantiate a class of the given client type based on the config setup. We will also need to return it when this is instantiated. ''' def __init__(self, settings={}): # Setup the database object self.torrentDB = Databases(flannelfox.settings['database']['defaultDatabaseEngine']) self.logger = logging.getLogger(__name__) self.logger.info("TransmissionClient INIT") if settings['type'] == "transmission": self.logger.info("TorrentClient Setup") self.client = Transmission.Client(settings=settings) else: self.logger.info("TorrentClient Not Defined") raise ValueError("Torrent client type not defined!") def __updateHashString(self, using=None, update=None): ''' TODO: This should be used instead of the function in the Transmission Class Updates the hash string for a torrent in the database Takes: using - A list of database properties that are used to identify the torrent to be updated update - Field to update in the database ''' self.torrentDB.updateHashString(update=update, using=using) def updateQueue(self): ''' Updates the class variable queue with the latest torrent queue info Returns: Tuple (transmissionResponseCode, httpResponseCode) ''' return self.client.updateQueue() def getQueue(self): ''' Returns: List [torrents] ''' return self.client.getQueue() def verifyTorrent(self,hashString=None): ''' Verifies a corrupted torrent Takes: hashString - Hash of the specific torrent to remove Returns: bool True is action completed ''' return self.client.verifyTorrent(hashString=hashString) def stopTorrent(self,hashString=None): ''' Stops a torrent Takes: hashString - Hash of the specific torrent to remove Returns: bool True is action completed ''' return self.client.stopTorrent(hashString=hashString) def startTorrent(self, hashString=None): ''' Starts a torrent Takes: hashString - Hash of the specific torrent to remove Returns: bool True is action completed ''' return self.client.startTorrent(hashString=hashString) def removeBadTorrent(self, hashString=None, reason='No Reason Given'): ''' Removes a torrent from both transmission and the database this should be called when there is a bad torrent. Takes: hashString - Hash of the specific torrent to remove ''' # Remove the torrent from the client self.client.removeTorrent(hashString=hashString, deleteData=True, reason=reason) # Remove the torrent from the DB self.torrentDB.deleteTorrent(hashString=hashString, reason=reason) def removeDupeTorrent(self, hashString=None, url=None): ''' Removes a duplicate torrent from transmission if it does not exist and is in the database but does in transmission. This is checked via the url and the bashString. TODO: Handle hash collision Takes: hashString - Hash of the specific torrent to remove url - Url of the torrent we are adding ''' if not self.torrentDB.torrentExists(hashString=hashString, url=url): # Remove the torrent from the client self.removeTorrent(hashString=hashString, deleteData=False, reason='Duplicate Torrent') return True else: # Remove the torrent from the DB # TODO: Perhaps this should be changed to just mark the torrent as added # or blacklisted self.torrentDB.deleteTorrent(url=url, reason='Duplicate Torrent') return True def removeTorrent(self, hashString=None, deleteData=False, reason='No Reason Given'): ''' Removes a torrent from transmission Takes: hashString - Hash of the specific torrent to remove deleteData - bool, tells if the torrent data should be removed TODO: if hashString is not specified then we should remove the torrent that has the longest time since active. Returns: bool True is action completed ''' return self.client.removeTorrent(hashString=hashString, deleteData=deleteData, reason=reason) def deleteTorrent(self, hashString=None, reason='No Reason Given'): ''' Removes a torrent from transmission and deletes the associated data Takes: hashString - Hash of the specific torrent to remove TODO: if hashString is not specified then we should remove the torrent that has the longest time since active. Returns: bool True is action completed ''' # Logging is skipped here and put into the removeTorrent function to prevent # duplicate logging return self.client.removeTorrent(hashString=hashString, deleteData=True, reason=reason) def addTorrentURL(self, url=None, destination=flannelfox.settings['files']['defaultTorrentLocation']): ''' Attempts to load the torrent at the given url into transmission Takes: url - url of the torrent file to be added destination - where the torrent should be saved Returns: bool True is action completed successfully ''' self.logger.info("TorrentClient adding torrent") result, response = self.client.addTorrentURL(url=url, destination=destination) self.logger.info("TorrentClient responded with ({0}, {1})".format(result, response)) if result == 0: # Get Current Time sinceEpoch = int(time.time()) # update hash, addedOn, added in DB self.logger.info("TorrentClient added torrent") self.__updateHashString(using={u"url":url}, update={u"hashString":response, u"addedOn":sinceEpoch, u"added":1}) time.sleep(10) return True elif result == 1: # Torrent is broken so lets delete it from the DB, this leaves the opportunity # for the torrent to later be added again self.logger.info("TorrentClient duplicate torrent") self.removeDupeTorrent(url=url, hashString=response) time.sleep(10) return False elif result == 2: self.logger.info("TorrentClient bad torrent, but we can retry") self.torrentDB.deleteTorrent(url=url, reason=response) return False elif result == 3: self.logger.info("TorrentClient bad torrent, so blacklist it") self.torrentDB.addBlacklistedTorrent(url=url, reason=response) self.torrentDB.deleteTorrent(url=url, reason=response) return False def getSlowestSeeds(self, num=None): ''' Look for the slowest seeding torrents, slowest first Takes: num - Int, the number of torrent objects to return ''' return self.client.getSlowestSeeds(num=num) def getDormantSeeds(self, num=None): ''' Looks for a seeding torrent with the longest time since active, returns torrents, oldest first ''' return self.client.getDormantSeeds(num=num) def getDownloading(self, num=None): ''' Returns a list of torrents that are downloading Takes: num - Int, the number of torrents to return ''' return self.client.getDownloading(num=num) def getSeeding(self, num=None): ''' Returns a list of torrents that are Seeding Takes: num - Int, the number of torrents to return ''' return self.client.getSeeding(num=num) def getFinishedSeeding(self, num=None): ''' Returns a list of torrents that are finished seeding Takes: num - Int, the number of torrents to return ''' return self.client.getFinishedSeeding(num=num) def restart(self): return self.client.restart() def start(self): return self.client.start() def stop(self): return self.client.stop()
class Queue(): ''' Used to Track a list of torrents and interact with the torrent database ''' # Setup the database object database = None defaultDatabaseType = settings['database']['defaultDatabaseEngine'] def __init__(self, *args): self.elements = list(*args) self.database = Databases(dbType=self.defaultDatabaseType) def __getitem__(self, idx): # Ensure the index is in the correct range if idx < 0 or idx >= len(self.elements): raise IndexError('Index out of range') return self.elements[idx] def __setitem__(self, idx, torrent): self.elements[idx] = torrent # Ensure the value was taken if self.elements[idx] == torrent: return 0 else: return -1 def __len__(self): return len(self.elements) def __iter__(self): return iter(self.elements) def __contains__(self, element): return element in self.elements def databaseTorrentExists(self, torrent): return self.database.torrentExists(torrent=torrent) #return False def databaseTorrentBlacklisted(self, torrent): return self.database.torrentBlacklisted(torrent.get('url', '')) #return False def append(self, torrent): # Check and see if the value already exists in elements if torrent in self.elements: return -1 # Check and see if the value already exists in DB elif self.databaseTorrentExists(torrent): return -1 # Ensure it is not Blacklisted elif self.databaseTorrentBlacklisted(torrent): return -1 # Append the value to elements else: self.elements.append(torrent) # Ensure the value was taken if torrent in self.elements: return 0 else: return -1 def writeToDB(self): self.database.addTorrentsToQueue(self.elements) def __str__(self): out = '' for element in self.elements: out += u'{0}\n'.format(element) return out