Example #1
0
    def start(self):
        # keep track of running time.
        self.status.start_time = time.time()
        self.running = True

        # Generate a list of segments and build our queue.
        for file in self.nzb.files:
            for seg in file.segments:
                self.segment_list.append(seg.msgid)
                self.segment_queue.append(seg)

        # start the connections.
        for a in range(0, self.nntpConnections):
            thread = NNTPConnection(
                a,
                self.nntpServer,
                self.nntpPort,
                self.nntpUser,
                self.nntpPassword,
                self.nntpSSL,
                self.nextSeg,
                self.segComplete,
                self.segFailed,
                self.threadStopped,
            )
            self.threads.append(thread)
            self.connection_count += 1
            thread.start()

        # start the article decoder.
        self.articleDecoder = ArticleDecoder(
            self.decodeNextSeg,
            self.save_to,
            self.cache_path,
            self.decodeFinished,
            self.decodeSuccess,
            self.decodeFailed,
            self.assemblyStatus,
        )
        self.articleDecoder.start()
Example #2
0
class NZBClient:
    def __init__(
        self,
        nzbFile,
        save_to,
        nntpServer,
        nntpPort,
        nntpUser=None,
        nntpPassword=None,
        nntpSSL=False,
        nntpConnections=5,
        cache_path="",
    ):

        # Settings
        self.save_to = save_to
        self.nntpServer = nntpServer
        self.nntpUser = nntpUser
        self.nntpPort = nntpPort
        self.nntpPassword = nntpPassword
        self.nntpSSL = nntpSSL
        self.nntpConnections = nntpConnections
        self.threads = []
        self.running = False

        # setup our cache folder.
        self.cache_path = cache_path
        if self.cache_path == "":
            self.cache_path = "packages/dlmanager/cache/"
        self.clearCache()

        # ensure both directorys exist
        mt.utils.mkdir(self.save_to)
        mt.utils.mkdir(self.cache_path)

        # Open the NZB, get this show started.
        realFile = urllib.urlopen(nzbFile)
        self.nzb = NZBParser.parse(realFile)
        self.all_decoded = False
        self.connection_count = 0

        # used to track status.
        self.status = StatusReport()
        self.status.file_name = nzbFile
        self.status.total_bytes = self.nzb.size

        # Segment tracking.
        self.cache = []
        self.segment_list = []
        self.segments_finished = []
        self.segments_aborted = []

        # Queues.
        self.segment_queue = []
        self.failed_queue = []

        # Used to track the speed.
        self.speedTime = 0
        self.speedCounter = 0

    def start(self):
        # keep track of running time.
        self.status.start_time = time.time()
        self.running = True

        # Generate a list of segments and build our queue.
        for file in self.nzb.files:
            for seg in file.segments:
                self.segment_list.append(seg.msgid)
                self.segment_queue.append(seg)

        # start the connections.
        for a in range(0, self.nntpConnections):
            thread = NNTPConnection(
                a,
                self.nntpServer,
                self.nntpPort,
                self.nntpUser,
                self.nntpPassword,
                self.nntpSSL,
                self.nextSeg,
                self.segComplete,
                self.segFailed,
                self.threadStopped,
            )
            self.threads.append(thread)
            self.connection_count += 1
            thread.start()

        # start the article decoder.
        self.articleDecoder = ArticleDecoder(
            self.decodeNextSeg,
            self.save_to,
            self.cache_path,
            self.decodeFinished,
            self.decodeSuccess,
            self.decodeFailed,
            self.assemblyStatus,
        )
        self.articleDecoder.start()

    def getStatus(self):
        return self.status

    # Article Decoder - Next segment.
    def decodeNextSeg(self):
        # if we're not running send an instant kill switch.
        if not self.running:
            return -1

        # try to grab a segment from the cache to decode.
        seg = None
        try:
            seg = self.cache.pop()
        except:
            pass

        if (seg == None) and (self.all_decoded):
            return -1
        return seg

    # Article Decoder - Decoded all segments.
    def decodeFinished(self):
        self.status.completed = True

    # Article Decoder - Decode success.
    def decodeSuccess(self, seg):
        self.status.current_bytes += seg.size
        self.segments_finished.append(seg.msgid)
        if (len(self.segments_finished) + len(self.segments_aborted)) >= len(self.segment_list):
            self.all_decoded = True

    # Article Decoder - Decode failed.
    def decodeFailed(self, seg):
        if seg == None:
            return
        mt.log.debug("Segment failed to decode: " + seg.msgid)
        self.segFailed(seg)

    # Article Decoder - Assembly Status.
    def assemblyStatus(self, percent):
        self.status.assembly = True
        self.status.assembly_percent = percent

    # NNTP Connection - Thread stopped.
    def threadStopped(self, thread_num):
        self.connection_count -= 1

    # NNTP Connection - Segment completed.
    def segComplete(self, seg):
        if seg == None:
            return

        if seg.data:
            data_size = len("".join(seg.data))

            current_time = time.time()
            if (current_time - self.speedTime) > 1:
                self.status.kbps = self.speedCounter
                self.speedCounter = 0
                self.speedTime = current_time
            else:
                self.speedCounter += data_size / 1024

            self.cache.append(seg)
        # mt.log.debug("Segment Complete: " + seg.msgid)

    # NNTP Connection - Download of segment failed.
    def segFailed(self, seg):
        if seg == None:
            return

        if seg.aborted():
            mt.log.error("Segment Aborted: " + seg.msgid + " after " + str(seg.retries) + " attempts.")
            self.segments_aborted.append(seg.msgid)
            seg.data = []
            if (len(self.segments_finished) + len(self.segments_aborted)) >= len(self.segment_list):
                self.all_decoded = True
            return

        seg.retries += 1

        mt.log.error("Segment Failed: " + seg.msgid + " Attempt #" + str(seg.retries) + ".")
        self.failed_queue.append(seg)

    # NNTP Connection - Next Segment
    def nextSeg(self):
        # if we're not running send an instant kill switch.
        if not self.running:
            return -1

        # try to get a segment from main queue or failed queue.
        queue_empty = False
        seg = None
        try:
            seg = self.segment_queue.pop()
        except:
            try:
                seg = self.failed_queue.pop()
            except:
                queue_empty = True
                pass
            pass

        # We're all outta segments, if they're done decoding, kill the threads.
        if (queue_empty) and (self.all_decoded):
            return -1

        return seg

    # empty the cache of any files.
    def clearCache(self):
        mt.utils.rmdir(self.cache_path)

    def stop(self):
        self.running = False
        self.articleDecoder.stop()
        for thread in self.threads:
            thread.stop()
        self.clearCache()