Compare commits

..

33 Commits

Author SHA1 Message Date
shypike
f61e7cb1ed Update text files for 0.7.5 Final. 2012-11-03 16:15:22 +01:00
ShyPike
3de0c0e4ac Add missing "%dn" (original folder name) formula to Generic Sorting. 2012-11-01 21:24:51 +01:00
ShyPike
63796d3feb Improve logging for RSS readouts. 2012-11-01 19:47:48 +01:00
ShyPike
6b07529300 Update text files for 0.7.5RC1 2012-10-30 20:35:57 +01:00
ShyPike
e10676710c Support for news in Config. 2012-10-30 20:17:51 +01:00
shypike
77f67c6666 Merge pull request #59 from akuiraz/newzxxx2_fix
Fixed regex for newzbin rss filtering
2012-10-30 11:58:20 -07:00
ShyPike
bdbcdd61e1 Mask password in "Add Server" dialog. 2012-10-30 19:51:12 +01:00
ShyPike
4ab7ec754d Add periodic detection of completed but hanging jobs in the queue.
The 30 second watchdog now detects jobs without pending files.
Those jobs will be sent to the post-processor.
2012-10-30 18:47:18 +01:00
akuiraz
20f98f48bc Fixed regex for newzbin filtering by adding xxx2, now rss feeds from newzxxx2.ch will successfully download 2012-10-30 01:27:36 -04:00
shypike
84e0502e50 Prevent crash when trying to open non-existing "complete" folder from Windows System-tray icon. 2012-10-28 12:39:34 +01:00
shypike
2aa1b00dbb Prevent CherryPy crash when reading a cookie from another app which has a non-standard name. 2012-10-27 13:33:33 +02:00
ShyPike
972078a514 Fix problem with "Read" button when RSS feed name contains "&".
The feed's name wasn't properly encoded in the URL.
2012-10-24 19:34:45 +02:00
shypike
be8382d25b Add special option 'empty_postproc'.
Setting this option will run the user script on an empty download.
Normally this isn't done.
The status sent to the user script is -1, meaning "no files were downloaded".
2012-10-21 18:23:25 +02:00
shypike
8d46e88cd8 Update translations 2012-10-21 12:56:00 +02:00
shypike
6b6b1b79ad Add 'prio_sort_list' special.
This is a list of file name extensions.
Matching files will be the first to be downloaded within an NZB.

Also, if the user sets a simple space-seperated list, this will be converted to a standardized list.
2012-10-21 12:16:13 +02:00
shypike
e1fd40b34d OSX: Retina compatible menu-bar icons. 2012-10-20 19:57:11 +02:00
ShyPike
bc1f8f97a8 Prefix categories of nzbxxx.com with "XXX:". 2012-10-20 16:03:09 +02:00
shypike
b51705f458 Fix issues with accented and special characters in names of downloaded files.
name_extractor() returned Unicode instead of platform-compatible encoding.
QuickCheck assumed incorrectly that file names are not yet platform-compatible.
2012-10-20 15:11:08 +02:00
ShyPike
aaed5f4797 Adjust nzbmatrix category table. 2012-10-17 21:30:22 +02:00
ShyPike
a8eedef1d2 Prevent stuck jobs at end of pre-check. 2012-10-17 21:22:36 +02:00
shypike
9407e21e1e Prevent unusual SFV files from crashing post-processing. 2012-10-13 10:56:05 +02:00
ShyPike
ba6dcfd467 Don't show speed and ETA when download is paused during post-processing. 2012-10-08 21:21:15 +02:00
shypike
e2c1de5008 Prevent soft-crash when api-function "addfile" is called without parameters. 2012-10-06 21:57:48 +02:00
ShyPike
10b7403748 Update text files for 0.7.4 Final. 2012-10-02 20:04:03 +02:00
ShyPike
1ba924cc12 Pre-queue script didn't get the show/season/episode information any longer.
Due to earlier changes in tvsort, the pre-queue script handler now needs to
call an extra method of the SeriesSorter.
2012-10-02 19:56:47 +02:00
ShyPike
11eb034bd3 Prevent crash on startup when a fully downloaded job is still in download queue.
When at startup, there's a job in the download which has no more file to download,
it will be passed to post-processing. However the queue logic will try do disconnect
all servers first. This call will fail because the downloader hasn't start yet.
2012-10-02 19:34:21 +02:00
ShyPike
c3250e15cb Update text files for 0.7.4 Final. 2012-10-01 21:25:19 +02:00
shypike
8ff8a59b4c Update translations 2012-10-01 21:02:44 +02:00
ShyPike
0c646d88b2 New RSS feed should no longer be considered new after first, but empty readout.
Search and bookmark feeds can be empty when entered into SABnzbd.
The "don't download first batch" rule should be discarded when the
first readout is empty (as expected).
Currently the feed remains "new" until the first content is found.
2012-10-01 19:53:22 +02:00
ShyPike
05670ea599 Make "auth" call backward-compatible with 0.6.x releases.
Return "apikey" when no key is sent, instead of "badkey".
2012-09-30 23:17:05 +02:00
ShyPike
e25eb32885 Config->Notifications: email and growl server addresses should not be marked as "url" type. 2012-09-30 16:08:19 +02:00
shypike
250f75f084 OSX: fix top menu queue info.
The OSX queue menu entry shows a maximum of 10 jobs.
However, the counter should show the total amount of active jobs along with
the total size of these jobs.
Instead only the contribution of the 10 visible jobs was shown.
2012-09-29 15:21:30 +02:00
ShyPike
cdd39e6777 Plush: Purge history will now use the active filter.
When selecting one of the buttons in the Purge History dialog will
now use the current filter to select jobs to be deleted.
This is more what people expect to happen.
2012-09-29 12:19:56 +02:00
37 changed files with 306 additions and 160 deletions

View File

@@ -1,5 +1,5 @@
*******************************************
*** This is SABnzbd 0.7.4 ***
*** This is SABnzbd 0.7.5 ***
*******************************************
SABnzbd is an open-source cross-platform binary newsreader.
It simplifies the process of downloading from Usenet dramatically,

View File

@@ -1,3 +1,33 @@
-------------------------------------------------------------------------------
0.7.5Final by The SABnzbd-Team
-------------------------------------------------------------------------------
- Add missing %dn formula to Generic Sort
- Improve RSS logging
-------------------------------------------------------------------------------
0.7.5RC1 by The SABnzbd-Team
-------------------------------------------------------------------------------
- Prevent stuck jobs at end of pre-check.
- Fix issues with accented and special characters in names of downloaded files.
- Adjust nzbmatrix category table.
- Add 'prio_sort_list' special
- Add special option 'empty_postproc'.
- Prevent CherryPy crash when reading a cookie from another app which has a non-standard name.
- Prevent crash when trying to open non-existing "complete" folder from Windows System-tray icon.
- Fix problem with "Read" button when RSS feed name contains "&".
- Prevent unusual SFV files from crashing post-processing.
- OSX: Retina compatible menu-bar icons.
- Don't show speed and ETA when download is paused during post-processing
- Prevent soft-crash when api-function "addfile" is called without parameters.
- Add news channel frame
-------------------------------------------------------------------------------
0.7.4Final by The SABnzbd-Team
-------------------------------------------------------------------------------
- Pre-queue script no longer got the show/season/episode information.
- Prevent crash on startup when a fully downloaded job is still in download queue.
- New RSS feed should no longer be considered new after first, but empty readout.
- Make "auth" call backward-compatible with 0.6.x releases.
- Config->Notifications: email and growl server addresses should not be marked as "url" type.
- OSX: fix top menu queue info so that it shows total queue size
-------------------------------------------------------------------------------
0.7.4RC2 by The SABnzbd-Team
-------------------------------------------------------------------------------

View File

@@ -1,4 +1,4 @@
SABnzbd 0.7.4
SABnzbd 0.7.5
-------------------------------------------------------------------------------
0) LICENSE

View File

@@ -1,7 +1,7 @@
Metadata-Version: 1.0
Name: SABnzbd
Version: 0.7.4RC2
Summary: SABnzbd-0.7.4RC2
Version: 0.7.5
Summary: SABnzbd-0.7.5
Home-page: http://sabnzbd.org
Author: The SABnzbd Team
Author-email: team@sabnzbd.org

View File

@@ -1,29 +1,17 @@
Release Notes - SABnzbd 0.7.4RC2
==================================
Release Notes - SABnzbd 0.7.5
===============================
## Fixes in 0.7.4RC2
- Pre-check failed to consider extra par2 files
- Fixed unjustified warning that can occur with OSX Growl 2.0
- Show memory usage on Linux systems
- Fix incorrect end-of-month quota reset
- Fix UI refresh issue when using Safari on iOS6
## Features
- Update nzbmatrix category table (check your categories!)
- Add special options 'empty_postproc' and 'prio_sort_list' (See Wiki)
- OSX: Retina compatible menu-bar icons
## Fixes in 0.7.4RC1
- OSX Mountain Lion: Notification Center support
- OSX Mountain Lion: improved "keep awake" support
- Restore SABnzbd icon for Growl
- Scheduler: action can now run on multiple weekdays
- Scheduler: add "remove failed jobs" action
- After successful pre-check, preserve a job's position in the queue
- Make Windows version less eager to use par2-classic
- Support for HTTPS chain files (needed when you buy your own certificate)
- Prevent jobs from showing up in queue and history simultaneously
- Fix failure to fetch more par2-files for posts with badly formatted subject lines
- Special option: rss_odd_titles (see Wiki)
- Special option: 'overwrite_files' (See Wiki)
- A number of small issues (see changelog)
- Fix for third-party tools requesting too much history
## Bug fixes
- Prevent stuck jobs at end of pre-check
- Fix issues with accented and special characters in names of downloaded files
- Fix problem with "Read" button when RSS feed name contains "&"
- Prevent unusual SFV files from crashing post-processing
- Added missing %dn formula to Generic Sort
## What's new in 0.7.0

View File

@@ -658,7 +658,10 @@ class Request(object):
# Handle cookies differently because on Konqueror, multiple
# cookies come on different lines with the same key
if name == 'Cookie':
self.cookie.load(value)
try:
self.cookie.load(value)
except:
pass
if not dict.__contains__(headers, 'Host'):
# All Internet-based HTTP/1.1 servers MUST respond with a 400

View File

@@ -24,6 +24,11 @@
<h5 class="copyright">Copyright &copy; 2008-2012 The SABnzbd Team &lt;<span style="color: #0000ff;">team@sabnzbd.org</span>&gt;</h5>
<p class="copyright"><small>$T('yourRights')</small></p>
</div>
<!--#if $news_items#-->
<div class="padding">
<iframe frameborder=0 width=100% src="http://sabnzbdplus.sourceforge.net/version/news.html"></iframe>
</div>
<!--#end if#-->
</div>
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->

View File

@@ -48,7 +48,7 @@
<fieldset>
<div class="field-pair alt">
<label class="config" for="email_server">$T('opt-email_server')</label>
<input type="url" name="email_server" id="email_server" value="$email_server" size="40" />
<input type="text" name="email_server" id="email_server" value="$email_server" size="40" />
<span class="desc">$T('explain-email_server')</span>
</div>
<div class="field-pair">
@@ -111,7 +111,7 @@
</div>
<div class="field-pair alt">
<label class="config" for="growl_server">$T('opt-growl_server')</label>
<input type="url" name="growl_server" id="growl_server" value="$growl_server" size="40" />
<input type="text" name="growl_server" id="growl_server" value="$growl_server" size="40" />
<span class="desc">$T('explain-growl_server')</span>
</div>
<div class="field-pair">

View File

@@ -498,6 +498,10 @@
</div><!-- /colmask -->
<script>
function urlencode(str) {
return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
}
\$(document).ready(function(){
\$('.editFeed').click(function(){
var oldURI = \$(this).prev().val();
@@ -537,7 +541,7 @@
url: "test_rss_feed",
data: {feed: whichFeed, session: "$session" }
}).done(function( msg ) {
location = '?feed=' + whichFeed;
location = '?feed=' + urlencode(whichFeed);
// location.reload();
});
});

View File

@@ -35,7 +35,7 @@
</div>
<div class="field-pair alt">
<label class="config" for="password">$T('srv-password')</label>
<input type="text" name="password" id="password" size="30" />
<input type="password" name="password" id="password" size="30" />
</div>
<div class="field-pair">
<label class="config" for="connections">$T('srv-connections')</label>

View File

@@ -265,6 +265,11 @@
<td>$T('sort-File')</td>
</tr>
<tr>
<td class="align-right"><b>$T('orgDirname'):</b></td>
<td>%dn</td>
<td>$T("sort-Folder")</td>
</tr>
<tr class="even">
<td class="align-right"><b>$T('lowercase'):</b></td>
<td>{$T('TEXT')}</td>
<td>$T('text')</td>
@@ -432,7 +437,7 @@
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
}
}
})();
function tvSet(val) {

View File

@@ -46,7 +46,7 @@
<div class="field-pair alt">
<label class="nocheck clearfix" for="password">
<span class="component-title">$T('srv-password')</span>
<input type="text" size="25" name="password"/>
<input type="password" size="25" name="password"/>
</label>
</div>
<div class="field-pair">
@@ -156,7 +156,7 @@
<div class="field-pair alt">
<label class="nocheck clearfix" for="password">
<span class="component-title">$T('srv-password')</span>
<input type="text" size="25" name="password" value="$servers[$server]['password']" />
<input type="password" size="25" name="password" value="$servers[$server]['password']" />
</label>
</div>
<div class="field-pair">

View File

@@ -895,7 +895,7 @@ $("a","#multiops_inputs").click(function(e){
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
data: {mode:'history', name:'delete', value:value, del_files:del_files, apikey: $.plush.apikey},
data: {mode:'history', name:'delete', value:value, del_files:del_files, search: $('#historySearchBox').val(), apikey: $.plush.apikey},
success: function(){
$.colorbox.close();
$.plush.modalOpen=false;
@@ -1242,12 +1242,16 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
SetQueueETAStats : function(speed,kbpersec,timeleft,eta) {
// ETA/speed stats at top of queue
if (kbpersec < 1 && $.plush.paused)
if (kbpersec < 1 || $.plush.paused) {
$('#stats_eta').html('&mdash;');
else
$('#stats_speed').html('&mdash;');
$('#time-left').attr('title','&mdash;'); // Tooltip on "time left"
}
else {
$('#stats_eta').html(timeleft);
$('#stats_speed').html(speed+"B/s");
$('#time-left').attr('title',eta); // Tooltip on "time left"
$('#stats_speed').html(speed+"B/s");
$('#time-left').attr('title',eta); // Tooltip on "time left"
}
},

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 B

View File

Binary file not shown.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 902 B

BIN
osx/resources/sab_idle.tiff Normal file
View File

Binary file not shown.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

Binary file not shown.

View File

@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-09-10 17:12+0000\n"
"PO-Revision-Date: 2012-08-03 02:19+0000\n"
"PO-Revision-Date: 2012-09-29 03:57+0000\n"
"Last-Translator: lrrosa <Unknown>\n"
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-09-11 05:13+0000\n"
"X-Generator: Launchpad (build 15924)\n"
"X-Launchpad-Export-Date: 2012-09-30 05:05+0000\n"
"X-Generator: Launchpad (build 16049)\n"
#: SABnzbd.py:302 [Error message]
msgid "Failed to start web-interface"
@@ -56,7 +56,7 @@ msgstr "HTTPS desabilitado pela falta de arquivos CERT e KEY"
#: SABnzbd.py:1525
msgid "SABnzbd %s started"
msgstr ""
msgstr "SABnzbd %s iniciado"
#: SABnzbd.py:1667 # sabnzbd/osxmenu.py:775
msgid "SABnzbd shutdown finished"
@@ -1518,7 +1518,7 @@ msgstr "Ler feeds RSS"
#: sabnzbd/skintext.py:65 [Config->Scheduler]
msgid "Remove failed jobs"
msgstr ""
msgstr "Remover tarefas com falha"
#: sabnzbd/skintext.py:70 [Speed indicator kilobytes/sec]
msgid "KB/s"
@@ -2254,11 +2254,11 @@ msgstr "Nome do arquivo ou caminho para a chave HTTPS."
#: sabnzbd/skintext.py:302
msgid "HTTPS Chain Certifcates"
msgstr ""
msgstr "Cadeia de Certificados HTTPS"
#: sabnzbd/skintext.py:303
msgid "File name or path to HTTPS Chain."
msgstr ""
msgstr "Nome de arquivo ou caminho da Cadeia HTTPS."
#: sabnzbd/skintext.py:304
msgid "Tuning"
@@ -2769,7 +2769,7 @@ msgstr "Checar semanalmente por nova versão do SABnzbd."
#: sabnzbd/skintext.py:421 [Pick list for weekly test for new releases]
msgid "Also test releases"
msgstr ""
msgstr "Também versões de testes"
#: sabnzbd/skintext.py:422
msgid "Replace Spaces in Foldername"
@@ -3208,19 +3208,20 @@ msgstr "Enviar as notificações a NotifyOSD."
#: sabnzbd/skintext.py:557
msgid "Notification Center"
msgstr ""
msgstr "Centro de Notificações"
#: sabnzbd/skintext.py:558
msgid "Send notifications to Notification Center"
msgstr ""
msgstr "Envia notificações para o Centro de Notificações"
#: sabnzbd/skintext.py:559
msgid "Notification classes"
msgstr ""
msgstr "Classes de notificação"
#: sabnzbd/skintext.py:560
msgid "Enable classes of messages to be reported (none, one or multiple)"
msgstr ""
"Habilita classes de mensagens a serem relatadas (nenhuma, uma, ou múltiplas)"
#: sabnzbd/skintext.py:564
msgid ""

View File

@@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-09-10 17:12+0000\n"
"PO-Revision-Date: 2012-08-02 15:29+0000\n"
"PO-Revision-Date: 2012-10-13 17:23+0000\n"
"Last-Translator: nicusor <Unknown>\n"
"Language-Team: Romanian <ro@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-09-11 05:13+0000\n"
"X-Generator: Launchpad (build 15924)\n"
"X-Launchpad-Export-Date: 2012-10-14 04:48+0000\n"
"X-Generator: Launchpad (build 16137)\n"
#: SABnzbd.py:302 [Error message]
msgid "Failed to start web-interface"
@@ -55,7 +55,7 @@ msgstr "Dezactivează HTTPS din cauza lipsei fişierelor CERT şi KEY"
#: SABnzbd.py:1525
msgid "SABnzbd %s started"
msgstr ""
msgstr "SABnzbd %s pornit"
#: SABnzbd.py:1667 # sabnzbd/osxmenu.py:775
msgid "SABnzbd shutdown finished"
@@ -1517,7 +1517,7 @@ msgstr "Citeşte fluxuri RSS"
#: sabnzbd/skintext.py:65 [Config->Scheduler]
msgid "Remove failed jobs"
msgstr ""
msgstr "Elimină sarcini nereuşite"
#: sabnzbd/skintext.py:70 [Speed indicator kilobytes/sec]
msgid "KB/s"
@@ -2253,11 +2253,11 @@ msgstr "Nume fişier sau cale Cheie HTTPS."
#: sabnzbd/skintext.py:302
msgid "HTTPS Chain Certifcates"
msgstr ""
msgstr "Certificate Cheie HTTPS"
#: sabnzbd/skintext.py:303
msgid "File name or path to HTTPS Chain."
msgstr ""
msgstr "Nume fişier sau cale cheie HTTPS."
#: sabnzbd/skintext.py:304
msgid "Tuning"
@@ -2764,7 +2764,7 @@ msgstr "Verificare săptămânală versiuni noi SABnzbd."
#: sabnzbd/skintext.py:421 [Pick list for weekly test for new releases]
msgid "Also test releases"
msgstr ""
msgstr "Testeaza şi versiuni de încercare"
#: sabnzbd/skintext.py:422
msgid "Replace Spaces in Foldername"
@@ -3204,19 +3204,20 @@ msgstr "Trimite notificări către NotifyOSD"
#: sabnzbd/skintext.py:557
msgid "Notification Center"
msgstr ""
msgstr "Centru Notificări"
#: sabnzbd/skintext.py:558
msgid "Send notifications to Notification Center"
msgstr ""
msgstr "Trimite notificări la Centru Notificări"
#: sabnzbd/skintext.py:559
msgid "Notification classes"
msgstr ""
msgstr "Clase notificări"
#: sabnzbd/skintext.py:560
msgid "Enable classes of messages to be reported (none, one or multiple)"
msgstr ""
"Activează clasă mesaje ce vor fi raportate (niciunul, unul sau mai multe)"
#: sabnzbd/skintext.py:564
msgid ""

View File

@@ -1037,6 +1037,9 @@ def check_all_tasks():
# Check one-shot pause
sabnzbd.scheduler.pause_check()
# Check (and terminate) idle jobs
sabnzbd.nzbqueue.NzbQueue.do.stop_idle_jobs()
return True

View File

@@ -293,9 +293,11 @@ def _api_addfile(name, output, kwargs):
#Side effect of next line is that attribute .value is created
#which is needed to make add_nzbfile() work
size = name.length
else:
elif hasattr(name, 'value'):
size = len(name.value)
if name is not None and name.filename and size:
else:
size = 0
if name is not None and size and name.filename:
cat = kwargs.get('cat')
xcat = kwargs.get('xcat')
if not cat and xcat:
@@ -432,10 +434,10 @@ def _api_history(name, output, kwargs):
history_db = cherrypy.thread_data.history_db
if special in ('all', 'failed'):
if del_files:
del_job_files(history_db.get_failed_paths())
history_db.remove_failed()
del_job_files(history_db.get_failed_paths(search))
history_db.remove_failed(search)
if special in ('all', 'completed'):
history_db.remove_completed()
history_db.remove_completed(search)
return report(output)
elif value:
jobs = value.split(',')
@@ -556,10 +558,13 @@ def _api_auth(name, output, kwargs):
if not cfg.disable_key():
auth = 'badkey'
key = kwargs.get('key', '')
if key == cfg.nzb_key():
auth = 'nzbkey'
if key == cfg.api_key():
if not key:
auth = 'apikey'
else:
if key == cfg.nzb_key():
auth = 'nzbkey'
if key == cfg.api_key():
auth = 'apikey'
elif cfg.username() and cfg.password():
auth = 'login'
return report(output, keyword='auth', data=auth)
@@ -1102,7 +1107,7 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve
slot['mbdone_fmt'] = locale.format('%d', int(mb-mbleft), True)
slot['size'] = format_bytes(bytes)
slot['sizeleft'] = format_bytes(bytesleft)
if not Downloader.do.paused and status != 'Paused' and status != 'Fetching' and not found_active:
if not Downloader.do.paused and status not in (Status.PAUSED, Status.FETCHING) and not found_active:
if status == Status.CHECKING:
slot['status'] = Status.CHECKING
else:
@@ -1126,7 +1131,7 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve
slot['percentage'] = "%s" % (int(((mb-mbleft) / mb) * 100))
slot['missing'] = missing
if status in (Status.PAUSED, Status.CHECKING):
if Downloader.do.paused or Downloader.do.postproc or status not in (Status.DOWNLOADING, Status.QUEUED):
slot['timeleft'] = '0:00:00'
slot['eta'] = 'unknown'
else:
@@ -1539,7 +1544,8 @@ def build_header(prim, webdir=''):
if not color:
color = ''
header = { 'T': Ttemplate, 'Tspec': Tspec, 'Tx' : Ttemplate, 'version':sabnzbd.__version__, 'paused': Downloader.do.paused,
header = { 'T': Ttemplate, 'Tspec': Tspec, 'Tx' : Ttemplate, 'version':sabnzbd.__version__,
'paused': Downloader.do.paused or Downloader.do.postproc,
'pause_int': scheduler.pause_int(), 'paused_all': sabnzbd.PAUSED_ALL,
'uptime':uptime, 'color_scheme':color }
speed_limit = Downloader.do.get_limit()
@@ -1590,13 +1596,13 @@ def build_header(prim, webdir=''):
header['left_quota'] = to_units(BPSMeter.do.left)
status = ''
if Downloader.do.paused:
if Downloader.do.paused or Downloader.do.postproc:
status = Status.PAUSED
elif bytespersec > 0:
status = Status.DOWNLOADING
else:
status = 'Idle'
header['status'] = "%s" % status
header['status'] = status
anfo = ArticleCache.do.cache_info()

View File

@@ -80,6 +80,7 @@ email_dir = OptionDir('misc', 'email_dir', create=True)
email_rss = OptionBool('misc', 'email_rss', False)
version_check = OptionNumber('misc', 'check_new_rel', 1)
news_items = OptionBool('misc', 'news_items', True)
autobrowser = OptionBool('misc', 'auto_browser', True)
replace_illegal = OptionBool('misc', 'replace_illegal', True)
pre_script = OptionStr('misc', 'pre_script', 'None')
@@ -125,8 +126,10 @@ auto_sort = OptionBool('misc', 'auto_sort', False)
folder_rename = OptionBool('misc', 'folder_rename', True)
folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000)
pause_on_pwrar = OptionBool('misc', 'pause_on_pwrar', True)
prio_sort_list = OptionList('misc', 'prio_sort_list')
safe_postproc = OptionBool('misc', 'safe_postproc', True)
empty_postproc = OptionBool('misc', 'empty_postproc', False)
pause_on_post_processing = OptionBool('misc', 'pause_on_post_processing', False)
ampm = OptionBool('misc', 'ampm', False)
rss_filenames = OptionBool('misc', 'rss_filenames', False)

View File

@@ -224,7 +224,10 @@ class OptionList(Option):
error = None
if value is not None:
if not isinstance(value, list):
value = listquote.simplelist(value)
if '"' not in value and ',' not in value:
value = value.split()
else:
value = listquote.simplelist(value)
if self.__validation:
error, value = self.__validation(value)
if not error:

View File

@@ -43,6 +43,7 @@ PNFO_MISSING_FIELD = 18
QNFO_BYTES_FIELD = 0
QNFO_BYTES_LEFT_FIELD = 1
QNFO_PNFO_LIST_FIELD = 2
QNFO_Q_SIZE_LIST_FIELD = 3
ANFO_ARTICLE_SUM_FIELD = 0
ANFO_CACHE_SIZE_FIELD = 1

View File

@@ -51,6 +51,27 @@ def get_history_handle():
return HistoryDB(_HISTORY_DB)
def convert_search(search):
""" Convert classic wildcard to SQL wildcard """
if not search:
# Default value
search = ''
else:
# Allow * for wildcard matching and space
search = search.replace('*','%').replace(' ', '%')
# Allow ^ for start of string and $ for end of string
if search and search.startswith('^'):
search = search.replace('^','')
search += '%'
elif search and search.endswith('$'):
search = search.replace('$','')
search = '%' + search
else:
search = '%' + search + '%'
return search
# Note: Add support for execute return values
class HistoryDB(object):
@@ -141,19 +162,22 @@ class HistoryDB(object):
logging.error(Ta('Failed to close database, see log'))
logging.info("Traceback: ", exc_info = True)
def remove_completed(self):
return self.execute("""DELETE FROM history WHERE status = 'Completed'""", save=True)
def remove_completed(self, search=None):
search = convert_search(search)
return self.execute("""DELETE FROM history WHERE name LIKE ? AND status = 'Completed'""", (search,), save=True)
def get_failed_paths(self):
def get_failed_paths(self, search=None):
""" Return list of all storage paths of failed jobs (may contain non-existing or empty paths) """
fetch_ok = self.execute("""SELECT path FROM history WHERE status = 'Failed'""")
search = convert_search(search)
fetch_ok = self.execute("""SELECT path FROM history WHERE name LIKE ? AND status = 'Failed'""", (search,))
if fetch_ok:
return [item.get('path') for item in self.c.fetchall()]
else:
return []
def remove_failed(self):
return self.execute("""DELETE FROM history WHERE status = 'Failed'""", save=True)
def remove_failed(self, search=None):
search = convert_search(search)
return self.execute("""DELETE FROM history WHERE name LIKE ? AND status = 'Failed'""", (search,), save=True)
def remove_history(self, jobs=None):
if jobs is None:
@@ -180,22 +204,7 @@ class HistoryDB(object):
def fetch_history(self, start=None, limit=None, search=None, failed_only=0):
if not search:
# Default value
search = ''
else:
# Allow * for wildcard matching and space
search = search.replace('*','%').replace(' ', '%')
# Allow ^ for start of string and $ for end of string
if search and search.startswith('^'):
search = search.replace('^','')
search += '%'
elif search and search.endswith('$'):
search = search.replace('$','')
search = '%' + search
else:
search = '%' + search + '%'
search = convert_search(search)
# Get the number of results
if failed_only:

View File

@@ -1018,6 +1018,7 @@ class ConfigPage(object):
for svr in config.get_servers():
new[svr] = {}
conf['servers'] = new
conf['news_items'] = cfg.news_items()
conf['folders'] = sabnzbd.nzbqueue.scan_jobs(all=False, action=False)
@@ -1186,16 +1187,16 @@ class ConfigSwitches(object):
SPECIAL_BOOL_LIST = \
( 'start_paused', 'no_penalties', 'ignore_wrong_unrar', 'create_group_folders',
'queue_complete_pers', 'api_warnings', 'allow_64bit_tools', 'par2_multicore',
'never_repair', 'allow_streaming', 'ignore_unrar_dates', 'rss_filenames',
'never_repair', 'allow_streaming', 'ignore_unrar_dates', 'rss_filenames', 'news_items',
'osx_menu', 'osx_speed', 'win_menu', 'uniconfig', 'use_pickle', 'allow_incomplete_nzb',
'randomize_server_ip', 'no_ipv6', 'keep_awake', 'overwrite_files'
'randomize_server_ip', 'no_ipv6', 'keep_awake', 'overwrite_files', 'empty_postproc'
)
SPECIAL_VALUE_LIST = \
( 'size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker',
'req_completion_rate', 'wait_ext_drive', 'history_limit', 'show_sysload'
)
SPECIAL_LIST_LIST = \
( 'rss_odd_titles',
( 'rss_odd_titles', 'prio_sort_list'
)
class ConfigSpecial(object):

View File

@@ -1306,13 +1306,12 @@ def QuickCheck(set, nzo):
nzf_list = nzo.finished_files
for file in md5pack:
file = name_fixer(file)
if sabnzbd.misc.on_cleanup_list(file, False):
result = True
continue
found = False
for nzf in nzf_list:
if file == name_fixer(nzf.filename):
if file == nzf.filename:
found = True
if (nzf.md5sum is not None) and nzf.md5sum == md5pack[file]:
logging.debug('Quick-check of file %s OK', file)
@@ -1375,20 +1374,21 @@ def sfv_check(sfv_path):
root = os.path.split(sfv_path)[0]
for line in fp:
line = line.strip('\n\r ')
if line[0] != ';':
if line and line[0] != ';':
x = line.rfind(' ')
filename = platform_encode(line[:x].strip())
checksum = line[x:].strip()
path = os.path.join(root, filename)
if os.path.exists(path):
if crc_check(path, checksum):
logging.debug('File %s passed SFV check', path)
if x > 0:
filename = platform_encode(line[:x].strip())
checksum = line[x:].strip()
path = os.path.join(root, filename)
if os.path.exists(path):
if crc_check(path, checksum):
logging.debug('File %s passed SFV check', path)
else:
logging.info('File %s did not pass SFV check', latin1(path))
failed.append(unicoder(filename))
else:
logging.info('File %s did not pass SFV check', latin1(path))
logging.info('File %s missing in SFV check', latin1(path))
failed.append(unicoder(filename))
else:
logging.info('File %s missing in SFV check', latin1(path))
failed.append(unicoder(filename))
fp.close()
return failed
@@ -1415,6 +1415,7 @@ def crc_check(path, target_crc):
def analyse_show(name):
""" Do a quick SeasonSort check and return basic facts """
job = SeriesSorter(name, None, None)
job.match(force=True)
if job.is_match():
job.get_values()
info = job.show_info

View File

@@ -178,8 +178,9 @@ class NzbQueue(TryList):
logging.debug('Failed to find NZB file after pre-check (%s)', nzo.nzo_id)
return
from sabnzbd.dirscanner import ProcessSingleFile
nzo_id = ProcessSingleFile(os.path.split(nzb_path)[1], nzb_path, reuse=True)[1][0]
self.replace_in_q(nzo, nzo_id)
res, nzo_ids = ProcessSingleFile(nzo.work_name + '.nzb', nzb_path, reuse=True)
if res == 0 and nzo_ids:
self.replace_in_q(nzo, nzo_ids[0])
@synchronized(NZBQUEUE_LOCK)
@@ -189,8 +190,8 @@ class NzbQueue(TryList):
new_nzo = self.get_nzo(nzo_id)
pos = self.__nzo_list.index(new_nzo)
targetpos = self.__nzo_list.index(nzo)
self.__nzo_list.pop(pos)
self.__nzo_list[targetpos] = new_nzo
self.__nzo_list.pop(pos)
del self.__nzo_table[nzo.nzo_id]
del nzo
except:
@@ -741,7 +742,8 @@ class NzbQueue(TryList):
"""
if self.actives(grabs=False) < 2 and cfg.autodisconnect():
# This was the last job, close server connections
sabnzbd.downloader.Downloader.do.disconnect()
if sabnzbd.downloader.Downloader.do:
sabnzbd.downloader.Downloader.do.disconnect()
# Notify assembler to call postprocessor
if not nzo.deleted:
@@ -779,18 +781,24 @@ class NzbQueue(TryList):
def queue_info(self, for_cli=False, max_jobs=0):
bytes_left = 0
bytes = 0
q_size = 0
pnfo_list = []
n = 0
for nzo in self.__nzo_list:
pnfo = nzo.gather_info(for_cli = for_cli)
if nzo.status != 'Paused':
bytes += pnfo[PNFO_BYTES_FIELD]
bytes_left += pnfo[PNFO_BYTES_LEFT_FIELD]
pnfo_list.append(pnfo)
if not max_jobs or n < max_jobs:
pnfo = nzo.gather_info(for_cli = for_cli)
pnfo_list.append(pnfo)
if nzo.status != 'Paused':
bytes += pnfo[PNFO_BYTES_FIELD]
bytes_left += pnfo[PNFO_BYTES_LEFT_FIELD]
q_size += 1
elif nzo.status != 'Paused':
b, b_left = nzo.total_and_remaining()
bytes += b
bytes_left += b_left
q_size += 1
n += 1
if max_jobs and n >= max_jobs:
break
return (bytes, bytes_left, pnfo_list)
return (bytes, bytes_left, pnfo_list, q_size)
@synchronized(NZBQUEUE_LOCK)
@@ -819,6 +827,17 @@ class NzbQueue(TryList):
ArticleCache.do.purge_articles(nzo.saved_articles)
@synchronized(NZBQUEUE_LOCK)
def stop_idle_jobs(self):
""" Detect jobs that have zero files left and send them to post processing
"""
empty = []
for nzo in self.__nzo_list:
if not nzo.futuretype and not nzo.files and nzo.status not in (Status.PAUSED, Status.GRABBING):
empty.append(nzo)
for nzo in empty:
self.end_job(nzo)
def get_urls(self):
""" Return list of future-types needing URL """
lst = []

View File

@@ -1241,6 +1241,15 @@ class NzbObject(TryList):
bytes_left += nzf.bytes_left
return bytes_left
def total_and_remaining(self):
""" Return total and remaining bytes """
bytes = 0
bytes_left = 0
for nzf in self.files:
bytes += nzf.bytes
bytes_left += nzf.bytes_left
return bytes, bytes_left
def gather_info(self, for_cli = False):
bytes_left_all = 0
@@ -1383,8 +1392,9 @@ class NzbObject(TryList):
#-------------------------------------------------------------------------------
def nzf_get_filename(nzf):
# Return filename, if the filename not set, try the
# the full subject line instead. Can produce non-ideal results
""" Return filename, if the filename not set, try the
the full subject line instead. Can produce non-ideal results
"""
name = nzf.filename
if not name:
name = nzf.subject
@@ -1393,8 +1403,31 @@ def nzf_get_filename(nzf):
return name.lower()
def get_ext_list():
""" Return priority extenstion list, with extensions starting with a period
"""
exts = []
for ext in cfg.prio_sort_list():
ext = ext.strip()
if not ext.startswith('.'):
ext = '.' + ext
exts.append(ext)
return exts
def ext_on_list(name, lst):
""" Return True if `name` contains any extension in `lst`
"""
for ext in lst:
if name.rfind(ext) >= 0:
return True
return False
def nzf_cmp_date(nzf1, nzf2):
# Compare files based on date, but give vol-par files preference
""" Compare files based on date, but give vol-par files preference.
Wrapper needed, because `cmp` function doesn't handle extra parms.
"""
return nzf_cmp_name(nzf1, nzf2, name=False)
@@ -1422,6 +1455,16 @@ def nzf_cmp_name(nzf1, nzf2, name=True):
if is_par2 and not is_par1:
return -1
# Anything with a priority extention goes first
ext_list = get_ext_list()
if ext_list:
onlist1 = ext_on_list(name1, ext_list)
onlist2 = ext_on_list(name2, ext_list)
if onlist1 and not onlist2:
return -1
if onlist2 and not onlist1:
return 1
if name:
# Prioritise .rar files above any other type of file (other than vol-par)
# Useful for nzb streaming
@@ -1574,4 +1617,4 @@ def name_extractor(subject):
name = name.strip(' "')
if name and RE_NORMAL_NAME.search(name):
result = name
return result
return platform_encode(result)

View File

@@ -54,7 +54,7 @@ from sabnzbd.newzbin import Bookmarks
from sabnzbd.database import get_history_handle
from sabnzbd.encoding import unicoder
status_icons = {'idle':'../Resources/sab_idle.png','pause':'../Resources/sab_pause.png','clicked':'../Resources/sab_clicked.png'}
status_icons = {'idle':'../Resources/sab_idle.tiff','pause':'../Resources/sab_pause.tiff','clicked':'../Resources/sab_clicked.tiff'}
start_time = NSDate.date()
debug = 0
@@ -347,10 +347,7 @@ class SABnzbdDelegate(NSObject):
self.menu_queue.addItem_(menu_queue_item)
self.menu_queue.addItem_(NSMenuItem.separatorItem())
job_nb = 1
for pnfo in pnfo_list:
if job_nb > 10:
break
filename = unicoder(pnfo[PNFO_FILENAME_FIELD])
msgid = pnfo[PNFO_MSGID_FIELD]
bytesleft = pnfo[PNFO_BYTES_LEFT_FIELD] / MEBI
@@ -360,11 +357,10 @@ class SABnzbdDelegate(NSObject):
timeleft = self.calc_timeleft(bytesleftprogess, bpsnow)
job = "%s\t(%d/%d MB) %s" % (filename, bytesleft, bytes, timeleft)
job_nb += 1
menu_queue_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(job, '', '')
self.menu_queue.addItem_(menu_queue_item)
self.info = "%d nzb(s)\t( %d / %d MB )" % (len(pnfo_list),(qnfo[QNFO_BYTES_LEFT_FIELD] / MEBI), (qnfo[QNFO_BYTES_FIELD] / MEBI))
self.info = "%d nzb(s)\t( %d / %d MB )" % (qnfo[QNFO_Q_SIZE_LIST_FIELD],(qnfo[QNFO_BYTES_LEFT_FIELD] / MEBI), (qnfo[QNFO_BYTES_FIELD] / MEBI))
else:
menu_queue_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(T('Empty'), '', '')

View File

@@ -206,6 +206,8 @@ def process_job(nzo):
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 = ''
@@ -252,13 +254,15 @@ def process_job(nzo):
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
par_error = unpack_error = True
all_ok = False
all_ok = cfg.empty_postproc() and empty
if not all_ok:
par_error = unpack_error = True
script = nzo.script
cat = nzo.cat
@@ -392,7 +396,10 @@ def process_job(nzo):
logging.error(Ta('Error renaming "%s" to "%s"'), tmp_workdir_complete, workdir_complete)
logging.info("Traceback: ", exc_info = True)
job_result = int(par_error) + int(unpack_error)*2
if empty:
job_result = -1
else:
job_result = int(par_error) + int(unpack_error)*2
if cfg.ignore_samples() > 0:
remove_samples(workdir_complete)

View File

@@ -303,13 +303,7 @@ class RSSQueue(object):
regcount = len(regexes)
# Set first if this is the very first scan of this URI
if feed not in self.jobs:
self.jobs[feed] = {}
first = not bool(self.jobs[feed])
jobs = self.jobs[feed]
first = first and ignoreFirst
first = (feed not in self.jobs) and ignoreFirst
# Add sabnzbd's custom User Agent
feedparser.USER_AGENT = 'SABnzbd+/%s' % sabnzbd.version.__version__
@@ -319,6 +313,8 @@ class RSSQueue(object):
uri += '&dl=1'
# Read the RSS feed
msg = None
entries = None
if readout:
uri = uri.replace(' ', '%20')
logging.debug("Running feedparser on %s", uri)
@@ -343,6 +339,12 @@ class RSSQueue(object):
if not entries:
msg = Ta('RSS Feed %s was empty') % uri
logging.info(msg)
if feed not in self.jobs:
self.jobs[feed] = {}
jobs = self.jobs[feed]
if readout:
if not entries:
return unicoder(msg)
else:
entries = jobs.keys()
@@ -487,8 +489,7 @@ class RSSQueue(object):
for feed in feeds.keys():
try:
if feeds[feed].enable.get():
if not active:
logging.info('Starting scheduled RSS read-out')
logging.info('Starting scheduled RSS read-out for "%s"', feed)
active = True
self.run_feed(feed, download=True, ignoreFirst=True)
# Wait 15 seconds, else sites may get irritated
@@ -502,7 +503,7 @@ class RSSQueue(object):
pass
if active:
self.save()
logging.info('Finished scheduled RSS read-out')
logging.info('Finished scheduled RSS read-outs')
@synchronized(LOCK)
@@ -556,7 +557,7 @@ class RSSQueue(object):
self.jobs[feed][item]['status'] = 'D-'
RE_NEWZBIN = re.compile(r'(newz)(bin|xxx|bin2)\.[\w]+/browse/post/(\d+)', re.I)
RE_NEWZBIN = re.compile(r'(newz)(bin|xxx|bin2|xxx2)\.[\w]+/browse/post/(\d+)', re.I)
def _HandleLink(jobs, link, title, flag, orgcat, cat, pp, script, download, star, order,
priority=NORMAL_PRIORITY, rule=0):
@@ -617,7 +618,7 @@ def _get_link(uri, entry):
link = None
category = ''
uri = uri.lower()
if 'newzbin.' in uri or 'newzxxx.'in uri or 'newzbin2.' in uri:
if 'newzbin.' in uri or 'newzxxx.' in uri or 'newzbin2.' in uri or 'newzxxx2.' in uri:
link = entry.link
if not (link and '/post/' in link.lower()):
# Use alternative link

View File

@@ -94,7 +94,10 @@ class SABTrayThread(SysTrayIconThread):
# menu handler
def opencomplete(self, icon):
os.startfile(cfg.complete_dir.get_path())
try:
os.startfile(cfg.complete_dir.get_path())
except WindowsError:
pass
# menu handler
def browse(self, icon):

View File

@@ -625,6 +625,9 @@ class GenericSorter(object):
mapping.append(('%decade', self.movie_info['decade']))
mapping.append(('%0decade', self.movie_info['decade_two']))
# Original dir name
mapping.append(('%dn', self.original_dirname))
path = path_subst(sorter, mapping)
for key, name in REPLACE_AFTER.iteritems():

View File

@@ -166,7 +166,7 @@ class URLGrabber(Thread):
logging.error(msg)
misc.bad_fetch(future_nzo, clean_matrix_url(url), msg, retry=True)
continue
category = _MATRIX_MAP.get(category, category)
category = get_matrix_category(url, category)
if del_bookmark:
# No retries of nzbmatrix bookmark removals
@@ -398,13 +398,13 @@ _MATRIX_MAP = {
'13' : 'games.xbox',
'14' : 'games.xbox360',
'56' : 'games.xbox360 (other)',
'54' : 'movies.brrip',
'2' : 'movies.divx/xvid',
'1' : 'movies.dvd',
'50' : 'movies.hd (image)',
'1' : 'movies.sd (image)',
'2' : 'movies.sd',
'54' : 'movies.hd (remux)',
'42' : 'movies.hd (x264)',
'50' : 'movies.hd (image)',
'4' : 'movies.other',
'24' : 'music.dvd',
'24' : 'music.sd (image)',
'23' : 'music.lossless',
'22' : 'music.mp3, albums',
'47' : 'music.mp3, singles',
@@ -418,7 +418,7 @@ _MATRIX_MAP = {
'38' : 'other.iOS/iPhone',
'40' : 'other.other',
'26' : 'other.radio',
'5' : 'tv.dvd (image)',
'5' : 'tv.sd (image)',
'57' : 'tv.hd (image)',
'41' : 'tv.hd (x264)',
'8' : 'tv.other',
@@ -426,3 +426,9 @@ _MATRIX_MAP = {
'7' : 'tv.sport/ent'
}
def get_matrix_category(url, category):
category = _MATRIX_MAP.get(category, category)
if 'nzbxxx.com' in url:
return 'XXX: ' + category
else:
return category