Beispiel #1
0
def bad_fetch(nzo, url, msg='', content=False):
    """ Create History entry for failed URL Fetch
        msg : message to be logged
        retry : make retry link in history
        content : report in history that cause is a bad NZB file
    """
    if msg:
        msg = unicoder(msg)
    else:
        msg = ''

    nzo.status = Status.FAILED

    if url:
        nzo.filename = url
        nzo.final_name = url.strip()

    if content:
        # Bad content
        msg = T('Unusable NZB file')
    else:
        # Failed fetch
        msg = T('URL Fetching failed; %s') % msg

    nzo.fail_msg = msg

    growler.send_notification(
        T('URL Fetching failed; %s') % '', '%s\n%s' % (msg, url), 'other')
    if cfg.email_endjob() > 0:
        emailer.badfetch_mail(msg, url)

    NzbQueue.do.remove(nzo.nzo_id, add_to_history=True)
Beispiel #2
0
def bad_fetch(nzo, url, msg='', content=False):
    """ Create History entry for failed URL Fetch
        msg : message to be logged
        retry : make retry link in history
        content : report in history that cause is a bad NZB file
    """
    if msg:
        msg = unicoder(msg)
    else:
        msg = ''

    nzo.status = Status.FAILED

    if url:
        nzo.filename = url
        nzo.final_name = url.strip()

    if content:
        # Bad content
        msg = T('Unusable NZB file')
    else:
        # Failed fetch
        msg = T('URL Fetching failed; %s') % msg

    nzo.fail_msg = msg

    growler.send_notification(T('URL Fetching failed; %s') % '', '%s\n%s' % (msg, url), 'other')
    if cfg.email_endjob() > 0:
        emailer.badfetch_mail(msg, url)

    NzbQueue.do.remove(nzo.nzo_id, add_to_history=True)
Beispiel #3
0
    def add(self, nzo, save=True, quiet=False):
        assert isinstance(nzo, NzbObject)
        if not nzo.nzo_id:
            nzo.nzo_id = sabnzbd.get_new_id('nzo', nzo.workpath,
                                            self.__nzo_table)

        # If no files are to be downloaded anymore, send to postproc
        if not nzo.files and not nzo.futuretype:
            self.end_job(nzo)
            return ''

        # Reset try_lists
        nzo.reset_try_list()
        self.reset_try_list()

        if nzo.nzo_id:
            nzo.deleted = False
            priority = nzo.priority
            if sabnzbd.scheduler.analyse(False, priority):
                nzo.status = Status.PAUSED

            self.__nzo_table[nzo.nzo_id] = nzo
            if priority > HIGH_PRIORITY:
                # Top and repair priority items are added to the top of the queue
                self.__nzo_list.insert(0, nzo)
            elif priority == LOW_PRIORITY:
                self.__nzo_list.append(nzo)
            else:
                # for high priority we need to add the item at the bottom
                # of any other high priority items above the normal priority
                # for normal priority we need to add the item at the bottom
                # of the normal priority items above the low priority
                if self.__nzo_list:
                    pos = 0
                    added = False
                    for position in self.__nzo_list:
                        if position.priority < priority:
                            self.__nzo_list.insert(pos, nzo)
                            added = True
                            break
                        pos += 1
                    if not added:
                        # if there are no other items classed as a lower priority
                        # then it will be added to the bottom of the queue
                        self.__nzo_list.append(nzo)
                else:
                    # if the queue is empty then simple append the item to the bottom
                    self.__nzo_list.append(nzo)
            if save:
                self.save(nzo)

            if not (quiet or nzo.status in ('Fetching', )):
                growler.send_notification(T('NZB added to queue'),
                                          nzo.filename, 'download')

        if cfg.auto_sort():
            self.sort_by_avg_age()
        return nzo.nzo_id
Beispiel #4
0
def parring(nzo, workdir):
    """ Perform par processing. Returns: (par_error, re_add)
    """
    filename = nzo.final_name
    growler.send_notification(T('Post-processing'), nzo.final_name, 'pp')
    logging.info('Par2 check starting on %s', filename)

    ## Collect the par files
    if nzo.partable:
        par_table = nzo.partable.copy()
    else:
        par_table = {}
    repair_sets = par_table.keys()

    re_add = False
    par_error = False

    if repair_sets:

        for set_ in repair_sets:
            logging.info("Running repair on set %s", set_)
            parfile_nzf = par_table[set_]
            need_re_add, res = par2_repair(parfile_nzf, nzo, workdir, set_)
            if need_re_add:
                re_add = True
            else:
                par_error = par_error or not res

        if re_add:
            logging.info('Readded %s to queue', filename)
            nzo.priority = REPAIR_PRIORITY
            sabnzbd.nzbqueue.add_nzo(nzo)
            sabnzbd.downloader.Downloader.do.resume_from_postproc()

        logging.info('Par2 check finished on %s', filename)

    if (par_error and not re_add) or not repair_sets:
        # See if alternative SFV check is possible
        if cfg.sfv_check():
            sfvs = globber(workdir, '*.sfv')
        else:
            sfvs = None
        if sfvs:
            par_error = False
            nzo.set_unpack_info('Repair', T('Trying SFV verification'))
            for sfv in sfvs:
                if not sfv_check(sfv):
                    nzo.set_unpack_info('Repair', T('Some files failed to verify against "%s"') % unicoder(os.path.basename(sfv)))
                    par_error = True
            if not par_error:
                nzo.set_unpack_info('Repair', T('Verified successfully using SFV files'))
        elif not repair_sets:
            logging.info("No par2 sets for %s", filename)
            nzo.set_unpack_info('Repair', T('[%s] No par2 sets') % unicoder(filename))

    if not par_error:
        verified_flag_file(workdir, create=True)
    return par_error, re_add
Beispiel #5
0
def parring(nzo, workdir):
    """ Perform par processing. Returns: (par_error, re_add)
    """
    assert isinstance(nzo, sabnzbd.nzbstuff.NzbObject)
    filename = nzo.final_name
    growler.send_notification(T('Post-processing'), nzo.final_name, 'pp')
    logging.info('Par2 check starting on %s', filename)

    ## Get verification status of sets
    verified = sabnzbd.load_data(VERIFIED_FILE, nzo.workpath,
                                 remove=False) or {}

    ## Collect the par files
    if nzo.partable:
        par_table = nzo.partable.copy()
    else:
        par_table = {}
    repair_sets = par_table.keys()

    re_add = False
    par_error = False

    if repair_sets:
        for setname in repair_sets:
            if cfg.ignore_samples() > 0 and 'sample' in setname.lower():
                continue
            if not verified.get(setname, False):
                logging.info("Running repair on set %s", setname)
                parfile_nzf = par_table[setname]
                if not os.path.exists(
                        os.path.join(nzo.downpath, parfile_nzf.filename)):
                    continue
                need_re_add, res = par2_repair(parfile_nzf, nzo, workdir,
                                               setname)
                re_add = re_add or need_re_add
                if not res and not need_re_add and cfg.sfv_check():
                    res = try_sfv_check(nzo, workdir, setname)
                verified[setname] = res
                par_error = par_error or not res
    else:
        logging.info("No par2 sets for %s", filename)
        nzo.set_unpack_info('Repair',
                            T('[%s] No par2 sets') % unicoder(filename))
        if cfg.sfv_check():
            par_error = not try_sfv_check(nzo, workdir, '')
            verified[''] = not par_error

    if re_add:
        logging.info('Readded %s to queue', filename)
        if nzo.priority != TOP_PRIORITY:
            nzo.priority = REPAIR_PRIORITY
        sabnzbd.nzbqueue.add_nzo(nzo)
        sabnzbd.downloader.Downloader.do.resume_from_postproc()

    sabnzbd.save_data(verified, VERIFIED_FILE, nzo.workpath)

    logging.info('Par2 check finished on %s', filename)
    return par_error, re_add
Beispiel #6
0
 def versionUpdate(self):
     try:
         if sabnzbd.NEW_VERSION and self.version_notify:
             #logging.info("[osx] New Version : %s" % (sabnzbd.NEW_VERSION))
             new_release, new_rel_url = sabnzbd.NEW_VERSION.split(';')
             growler.send_notification("SABnzbd","%s : %s" % (T('New release available'), new_release), 'other')
             self.version_notify = 0
     except :
         logging.info("[osx] versionUpdate Exception %s" % (sys.exc_info()[0]))
Beispiel #7
0
 def versionUpdate(self):
     try:
         if sabnzbd.NEW_VERSION and self.version_notify:
             #logging.info("[osx] New Version : %s" % (sabnzbd.NEW_VERSION))
             new_release, new_rel_url = sabnzbd.NEW_VERSION.split(';')
             growler.send_notification("SABnzbd","%s : %s" % (T('New release available'), new_release), 'other')
             self.version_notify = 0
     except :
         logging.info("[osx] versionUpdate Exception %s" % (sys.exc_info()[0]))
Beispiel #8
0
    def add(self, nzo, save=True, quiet=False):
        assert isinstance(nzo, NzbObject)
        if not nzo.nzo_id:
            nzo.nzo_id = sabnzbd.get_new_id('nzo', nzo.workpath, self.__nzo_table)

        # If no files are to be downloaded anymore, send to postproc
        if not nzo.files and not nzo.futuretype:
            self.end_job(nzo)
            return ''

        # Reset try_lists
        nzo.reset_try_list()
        self.reset_try_list()

        if nzo.nzo_id:
            nzo.deleted = False
            priority = nzo.priority
            if sabnzbd.scheduler.analyse(False, priority):
                nzo.status = Status.PAUSED

            self.__nzo_table[nzo.nzo_id] = nzo
            if priority > HIGH_PRIORITY:
                # Top and repair priority items are added to the top of the queue
                self.__nzo_list.insert(0, nzo)
            elif priority == LOW_PRIORITY:
                self.__nzo_list.append(nzo)
            else:
                # for high priority we need to add the item at the bottom
                # of any other high priority items above the normal priority
                # for normal priority we need to add the item at the bottom
                # of the normal priority items above the low priority
                if self.__nzo_list:
                    pos = 0
                    added = False
                    for position in self.__nzo_list:
                        if position.priority < priority:
                            self.__nzo_list.insert(pos, nzo)
                            added = True
                            break
                        pos += 1
                    if not added:
                        # if there are no other items classed as a lower priority
                        # then it will be added to the bottom of the queue
                        self.__nzo_list.append(nzo)
                else:
                    # if the queue is empty then simple append the item to the bottom
                    self.__nzo_list.append(nzo)
            if save:
                self.save(nzo)

            if not (quiet or nzo.status in ('Fetching',)):
                growler.send_notification(T('NZB added to queue'), nzo.filename, 'download')

        if cfg.auto_sort():
            self.sort_by_avg_age()
        return nzo.nzo_id
Beispiel #9
0
def parring(nzo, workdir):
    """ Perform par processing. Returns: (par_error, re_add)
    """
    assert isinstance(nzo, sabnzbd.nzbstuff.NzbObject)
    filename = nzo.final_name
    growler.send_notification(T('Post-processing'), nzo.final_name, 'pp')
    logging.info('Par2 check starting on %s', filename)

    ## Get verification status of sets
    verified = sabnzbd.load_data(VERIFIED_FILE, nzo.workpath, remove=False) or {}

    ## Collect the par files
    if nzo.partable:
        par_table = nzo.partable.copy()
    else:
        par_table = {}
    repair_sets = par_table.keys()

    re_add = False
    par_error = False
    single = len(repair_sets) == 1

    if repair_sets:
        for setname in repair_sets:
            if cfg.ignore_samples() > 0 and 'sample' in setname.lower():
                continue
            if not verified.get(setname, False):
                logging.info("Running repair on set %s", setname)
                parfile_nzf = par_table[setname]
                if os.path.exists(os.path.join(nzo.downpath, parfile_nzf.filename)) or parfile_nzf.extrapars:
                    need_re_add, res = par2_repair(parfile_nzf, nzo, workdir, setname, single=single)
                    re_add = re_add or need_re_add
                    if not res and not need_re_add and cfg.sfv_check():
                        res = try_sfv_check(nzo, workdir, setname)
                    verified[setname] = res
                else:
                    continue
                par_error = par_error or not res
    else:
        logging.info("No par2 sets for %s", filename)
        nzo.set_unpack_info('Repair', T('[%s] No par2 sets') % unicoder(filename))
        if cfg.sfv_check():
            par_error = not try_sfv_check(nzo, workdir, '')
            verified[''] = not par_error

    if re_add:
        logging.info('Readded %s to queue', filename)
        if nzo.priority != TOP_PRIORITY:
            nzo.priority = REPAIR_PRIORITY
        sabnzbd.nzbqueue.add_nzo(nzo)
        sabnzbd.downloader.Downloader.do.resume_from_postproc()

    sabnzbd.save_data(verified, VERIFIED_FILE, nzo.workpath)

    logging.info('Par2 check finished on %s', filename)
    return par_error, re_add
Beispiel #10
0
 def pause(self, save=True):
     """ Pause the downloader, optionally saving admin """
     if not self.paused:
         self.paused = True
         logging.info("Pausing")
         growler.send_notification("SABnzbd", T('Paused'), 'download')
         if self.is_paused():
             BPSMeter.do.reset()
         if cfg.autodisconnect():
             self.disconnect()
         if save:
             sabnzbd.save_state()
Beispiel #11
0
 def pause(self, save=True):
     """ Pause the downloader, optionally saving admin """
     if not self.paused:
         self.paused = True
         logging.info("Pausing")
         growler.send_notification("SABnzbd", T('Paused'), 'download')
         if self.is_paused():
             BPSMeter.do.reset()
         if cfg.autodisconnect():
             self.disconnect()
         if save:
             sabnzbd.save_state()
Beispiel #12
0
    def run(self):
        """ Process the queue (including waits and retries) """
        from sabnzbd.nzbqueue import NzbQueue
        self.shutdown = False
        while not self.shutdown:
            time.sleep(5)
            (msgid, nzo) = self.queue.get()
            if self.shutdown or not msgid:
                break
            if nzo.wait and nzo.wait > time.time():
                self.grab(msgid, nzo)
                continue
            logging.debug("Popping msgid %s", msgid)

            filename, data, newzbin_cat, nzo_info = _grabnzb(msgid)
            if filename and data:
                filename = name_fixer(filename)

                pp = nzo.pp
                script = nzo.script
                cat = nzo.cat
                if cat == '*' or not cat:
                    cat = cat_convert(newzbin_cat)

                priority = nzo.priority
                nzbname = nzo.custom_name

                cat, pp, script, priority = cat_to_opts(cat, pp, script, priority)

                try:
                    sabnzbd.nzbqueue.insert_future_nzo(nzo, filename, msgid, data, pp=pp, script=script, cat=cat, priority=priority, nzbname=nzbname, nzo_info=nzo_info)
                    nzo.url = format_source_url(str(msgid))
                except:
                    logging.error(Ta('Failed to update newzbin job %s'), msgid)
                    logging.info("Traceback: ", exc_info = True)

                    NzbQueue.do.remove(nzo.nzo_id, False)
                msgid = None
            else:
                if filename:
                    self.grab(msgid, nzo, float(filename))
                else:
                    # Fatal error, give up on this one
                    bad_fetch(nzo, msgid, msg=nzo_info, retry=True)
                msgid = None

            if msgid:
                growler.send_notification(T('NZB added to queue'), filename, 'download')

        logging.debug('Stopping MSGIDGrabber')
Beispiel #13
0
 def applicationShouldTerminate_(self, sender):
     logging.info('[osx] application terminating')
     self.setMenuTitle("\n\n%s\n"% (T('Stopping...')))
     self.status_item.setHighlightMode_(NO)
     self.osx_icon = False
     logging.info('[osx] application stopping daemon')
     sabnzbd.halt()
     cherrypy.engine.exit()
     sabnzbd.SABSTOP = True
     try:
         growler.send_notification('SABnzbd', T('SABnzbd shutdown finished'), growler.NOTIFICATION['other'])
     except AttributeError:
         # Fails for the OSX binary
         pass
     logging.info('Leaving SABnzbd')
     sys.stderr.flush()
     sys.stdout.flush()
     return NSTerminateNow
Beispiel #14
0
 def applicationShouldTerminate_(self, sender):
     logging.info('[osx] application terminating')
     self.setMenuTitle("\n\n%s\n"% (T('Stopping...')))
     self.status_item.setHighlightMode_(NO)
     self.osx_icon = False
     logging.info('[osx] application stopping daemon')
     sabnzbd.halt()
     cherrypy.engine.exit()
     sabnzbd.SABSTOP = True
     try:
         growler.send_notification('SABnzbd', T('SABnzbd shutdown finished'), growler.NOTIFICATION['other'])
     except AttributeError:
         # Fails for the OSX binary
         pass
     logging.info('Leaving SABnzbd')
     sys.stderr.flush()
     sys.stdout.flush()
     return NSTerminateNow
Beispiel #15
0
def check_in(self):
    t = time.time()

    if t > self.end_of_day:
        # current day passed. get new end of day
        self.end_of_day = tomorrow(t) - 1.0

        if t > self.end_of_week:
            self.end_of_week = next_week(t) - 1.0

        if t > self.end_of_month:
            self.end_of_month = next_month(t) - 1.0


    check_time = t - (MIN * 5) # check every 5 mins

    if self.log_time < check_time:

        #logging.debug("log_time < check_time, so check IP")

        #time_tm = time.localtime(t)
        #log_time_tm = time.localtime(self.log_time)
        check_time_tm = time.localtime(check_time)

        #logging.debug("      time: %s", t)
        #logging.debug("  log_time: %s", self.log_time)
        #logging.debug("check_time: %s", check_time)
        
        #logging.debug("      time: %s", time.strftime("%a, %d %b %Y %H:%M:%S", time_tm ))
        #logging.debug("  log_time: %s", time.strftime("%a, %d %b %Y %H:%M:%S", log_time_tm ))
        #logging.debug("check_time: %s", time.strftime("%a, %d %b %Y %H:%M:%S", check_time_tm ))

        self.log_time = t

        fqn = os.uname()[1]
        url = 'http://automation.whatismyip.com/n09230945.asp'

        req = Request(url)

        try:
            response = urlopen(req) #urlopen(req, None, 10) for a 10s timeout

        except URLError, e:
            if hasattr(e, 'reason'):
                logging.debug( 'We failed to reach: %s', url)
                logging.debug( 'Reason: %s', e.reason)
            elif hasattr(e, 'code'):
                logging.debug( 'The server couldn\'t fulfill the request for %s', url)
                logging.debug( 'Error code: %s', e.code)
      
        else:
            logging.debug( 'NO URLError, read response')

            new_ext_ip = response.read()

            if check_ip(new_ext_ip):
                logging.info("Host %s, Checking in from IP#: %s ", fqn, new_ext_ip)

                if self.ext_ip == new_ext_ip:
                    logging.debug("IP address is UNCHANGED")
                    #tmpStr = "IP address NOT changed. Still: [%s]" % (self.ext_ip,)
                    #growler.send_notification("SABnzbd", tmpStr, 'other')

                else:
                    if self.ext_ip == "":
                        self.ext_ip = new_ext_ip
                    else:    
                        logging.info("IP address HAS CHANGED")
                        tmpStr = "IP address changed from %s to %s" % (self.ext_ip, new_ext_ip)
                        growler.send_notification("SABnzbd", tmpStr, 'other')
                        self.ext_ip = new_ext_ip
Beispiel #16
0
def process_job(nzo):
    """ Process one job """
    assert isinstance(nzo, sabnzbd.nzbstuff.NzbObject)
    start = time.time()

    # keep track of whether we can continue
    all_ok = True
    # keep track of par problems
    par_error = False
    # keep track of any unpacking errors
    unpack_error = False
    # Signal empty download, for when 'empty_postproc' is enabled
    empty = False
    nzb_list = []
    # These need to be initialized in case of a crash
    workdir_complete = ""
    postproc_time = 0  # @UnusedVariable -- pep8 bug?
    script_log = ""
    script_line = ""
    crash_msg = ""

    # Get the job flags
    nzo.save_attribs()
    flag_repair, flag_unpack, flag_delete = nzo.repair_opts
    # Normalize PP
    if flag_delete:
        flag_unpack = True
    if flag_unpack:
        flag_repair = True

    # Get the NZB name
    filename = nzo.final_name

    if cfg.allow_streaming() and not (flag_repair or flag_unpack or flag_delete):
        # After streaming, force +D
        nzo.set_pp(3)
        nzo.status = Status.FAILED
        nzo.save_attribs()
        all_ok = False

    if nzo.fail_msg:  # Special case: aborted due to too many missing data
        nzo.status = Status.FAILED
        nzo.save_attribs()
        all_ok = False
        par_error = True
        unpack_error = 1

    try:

        # Get the folder containing the download result
        workdir = nzo.downpath
        tmp_workdir_complete = None

        # if no files are present (except __admin__), fail the job
        if all_ok and len(globber(workdir)) < 2:
            if nzo.precheck:
                _enough, ratio = nzo.check_quality()
                req_ratio = float(cfg.req_completion_rate()) / 100.0
                # Make sure that rounded ratio doesn't equal required ratio
                # when it is actually below required
                if (ratio < req_ratio) and (req_ratio - ratio) < 0.001:
                    ratio = req_ratio - 0.001
                emsg = "%.1f%%" % (ratio * 100.0)
                emsg2 = "%.1f%%" % float(cfg.req_completion_rate())
                emsg = T("Download might fail, only %s of required %s available") % (emsg, emsg2)
            else:
                emsg = T("Download failed - Not on your server(s)")
                empty = True
            nzo.fail_msg = emsg
            nzo.set_unpack_info("Fail", emsg)
            nzo.status = Status.FAILED
            # do not run unpacking or parity verification
            flag_repair = flag_unpack = False
            all_ok = cfg.empty_postproc() and empty
            if not all_ok:
                par_error = True
                unpack_error = 1

        script = nzo.script
        cat = nzo.cat

        logging.info(
            "Starting PostProcessing on %s" + " => Repair:%s, Unpack:%s, Delete:%s, Script:%s, Cat:%s",
            filename,
            flag_repair,
            flag_unpack,
            flag_delete,
            script,
            cat,
        )

        # Set complete dir to workdir in case we need to abort
        workdir_complete = workdir
        dirname = nzo.final_name
        marker_file = None

        # Par processing, if enabled
        if all_ok and flag_repair:
            if not check_win_maxpath(workdir):
                crash_msg = T('Path exceeds 260, repair by "par2" is not possible')
                raise WindowsError
            par_error, re_add = parring(nzo, workdir)
            if re_add:
                # Try to get more par files
                return False

        # Check if user allows unsafe post-processing
        if flag_repair and cfg.safe_postproc():
            all_ok = all_ok and not par_error

        if all_ok:
            fix_unix_encoding(workdir)
            one_folder = False
            # Determine class directory
            if cfg.create_group_folders():
                complete_dir = addPrefixes(cfg.complete_dir.get_path(), nzo.dirprefix)
                complete_dir = create_dirs(complete_dir)
            else:
                catdir = config.get_categories(cat).dir()
                if catdir.endswith("*"):
                    catdir = catdir.strip("*")
                    one_folder = True
                complete_dir = real_path(cfg.complete_dir.get_path(), catdir)
            complete_dir = long_path(complete_dir)

            # TV/Movie/Date Renaming code part 1 - detect and construct paths
            if cfg.enable_meta():
                file_sorter = Sorter(nzo, cat)
            else:
                file_sorter = Sorter(None, cat)
            complete_dir = file_sorter.detect(dirname, complete_dir)
            if file_sorter.sort_file:
                one_folder = False

            complete_dir = sanitize_and_trim_path(complete_dir)

            if one_folder:
                workdir_complete = create_dirs(complete_dir)
            else:
                workdir_complete = get_unique_path(os.path.join(complete_dir, dirname), create_dir=True)
                marker_file = set_marker(workdir_complete)

            if not workdir_complete or not os.path.exists(workdir_complete):
                crash_msg = T("Cannot create final folder %s") % unicoder(os.path.join(complete_dir, dirname))
                raise IOError

            if cfg.folder_rename() and not one_folder:
                tmp_workdir_complete = prefix(workdir_complete, "_UNPACK_")
                try:
                    renamer(workdir_complete, tmp_workdir_complete)
                except:
                    pass  # On failure, just use the original name
            else:
                tmp_workdir_complete = workdir_complete

            newfiles = []
            # Run Stage 2: Unpack
            if flag_unpack:
                if all_ok:
                    # set the current nzo status to "Extracting...". Used in History
                    nzo.status = Status.EXTRACTING
                    logging.info("Running unpack_magic on %s", filename)
                    short_complete = short_path(tmp_workdir_complete)
                    unpack_error, newfiles = unpack_magic(
                        nzo, short_path(workdir), short_complete, flag_delete, one_folder, (), (), (), (), ()
                    )
                    if short_complete != tmp_workdir_complete:
                        newfiles = [f.replace(short_complete, tmp_workdir_complete) for f in newfiles]
                    logging.info("unpack_magic finished on %s", filename)
                else:
                    nzo.set_unpack_info("Unpack", T("No post-processing because of failed verification"))

            if cfg.safe_postproc():
                all_ok = all_ok and not unpack_error

            if all_ok:
                # Move any (left-over) files to destination
                nzo.status = Status.MOVING
                nzo.set_action_line(T("Moving"), "...")
                for root, _dirs, files in os.walk(workdir):
                    if not root.endswith(JOB_ADMIN):
                        for file_ in files:
                            path = os.path.join(root, file_)
                            new_path = path.replace(workdir, tmp_workdir_complete)
                            ok, new_path = move_to_path(path, new_path)
                            newfiles.append(new_path)
                            if not ok:
                                nzo.set_unpack_info(
                                    "Unpack", T("Failed moving %s to %s") % (unicoder(path), unicoder(new_path))
                                )
                                all_ok = False
                                break

            # Set permissions right
            set_permissions(tmp_workdir_complete)

            if all_ok and marker_file:
                del_marker(os.path.join(tmp_workdir_complete, marker_file))
                remove_from_list(marker_file, newfiles)

            if all_ok:
                # Remove files matching the cleanup list
                cleanup_list(tmp_workdir_complete, True)

                # Check if this is an NZB-only download, if so redirect to queue
                # except when PP was Download-only
                if flag_repair:
                    nzb_list = nzb_redirect(
                        tmp_workdir_complete, nzo.final_name, nzo.pp, script, cat, priority=nzo.priority
                    )
                else:
                    nzb_list = None
                if nzb_list:
                    nzo.set_unpack_info("Download", T("Sent %s to queue") % unicoder(nzb_list))
                    cleanup_empty_directories(tmp_workdir_complete)
                else:
                    cleanup_list(tmp_workdir_complete, False)

        script_output = ""
        script_ret = 0
        if not nzb_list:
            # Give destination its final name
            if cfg.folder_rename() and tmp_workdir_complete and not one_folder:
                if all_ok:
                    try:
                        newfiles = rename_and_collapse_folder(tmp_workdir_complete, workdir_complete, newfiles)
                    except:
                        logging.error(
                            T('Error renaming "%s" to "%s"'),
                            clip_path(tmp_workdir_complete),
                            clip_path(workdir_complete),
                        )
                        logging.info("Traceback: ", exc_info=True)
                        # Better disable sorting because filenames are all off now
                        file_sorter.sort_file = None
                else:
                    workdir_complete = tmp_workdir_complete.replace("_UNPACK_", "_FAILED_")
                    workdir_complete = get_unique_path(workdir_complete, n=0, create_dir=False)
                    workdir_complete = workdir_complete

            if empty:
                job_result = -1
            else:
                job_result = int(par_error) + int(bool(unpack_error)) * 2

            if cfg.ignore_samples():
                remove_samples(workdir_complete)

            # TV/Movie/Date Renaming code part 2 - rename and move files to parent folder
            if all_ok and file_sorter.sort_file:
                if newfiles:
                    file_sorter.rename(newfiles, workdir_complete)
                    workdir_complete, ok = file_sorter.move(workdir_complete)
                else:
                    workdir_complete, ok = file_sorter.rename_with_ext(workdir_complete)
                if not ok:
                    nzo.set_unpack_info("Unpack", T("Failed to move files"))
                    all_ok = False

            # Run the user script
            script_path = make_script_path(script)
            if (all_ok or not cfg.safe_postproc()) and (not nzb_list) and script_path:
                # set the current nzo status to "Ext Script...". Used in History
                nzo.status = Status.RUNNING
                nzo.set_action_line(T("Running script"), unicoder(script))
                nzo.set_unpack_info("Script", T("Running user script %s") % unicoder(script), unique=True)
                script_log, script_ret = external_processing(
                    short_path(script_path, False),
                    short_path(workdir_complete, False),
                    nzo.filename,
                    dirname,
                    cat,
                    nzo.group,
                    job_result,
                    nzo.nzo_info.get("failure", ""),
                )
                script_line = get_last_line(script_log)
                if script_log:
                    script_output = nzo.nzo_id
                if script_line:
                    nzo.set_unpack_info("Script", unicoder(script_line), unique=True)
                else:
                    nzo.set_unpack_info("Script", T("Ran %s") % unicoder(script), unique=True)
            else:
                script = ""
                script_line = ""
                script_ret = 0

        # Maybe bad script result should fail job
        if script_ret and cfg.script_can_fail():
            script_error = True
            all_ok = False
            nzo.fail_msg = T("Script exit code is %s") % script_ret
        else:
            script_error = False

        # Email the results
        if (not nzb_list) and cfg.email_endjob():
            if (cfg.email_endjob() == 1) or (cfg.email_endjob() == 2 and (unpack_error or par_error or script_error)):
                emailer.endjob(
                    dirname,
                    cat,
                    all_ok,
                    workdir_complete,
                    nzo.bytes_downloaded,
                    nzo.fail_msg,
                    nzo.unpack_info,
                    script,
                    TRANS(script_log),
                    script_ret,
                )

        if script_output:
            # Can do this only now, otherwise it would show up in the email
            if script_ret:
                script_ret = "Exit(%s) " % script_ret
            else:
                script_ret = ""
            if script_line:
                nzo.set_unpack_info(
                    "Script",
                    u'%s%s <a href="./scriptlog?name=%s">(%s)</a>'
                    % (script_ret, unicoder(script_line), urllib.quote(script_output), T("More")),
                    unique=True,
                )
            else:
                nzo.set_unpack_info(
                    "Script",
                    u'%s<a href="./scriptlog?name=%s">%s</a>'
                    % (script_ret, urllib.quote(script_output), T("View script output")),
                    unique=True,
                )

        # Cleanup again, including NZB files
        if all_ok:
            cleanup_list(workdir_complete, False)

        # Force error for empty result
        all_ok = all_ok and not empty

        # Update indexer with results
        if cfg.rating_enable():
            if nzo.encrypted > 0:
                Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_ENCRYPTED)
            if empty:
                hosts = map(lambda s: s.host, sabnzbd.downloader.Downloader.do.nzo_servers(nzo))
                if not hosts:
                    hosts = [None]
                for host in hosts:
                    Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_EXPIRED, host)

        # Show final status in history
        if all_ok:
            growler.send_notification(T("Download Completed"), filename, "complete")
            nzo.status = Status.COMPLETED
        else:
            growler.send_notification(T("Download Failed"), filename, "failed")
            nzo.status = Status.FAILED

    except:
        logging.error(T("Post Processing Failed for %s (%s)"), filename, crash_msg)
        if not crash_msg:
            logging.info("Traceback: ", exc_info=True)
            crash_msg = T("see logfile")
        nzo.fail_msg = T("PostProcessing was aborted (%s)") % unicoder(crash_msg)
        growler.send_notification(T("Download Failed"), filename, "failed")
        nzo.status = Status.FAILED
        par_error = True
        all_ok = False
        if cfg.email_endjob():
            emailer.endjob(
                dirname,
                cat,
                all_ok,
                clip_path(workdir_complete),
                nzo.bytes_downloaded,
                nzo.fail_msg,
                nzo.unpack_info,
                "",
                "",
                0,
            )

    if all_ok:
        # If the folder only contains one file OR folder, have that as the path
        # Be aware that series/generic/date sorting may move a single file into a folder containing other files
        workdir_complete = one_file_or_folder(workdir_complete)
        workdir_complete = os.path.normpath(workdir_complete)

    # Log the overall time taken for postprocessing
    postproc_time = int(time.time() - start)

    # Create the history DB instance
    history_db = database.get_history_handle()
    # Add the nzo to the database. Only the path, script and time taken is passed
    # Other information is obtained from the nzo
    history_db.add_history_db(nzo, clip_path(workdir_complete), nzo.downpath, postproc_time, script_log, script_line)
    # The connection is only used once, so close it here
    history_db.close()

    # Clean up the NZO
    try:
        logging.info("Cleaning up %s (keep_basic=%s)", filename, str(not all_ok))
        sabnzbd.nzbqueue.NzbQueue.do.cleanup_nzo(nzo, keep_basic=not all_ok)
    except:
        logging.error(T("Cleanup of %s failed."), nzo.final_name)
        logging.info("Traceback: ", exc_info=True)

    # Remove download folder
    if all_ok:
        try:
            if os.path.exists(workdir):
                logging.debug("Removing workdir %s", workdir)
                remove_all(workdir, recursive=True)
        except:
            logging.error(T("Error removing workdir (%s)"), clip_path(workdir))
            logging.info("Traceback: ", exc_info=True)

    # Use automatic retry link on par2 errors and encrypted/bad RARs
    if par_error or unpack_error in (2, 3):
        try_alt_nzb(nzo)

    return True
Beispiel #17
0
 def resume(self):
     logging.info("Resuming")
     if self.paused:
         growler.send_notification("SABnzbd", T('Resuming'), 'download')
     self.paused = False
Beispiel #18
0
def process_job(nzo):
    """ Process one job """
    assert isinstance(nzo, sabnzbd.nzbstuff.NzbObject)
    start = time.time()

    # keep track of whether we can continue
    all_ok = True
    # keep track of par problems
    par_error = False
    # keep track of any unpacking errors
    unpack_error = False
    # Signal empty download, for when 'empty_postproc' is enabled
    empty = False
    nzb_list = []
    # These need to be initialised incase of a crash
    workdir_complete = ''
    postproc_time = 0
    script_log = ''
    script_line = ''
    crash_msg = ''

    ## Get the job flags
    nzo.save_attribs()
    flag_repair, flag_unpack, flag_delete = nzo.repair_opts
    # Normalize PP
    if flag_delete: flag_unpack = True
    if flag_unpack: flag_repair = True

    # Get the NZB name
    filename = nzo.final_name
    msgid = nzo.msgid

    if cfg.allow_streaming() and not (flag_repair or flag_unpack
                                      or flag_delete):
        # After streaming, force +D
        nzo.set_pp(3)
        nzo.status = Status.FAILED
        nzo.save_attribs()
        all_ok = False

    if nzo.fail_msg:  # Special case: aborted due to too many missing data
        nzo.status = Status.FAILED
        nzo.save_attribs()
        all_ok = False
        par_error = unpack_error = True

    try:

        # Get the folder containing the download result
        workdir = nzo.downpath
        tmp_workdir_complete = None

        # if no files are present (except __admin__), fail the job
        if all_ok and len(globber(workdir)) < 2:
            if nzo.precheck:
                enough, ratio = nzo.check_quality()
                req_ratio = float(cfg.req_completion_rate()) / 100.0
                # Make sure that rounded ratio doesn't equal required ratio
                # when it is actually below required
                if (ratio < req_ratio) and (req_ratio - ratio) < 0.001:
                    ratio = req_ratio - 0.001
                emsg = '%.1f%%' % (ratio * 100.0)
                emsg2 = '%.1f%%' % float(cfg.req_completion_rate())
                emsg = T(
                    'Download might fail, only %s of required %s available'
                ) % (emsg, emsg2)
            else:
                emsg = T('Download failed - Out of your server\'s retention?')
                empty = True
            nzo.fail_msg = emsg
            nzo.set_unpack_info('Fail', emsg)
            nzo.status = Status.FAILED
            # do not run unpacking or parity verification
            flag_repair = flag_unpack = False
            all_ok = cfg.empty_postproc() and empty
            if not all_ok:
                par_error = unpack_error = True

        script = nzo.script
        cat = nzo.cat

        logging.info('Starting PostProcessing on %s' + \
                     ' => Repair:%s, Unpack:%s, Delete:%s, Script:%s, Cat:%s',
                     filename, flag_repair, flag_unpack, flag_delete, script, cat)

        ## Par processing, if enabled
        if all_ok and flag_repair:
            par_error, re_add = parring(nzo, workdir)
            if re_add:
                # Try to get more par files
                return False

        ## Check if user allows unsafe post-processing
        if flag_repair and cfg.safe_postproc():
            all_ok = all_ok and not par_error

        # Set complete dir to workdir in case we need to abort
        workdir_complete = workdir
        dirname = nzo.final_name
        marker_file = None

        if all_ok:
            one_folder = False
            ## Determine class directory
            if cfg.create_group_folders():
                complete_dir = addPrefixes(cfg.complete_dir.get_path(),
                                           nzo.dirprefix)
                complete_dir = create_dirs(complete_dir)
            else:
                catdir = config.get_categories(cat).dir()
                if catdir.endswith('*'):
                    catdir = catdir.strip('*')
                    one_folder = True
                complete_dir = real_path(cfg.complete_dir.get_path(), catdir)

            ## TV/Movie/Date Renaming code part 1 - detect and construct paths
            if cfg.enable_meta():
                file_sorter = Sorter(nzo, cat)
            else:
                file_sorter = Sorter(None, cat)
            complete_dir = file_sorter.detect(dirname, complete_dir)
            if file_sorter.sort_file:
                one_folder = False

            if one_folder:
                workdir_complete = create_dirs(complete_dir)
            else:
                workdir_complete = get_unique_path(os.path.join(
                    complete_dir, dirname),
                                                   create_dir=True)
                marker_file = set_marker(workdir_complete)

            if not workdir_complete or not os.path.exists(workdir_complete):
                crash_msg = T('Cannot create final folder %s') % unicoder(
                    os.path.join(complete_dir, dirname))
                raise IOError

            if cfg.folder_rename() and not one_folder:
                tmp_workdir_complete = prefix(workdir_complete, '_UNPACK_')
                try:
                    renamer(workdir_complete, tmp_workdir_complete)
                except:
                    pass  # On failure, just use the original name
            else:
                tmp_workdir_complete = workdir_complete

            newfiles = []
            ## Run Stage 2: Unpack
            if flag_unpack:
                if all_ok:
                    #set the current nzo status to "Extracting...". Used in History
                    nzo.status = Status.EXTRACTING
                    logging.info("Running unpack_magic on %s", filename)
                    unpack_error, newfiles = unpack_magic(
                        nzo, workdir, tmp_workdir_complete, flag_delete,
                        one_folder, (), (), (), ())
                    logging.info("unpack_magic finished on %s", filename)
                else:
                    nzo.set_unpack_info(
                        'Unpack',
                        T('No post-processing because of failed verification'))

            if cfg.safe_postproc():
                all_ok = all_ok and not unpack_error

            if all_ok:
                ## Move any (left-over) files to destination
                nzo.status = Status.MOVING
                nzo.set_action_line(T('Moving'), '...')
                for root, dirs, files in os.walk(workdir):
                    if not root.endswith(JOB_ADMIN):
                        for file_ in files:
                            path = os.path.join(root, file_)
                            new_path = path.replace(workdir,
                                                    tmp_workdir_complete)
                            ok, new_path = move_to_path(path, new_path)
                            newfiles.append(new_path)
                            if not ok:
                                nzo.set_unpack_info(
                                    'Unpack',
                                    T('Failed moving %s to %s') %
                                    (unicoder(path), unicoder(new_path)))
                                all_ok = False
                                break

            ## Set permissions right
            set_permissions(tmp_workdir_complete)

            if all_ok and marker_file:
                del_marker(os.path.join(tmp_workdir_complete, marker_file))
                remove_from_list(marker_file, newfiles)

            if all_ok:
                ## Remove files matching the cleanup list
                cleanup_list(tmp_workdir_complete, True)

                ## Check if this is an NZB-only download, if so redirect to queue
                ## except when PP was Download-only
                if flag_repair:
                    nzb_list = nzb_redirect(tmp_workdir_complete,
                                            nzo.final_name,
                                            nzo.pp,
                                            script,
                                            cat,
                                            priority=nzo.priority)
                else:
                    nzb_list = None
                if nzb_list:
                    nzo.set_unpack_info(
                        'Download',
                        T('Sent %s to queue') % unicoder(nzb_list))
                    cleanup_empty_directories(tmp_workdir_complete)
                else:
                    cleanup_list(tmp_workdir_complete, False)

        script_output = ''
        script_ret = 0
        if not nzb_list:
            ## Give destination its final name
            if cfg.folder_rename() and tmp_workdir_complete and not one_folder:
                if all_ok:
                    try:
                        newfiles = rename_and_collapse_folder(
                            tmp_workdir_complete, workdir_complete, newfiles)
                    except:
                        logging.error(Ta('Error renaming "%s" to "%s"'),
                                      tmp_workdir_complete, workdir_complete)
                        logging.info('Traceback: ', exc_info=True)
                        # Better disable sorting because filenames are all off now
                        file_sorter.sort_file = None
                else:
                    workdir_complete = tmp_workdir_complete.replace(
                        '_UNPACK_', '_FAILED_')
                    workdir_complete = get_unique_path(workdir_complete,
                                                       n=0,
                                                       create_dir=False)

            if empty:
                job_result = -1
            else:
                job_result = int(par_error) + int(unpack_error) * 2

            if cfg.ignore_samples() > 0:
                remove_samples(workdir_complete)

            ## TV/Movie/Date Renaming code part 2 - rename and move files to parent folder
            if all_ok and file_sorter.sort_file:
                if newfiles:
                    file_sorter.rename(newfiles, workdir_complete)
                    workdir_complete, ok = file_sorter.move(workdir_complete)
                else:
                    workdir_complete, ok = file_sorter.rename_with_ext(
                        workdir_complete)
                if not ok:
                    nzo.set_unpack_info('Unpack', T('Failed to move files'))
                    all_ok = False

            ## Run the user script
            script_path = make_script_path(script)
            if (all_ok or not cfg.safe_postproc()) and (
                    not nzb_list) and script_path:
                #set the current nzo status to "Ext Script...". Used in History
                nzo.status = Status.RUNNING
                nzo.set_action_line(T('Running script'), unicoder(script))
                nzo.set_unpack_info('Script',
                                    T('Running user script %s') %
                                    unicoder(script),
                                    unique=True)
                script_log, script_ret = external_processing(
                    script_path, workdir_complete, nzo.filename, msgid,
                    dirname, cat, nzo.group, job_result,
                    nzo.nzo_info.get('failure', ''))
                script_line = get_last_line(script_log)
                if script_log:
                    script_output = nzo.nzo_id
                if script_line:
                    nzo.set_unpack_info('Script',
                                        unicoder(script_line),
                                        unique=True)
                else:
                    nzo.set_unpack_info('Script',
                                        T('Ran %s') % unicoder(script),
                                        unique=True)
            else:
                script = ""
                script_line = ""
                script_ret = 0

        ## Email the results
        if (not nzb_list) and cfg.email_endjob():
            if (cfg.email_endjob() == 1) or (cfg.email_endjob() == 2 and
                                             (unpack_error or par_error)):
                emailer.endjob(dirname, msgid, cat, all_ok, workdir_complete,
                               nzo.bytes_downloaded,
                               nzo.fail_msg, nzo.unpack_info, script,
                               TRANS(script_log), script_ret)

        if script_output:
            # Can do this only now, otherwise it would show up in the email
            if script_ret:
                script_ret = 'Exit(%s) ' % script_ret
            else:
                script_ret = ''
            if script_line:
                nzo.set_unpack_info(
                    'Script',
                    u'%s%s <a href="./scriptlog?name=%s">(%s)</a>' %
                    (script_ret, unicoder(script_line),
                     urllib.quote(script_output), T('More')),
                    unique=True)
            else:
                nzo.set_unpack_info('Script',
                                    u'%s<a href="./scriptlog?name=%s">%s</a>' %
                                    (script_ret, urllib.quote(script_output),
                                     T('View script output')),
                                    unique=True)

        ## Cleanup again, including NZB files
        if all_ok:
            cleanup_list(workdir_complete, False)

        ## Remove newzbin bookmark, if any
        if msgid and all_ok:
            Bookmarks.do.del_bookmark(msgid)
        elif all_ok and isinstance(nzo.url, str):
            sabnzbd.proxy_rm_bookmark(nzo.url)

        ## Force error for empty result
        all_ok = all_ok and not empty

        ## Update indexer with results
        if nzo.encrypted > 0:
            Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_ENCRYPTED)
        if empty:
            hosts = map(lambda s: s.host,
                        sabnzbd.downloader.Downloader.do.nzo_servers(nzo))
            if not hosts: hosts = [None]
            for host in hosts:
                Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_EXPIRED,
                                           host)

        ## Show final status in history
        if all_ok:
            growler.send_notification(T('Download Completed'), filename,
                                      'complete')
            nzo.status = Status.COMPLETED
        else:
            growler.send_notification(T('Download Failed'), filename,
                                      'complete')
            nzo.status = Status.FAILED

    except:
        logging.error(Ta('Post Processing Failed for %s (%s)'), filename,
                      crash_msg)
        if not crash_msg:
            logging.info("Traceback: ", exc_info=True)
            crash_msg = T('see logfile')
        nzo.fail_msg = T('PostProcessing was aborted (%s)') % unicoder(
            crash_msg)
        growler.send_notification(T('Download Failed'), filename, 'complete')
        nzo.status = Status.FAILED
        par_error = True
        all_ok = False
        if cfg.email_endjob():
            emailer.endjob(dirname, msgid, cat, all_ok, workdir_complete,
                           nzo.bytes_downloaded, nzo.fail_msg, nzo.unpack_info,
                           '', '', 0)

    if all_ok:
        # If the folder only contains one file OR folder, have that as the path
        # Be aware that series/generic/date sorting may move a single file into a folder containing other files
        workdir_complete = one_file_or_folder(workdir_complete)
        workdir_complete = os.path.normpath(workdir_complete)

    # Log the overall time taken for postprocessing
    postproc_time = int(time.time() - start)

    # Create the history DB instance
    history_db = database.get_history_handle()
    # Add the nzo to the database. Only the path, script and time taken is passed
    # Other information is obtained from the nzo
    history_db.add_history_db(nzo, workdir_complete, nzo.downpath,
                              postproc_time, script_log, script_line)
    # The connection is only used once, so close it here
    history_db.close()

    ## Clean up the NZO
    try:
        logging.info('Cleaning up %s (keep_basic=%s)', filename,
                     str(not all_ok))
        sabnzbd.nzbqueue.NzbQueue.do.cleanup_nzo(nzo, keep_basic=not all_ok)
    except:
        logging.error(Ta('Cleanup of %s failed.'), nzo.final_name)
        logging.info("Traceback: ", exc_info=True)

    ## Remove download folder
    if all_ok:
        try:
            if os.path.exists(workdir):
                logging.debug('Removing workdir %s', workdir)
                remove_all(workdir, recursive=True)
        except:
            logging.error(Ta('Error removing workdir (%s)'), workdir)
            logging.info("Traceback: ", exc_info=True)

    return True
Beispiel #19
0
 def resume(self):
     logging.info("Resuming")
     if self.paused:
         growler.send_notification("SABnzbd", T('Resuming'), 'download')
     self.paused = False
Beispiel #20
0
def bad_fetch(nzo, url, msg='', retry=False, content=False):
    """ Create History entry for failed URL Fetch
        msg : message to be logged
        retry : make retry link in histort
        content : report in history that cause is a bad NZB file
    """
    if msg:
        msg = unicoder(msg)
    else:
        msg = ''

    pp = nzo.pp
    if pp is None:
        pp = ''
    else:
        pp = '&pp=%s' % str(pp)
    cat = nzo.cat
    if cat:
        cat = '&cat=%s' % urllib.quote(cat)
    else:
        cat = ''
    script = nzo.script
    if script:
        script = '&script=%s' % urllib.quote(script)
    else:
        script = ''

    nzo.status = Status.FAILED


    if url:
        nzo.filename = url
        nzo.final_name = url.strip()

    if content:
        # Bad content
        msg = T('Unusable NZB file')
    else:
        # Failed fetch
        msg = ' (' + msg + ')'

    if retry:
        nzbname = nzo.custom_name
        if nzbname:
            nzbname = '&nzbname=%s' % urllib.quote(nzbname)
        else:
            nzbname = ''
        text = T('URL Fetching failed; %s') + ', <a href="./retry?session=%s&url=%s&job=%s%s%s%s%s">' + T('Try again') + '</a>'
        parms = (msg, cfg.api_key(), urllib.quote(url), nzo.nzo_id, pp, cat, script, nzbname)
        nzo.fail_msg = text % parms
    else:
        nzo.fail_msg = msg

    if isinstance(url, int) or url.isdigit():
        url = 'Newzbin #%s' % url
    growler.send_notification(T('URL Fetching failed; %s') % '', '%s\n%s' % (msg, url), 'other')
    if cfg.email_endjob() > 0:
        #import sabnzbd.emailer
        sabnzbd.emailer.badfetch_mail(msg, url)

    from sabnzbd.nzbqueue import NzbQueue
    assert isinstance(NzbQueue.do, NzbQueue)
    NzbQueue.do.remove(nzo.nzo_id, add_to_history=True)
Beispiel #21
0
 def stop(self):
     self.shutdown = True
     growler.send_notification("SABnzbd",T('Shutting down'), 'startup')
Beispiel #22
0
def bad_fetch(nzo, url, msg='', retry=False, content=False):
    """ Create History entry for failed URL Fetch
        msg : message to be logged
        retry : make retry link in histort
        content : report in history that cause is a bad NZB file
    """
    if msg:
        msg = unicoder(msg)
    else:
        msg = ''

    pp = nzo.pp
    if pp is None:
        pp = ''
    else:
        pp = '&pp=%s' % str(pp)
    cat = nzo.cat
    if cat:
        cat = '&cat=%s' % urllib.quote(cat)
    else:
        cat = ''
    script = nzo.script
    if script:
        script = '&script=%s' % urllib.quote(script)
    else:
        script = ''

    nzo.status = Status.FAILED


    if url:
        nzo.filename = url
        nzo.final_name = url.strip()

    if content:
        # Bad content
        msg = T('Unusable NZB file')
    else:
        # Failed fetch
        msg = ' (' + msg + ')'

    if retry:
        nzbname = nzo.custom_name
        if nzbname:
            nzbname = '&nzbname=%s' % urllib.quote(nzbname)
        else:
            nzbname = ''
        text = T('URL Fetching failed; %s') + ', <a href="./retry?session=%s&url=%s&job=%s%s%s%s%s">' + T('Try again') + '</a>'
        parms = (msg, cfg.api_key(), urllib.quote(url), nzo.nzo_id, pp, cat, script, nzbname)
        nzo.fail_msg = text % parms
    else:
        nzo.fail_msg = msg

    if isinstance(url, int) or url.isdigit():
        url = 'Newzbin #%s' % url
    growler.send_notification(T('URL Fetching failed; %s') % '', '%s\n%s' % (msg, url), 'other')
    if cfg.email_endjob() > 0:
        #import sabnzbd.emailer
        sabnzbd.emailer.badfetch_mail(msg, url)

    from sabnzbd.nzbqueue import NzbQueue
    assert isinstance(NzbQueue.do, NzbQueue)
    NzbQueue.do.remove(nzo.nzo_id, add_to_history=True)
Beispiel #23
0
    def run(self):
        """ Process the queue (including waits and retries) """
        from sabnzbd.nzbqueue import NzbQueue
        self.shutdown = False
        while not self.shutdown:
            time.sleep(5)
            (msgid, nzo) = self.queue.get()
            if self.shutdown or not msgid:
                break
            if nzo.wait and nzo.wait > time.time():
                self.grab(msgid, nzo)
                continue
            logging.debug("Popping msgid %s", msgid)

            filename, data, newzbin_cat, nzo_info = _grabnzb(msgid)
            if filename and data:
                filename = name_fixer(filename)

                pp = nzo.pp
                script = nzo.script
                cat = nzo.cat
                if cat == '*' or not cat:
                    cat = cat_convert(newzbin_cat)

                priority = nzo.priority
                nzbname = nzo.custom_name

                cat, pp, script, priority = cat_to_opts(
                    cat, pp, script, priority)

                try:
                    sabnzbd.nzbqueue.insert_future_nzo(nzo,
                                                       filename,
                                                       msgid,
                                                       data,
                                                       pp=pp,
                                                       script=script,
                                                       cat=cat,
                                                       priority=priority,
                                                       nzbname=nzbname,
                                                       nzo_info=nzo_info)
                    nzo.url = format_source_url(str(msgid))
                except:
                    logging.error(Ta('Failed to update newzbin job %s'), msgid)
                    logging.info("Traceback: ", exc_info=True)

                    NzbQueue.do.remove(nzo.nzo_id, False)
                msgid = None
            else:
                if filename:
                    self.grab(msgid, nzo, float(filename))
                else:
                    # Fatal error, give up on this one
                    bad_fetch(nzo, msgid, msg=nzo_info, retry=True)
                msgid = None

            if msgid:
                growler.send_notification(T('NZB added to queue'), filename,
                                          'download')

        logging.debug('Stopping MSGIDGrabber')
Beispiel #24
0
def parring(nzo, workdir):
    """ Perform par processing. Returns: (par_error, re_add)
    """
    assert isinstance(nzo, sabnzbd.nzbstuff.NzbObject)
    filename = nzo.final_name
    growler.send_notification(T('Post-processing'), nzo.final_name, 'pp')
    logging.info('Par2 check starting on %s', filename)

    ## Collect the par files
    if nzo.partable:
        par_table = nzo.partable.copy()
    else:
        par_table = {}
    repair_sets = par_table.keys()

    re_add = False
    par_error = False

    if repair_sets:

        for set_ in repair_sets:
            logging.info("Running repair on set %s", set_)
            parfile_nzf = par_table[set_]
            need_re_add, res = par2_repair(parfile_nzf, nzo, workdir, set_)
            if need_re_add:
                re_add = True
            par_error = par_error or not res

        if re_add:
            logging.info('Readded %s to queue', filename)
            nzo.priority = REPAIR_PRIORITY
            sabnzbd.nzbqueue.add_nzo(nzo)
            sabnzbd.downloader.Downloader.do.resume_from_postproc()

        logging.info('Par2 check finished on %s', filename)

    if (par_error and not re_add) or not repair_sets:
        # See if alternative SFV check is possible
        if cfg.sfv_check():
            sfvs = globber(workdir, '*.sfv')
        else:
            sfvs = None
        if sfvs:
            par_error = False
            nzo.set_unpack_info('Repair', T('Trying SFV verification'))
            for sfv in sfvs:
                failed = sfv_check(sfv)
                if failed:
                    msg = T('Some files failed to verify against "%s"'
                            ) % unicoder(os.path.basename(sfv))
                    msg += '; '
                    msg += '; '.join(failed)
                    nzo.set_unpack_info('Repair', msg)
                    par_error = True
            if not par_error:
                nzo.set_unpack_info('Repair',
                                    T('Verified successfully using SFV files'))
        elif not repair_sets:
            logging.info("No par2 sets for %s", filename)
            nzo.set_unpack_info('Repair',
                                T('[%s] No par2 sets') % unicoder(filename))

    if not par_error:
        flag_file(workdir, VERIFIED_FILE, create=True)
    return par_error, re_add
Beispiel #25
0
def process_job(nzo):
    """ Process one job """
    assert isinstance(nzo, sabnzbd.nzbstuff.NzbObject)
    start = time.time()

    # keep track of whether we can continue
    all_ok = True
    # keep track of par problems
    par_error = False
    # keep track of any unpacking errors
    unpack_error = False
    # Signal empty download, for when 'empty_postproc' is enabled
    empty = False
    nzb_list = []
    # These need to be initialised incase of a crash
    workdir_complete = ''
    postproc_time = 0
    script_log = ''
    script_line = ''
    crash_msg = ''

    ## Get the job flags
    nzo.save_attribs()
    flag_repair, flag_unpack, flag_delete = nzo.repair_opts
    # Normalize PP
    if flag_delete: flag_unpack = True
    if flag_unpack: flag_repair = True

    # Get the NZB name
    filename = nzo.final_name
    msgid = nzo.msgid

    if cfg.allow_streaming() and not (flag_repair or flag_unpack or flag_delete):
        # After streaming, force +D
        nzo.set_pp(3)
        nzo.status = Status.FAILED
        nzo.save_attribs()
        all_ok = False

    if nzo.fail_msg: # Special case: aborted due to too many missing data
        nzo.status = Status.FAILED
        nzo.save_attribs()
        all_ok = False
        par_error = unpack_error = True

    try:

        # Get the folder containing the download result
        workdir = nzo.downpath
        tmp_workdir_complete = None

        # if no files are present (except __admin__), fail the job
        if all_ok and len(globber(workdir)) < 2:
            if nzo.precheck:
                enough, ratio = nzo.check_quality()
                req_ratio = float(cfg.req_completion_rate()) / 100.0
                # Make sure that rounded ratio doesn't equal required ratio
                # when it is actually below required
                if (ratio < req_ratio) and (req_ratio - ratio) < 0.001:
                    ratio = req_ratio - 0.001
                emsg = '%.1f%%' % (ratio * 100.0)
                emsg2 = '%.1f%%' % float(cfg.req_completion_rate())
                emsg = T('Download might fail, only %s of required %s available') % (emsg, emsg2)
            else:
                emsg = T('Download failed - Out of your server\'s retention?')
                empty = True
            nzo.fail_msg = emsg
            nzo.set_unpack_info('Fail', emsg)
            nzo.status = Status.FAILED
            # do not run unpacking or parity verification
            flag_repair = flag_unpack = False
            all_ok = cfg.empty_postproc() and empty
            if not all_ok:
                par_error = unpack_error = True

        script = nzo.script
        cat = nzo.cat

        logging.info('Starting PostProcessing on %s' + \
                     ' => Repair:%s, Unpack:%s, Delete:%s, Script:%s, Cat:%s',
                     filename, flag_repair, flag_unpack, flag_delete, script, cat)

        ## Par processing, if enabled
        if all_ok and flag_repair:
            par_error, re_add = parring(nzo, workdir)
            if re_add:
                # Try to get more par files
                return False

        ## Check if user allows unsafe post-processing
        if flag_repair and cfg.safe_postproc():
            all_ok = all_ok and not par_error

        # Set complete dir to workdir in case we need to abort
        workdir_complete = workdir
        dirname = nzo.final_name
        marker_file = None

        if all_ok:
            one_folder = False
            ## Determine class directory
            if cfg.create_group_folders():
                complete_dir = addPrefixes(cfg.complete_dir.get_path(), nzo.dirprefix)
                complete_dir = create_dirs(complete_dir)
            else:
                catdir = config.get_categories(cat).dir()
                if catdir.endswith('*'):
                    catdir = catdir.strip('*')
                    one_folder = True
                complete_dir = real_path(cfg.complete_dir.get_path(), catdir)

            ## TV/Movie/Date Renaming code part 1 - detect and construct paths
            file_sorter = Sorter(cat)
            complete_dir = file_sorter.detect(dirname, complete_dir)
            if file_sorter.sort_file:
                one_folder = False

            if one_folder:
                workdir_complete = create_dirs(complete_dir)
            else:
                workdir_complete = get_unique_path(os.path.join(complete_dir, dirname), create_dir=True)
                marker_file = set_marker(workdir_complete)

            if not workdir_complete or not os.path.exists(workdir_complete):
                crash_msg = T('Cannot create final folder %s') % unicoder(os.path.join(complete_dir, dirname))
                raise IOError

            if cfg.folder_rename() and not one_folder:
                tmp_workdir_complete = prefix(workdir_complete, '_UNPACK_')
                try:
                    renamer(workdir_complete, tmp_workdir_complete)
                except:
                    pass # On failure, just use the original name
            else:
                tmp_workdir_complete = workdir_complete

            newfiles = []
            ## Run Stage 2: Unpack
            if flag_unpack:
                if all_ok:
                    #set the current nzo status to "Extracting...". Used in History
                    nzo.status = Status.EXTRACTING
                    logging.info("Running unpack_magic on %s", filename)
                    unpack_error, newfiles = unpack_magic(nzo, workdir, tmp_workdir_complete, flag_delete, one_folder, (), (), (), ())
                    logging.info("unpack_magic finished on %s", filename)
                else:
                    nzo.set_unpack_info('Unpack', T('No post-processing because of failed verification'))

            if cfg.safe_postproc():
                all_ok = all_ok and not unpack_error

            if all_ok:
                ## Move any (left-over) files to destination
                nzo.status = Status.MOVING
                nzo.set_action_line(T('Moving'), '...')
                for root, dirs, files in os.walk(workdir):
                    if not root.endswith(JOB_ADMIN):
                        for file_ in files:
                            path = os.path.join(root, file_)
                            new_path = path.replace(workdir, tmp_workdir_complete)
                            ok, new_path = move_to_path(path, new_path)
                            newfiles.append(new_path)
                            if not ok:
                                nzo.set_unpack_info('Unpack', T('Failed moving %s to %s') % (unicoder(path), unicoder(new_path)))
                                all_ok = False
                                break

            ## Set permissions right
            set_permissions(tmp_workdir_complete)

            if all_ok and marker_file:
                del_marker(os.path.join(tmp_workdir_complete, marker_file))
                remove_from_list(marker_file, newfiles)

            if all_ok:
                ## Remove files matching the cleanup list
                cleanup_list(tmp_workdir_complete, True)

                ## Check if this is an NZB-only download, if so redirect to queue
                ## except when PP was Download-only
                if flag_repair:
                    nzb_list = nzb_redirect(tmp_workdir_complete, nzo.final_name, nzo.pp, script, cat, priority=nzo.priority)
                else:
                    nzb_list = None
                if nzb_list:
                    nzo.set_unpack_info('Download', T('Sent %s to queue') % unicoder(nzb_list))
                    cleanup_empty_directories(tmp_workdir_complete)
                else:
                    cleanup_list(tmp_workdir_complete, False)

        script_output = ''
        script_ret = 0
        if not nzb_list:
            ## Give destination its final name
            if cfg.folder_rename() and tmp_workdir_complete and not one_folder:
                if all_ok:
                    try:
                        newfiles = rename_and_collapse_folder(tmp_workdir_complete, workdir_complete, newfiles)
                    except:
                        logging.error(Ta('Error renaming "%s" to "%s"'), tmp_workdir_complete, workdir_complete)
                        logging.info('Traceback: ', exc_info = True)
                        # Better disable sorting because filenames are all off now
                        file_sorter.sort_file = None
                else:
                    workdir_complete = tmp_workdir_complete.replace('_UNPACK_', '_FAILED_')
                    workdir_complete = get_unique_path(workdir_complete, n=0, create_dir=False)

            if empty:
                job_result = -1
            else:
                job_result = int(par_error) + int(unpack_error)*2

            if cfg.ignore_samples() > 0:
                remove_samples(workdir_complete)

            ## TV/Movie/Date Renaming code part 2 - rename and move files to parent folder
            if all_ok and file_sorter.sort_file:
                if newfiles:
                    file_sorter.rename(newfiles, workdir_complete)
                    workdir_complete, ok = file_sorter.move(workdir_complete)
                else:
                    workdir_complete, ok = file_sorter.rename_with_ext(workdir_complete)
                if not ok:
                    nzo.set_unpack_info('Unpack', T('Failed to move files'))
                    all_ok = False

            ## Run the user script
            script_path = make_script_path(script)
            if (all_ok or not cfg.safe_postproc()) and (not nzb_list) and script_path:
                #set the current nzo status to "Ext Script...". Used in History
                nzo.status = Status.RUNNING
                nzo.set_action_line(T('Running script'), unicoder(script))
                nzo.set_unpack_info('Script', T('Running user script %s') % unicoder(script), unique=True)
                script_log, script_ret = external_processing(script_path, workdir_complete, nzo.filename,
                                                             msgid, dirname, cat, nzo.group, job_result)
                script_line = get_last_line(script_log)
                if script_log:
                    script_output = nzo.nzo_id
                if script_line:
                    nzo.set_unpack_info('Script', unicoder(script_line), unique=True)
                else:
                    nzo.set_unpack_info('Script', T('Ran %s') % unicoder(script), unique=True)
            else:
                script = ""
                script_line = ""
                script_ret = 0

        ## Email the results
        if (not nzb_list) and cfg.email_endjob():
            if (cfg.email_endjob() == 1) or (cfg.email_endjob() == 2 and (unpack_error or par_error)):
                emailer.endjob(dirname, msgid, cat, all_ok, workdir_complete, nzo.bytes_downloaded,
                               nzo.fail_msg, nzo.unpack_info, script, TRANS(script_log), script_ret)

        if script_output:
            # Can do this only now, otherwise it would show up in the email
            if script_ret:
                script_ret = 'Exit(%s) ' % script_ret
            else:
                script_ret = ''
            if script_line:
                nzo.set_unpack_info('Script',
                                    u'%s%s <a href="./scriptlog?name=%s">(%s)</a>' % (script_ret, unicoder(script_line), urllib.quote(script_output),
                                     T('More')), unique=True)
            else:
                nzo.set_unpack_info('Script',
                                    u'%s<a href="./scriptlog?name=%s">%s</a>' % (script_ret, urllib.quote(script_output),
                                    T('View script output')), unique=True)

        ## Cleanup again, including NZB files
        if all_ok:
            cleanup_list(workdir_complete, False)

        ## Remove newzbin bookmark, if any
        if msgid and all_ok:
            Bookmarks.do.del_bookmark(msgid)
        elif all_ok and isinstance(nzo.url, str):
            sabnzbd.proxy_rm_bookmark(nzo.url)

        ## Force error for empty result
        all_ok = all_ok and not empty

        ## Show final status in history
        if all_ok:
            growler.send_notification(T('Download Completed'), filename, 'complete')
            nzo.status = Status.COMPLETED
        else:
            growler.send_notification(T('Download Failed'), filename, 'complete')
            nzo.status = Status.FAILED

    except:
        logging.error(Ta('Post Processing Failed for %s (%s)'), filename, crash_msg)
        if not crash_msg:
            logging.info("Traceback: ", exc_info = True)
            crash_msg = T('see logfile')
        nzo.fail_msg = T('PostProcessing was aborted (%s)') % unicoder(crash_msg)
        growler.send_notification(T('Download Failed'), filename, 'complete')
        nzo.status = Status.FAILED
        par_error = True
        all_ok = False
        if cfg.email_endjob():
            emailer.endjob(dirname, msgid, cat, all_ok, workdir_complete, nzo.bytes_downloaded,
                           nzo.fail_msg, nzo.unpack_info, '', '', 0)


    if all_ok:
        # If the folder only contains one file OR folder, have that as the path
        # Be aware that series/generic/date sorting may move a single file into a folder containing other files
        workdir_complete = one_file_or_folder(workdir_complete)
        workdir_complete = os.path.normpath(workdir_complete)

    # Log the overall time taken for postprocessing
    postproc_time = int(time.time() - start)

    # Create the history DB instance
    history_db = database.get_history_handle()
    # Add the nzo to the database. Only the path, script and time taken is passed
    # Other information is obtained from the nzo
    history_db.add_history_db(nzo, workdir_complete, nzo.downpath, postproc_time, script_log, script_line)
    # The connection is only used once, so close it here
    history_db.close()

    ## Clean up the NZO
    try:
        logging.info('Cleaning up %s (keep_basic=%s)', filename, str(not all_ok))
        sabnzbd.nzbqueue.NzbQueue.do.cleanup_nzo(nzo, keep_basic=not all_ok)
    except:
        logging.error(Ta('Cleanup of %s failed.'), nzo.final_name)
        logging.info("Traceback: ", exc_info = True)

    ## Remove download folder
    if all_ok:
        try:
            if os.path.exists(workdir):
                logging.debug('Removing workdir %s', workdir)
                remove_all(workdir, recursive=True)
        except:
            logging.error(Ta('Error removing workdir (%s)'), workdir)
            logging.info("Traceback: ", exc_info = True)

    return True
Beispiel #26
0
 def stop(self):
     self.shutdown = True
     growler.send_notification("SABnzbd", T("Shutting down"), "startup")