added built-in unpack: 1) rar and 7-zip formats are supported (via external Unrar and 7-Zip executables); 2) new options <Unpack>, <UnpackPauseQueue>, <UnpackCleanupDisk>, <UnrarCmd>, <SevenZipCmd>; 3) web-interface now shows progress and estimated time during unpack (rar only; for 7-Zip progress is not available due to limitations of 7-Zip) 4) when built-in unpack is enabled, the post-processing script is called after unpack and possibly par-check/repair (if needed); 5) for nzb-files containing multiple collections (par-sets) the post-processing script is called only once, after the last par-set; 6) new parameter <NZBPP_UNPACKSTATUS> passed to post-processing script; 7) if the option <AllowReProcess> is enabled the post-processing-script is called after each par-set (as in previous versions); 8) example post-processing script updated: removed unrar-code, added check for unpack status; 9) new field <UnpackStatus> in result of RPC-method <history>; 10) history-dialog in web-interface shows three status: par-status, unpack-status, script-status; 11) with two built-in special post-processing parameters <*Unpack:> and <*Unpack:Password> the unpack can be disabled for individual nzb-file or the password can be set; 12) built-in special post-processing parameters can be set via web-interface on page <PP-Parameters> (when built-in unpack is enabled).

This commit is contained in:
Andrey Prygunkov
2013-02-06 22:04:50 +00:00
parent 68a73f96c4
commit 940448ffae
29 changed files with 1215 additions and 425 deletions

View File

@@ -82,7 +82,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
return false;
}
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 17);
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 18);
// save nzb-infos
SaveNZBList(pDownloadQueue, outfile);
@@ -136,7 +136,7 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (iFormatVersion < 3 || iFormatVersion > 17)
if (iFormatVersion < 3 || iFormatVersion > 18)
{
error("Could not load diskstate due to file version mismatch");
fclose(infile);
@@ -152,7 +152,7 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
if (iFormatVersion >= 7)
{
// load post-queue
if (!LoadPostQueue(pDownloadQueue, infile)) goto error;
if (!LoadPostQueue(pDownloadQueue, infile, iFormatVersion)) goto error;
}
else if (iFormatVersion < 7 && g_pOptions->GetReloadPostQueue())
{
@@ -204,8 +204,7 @@ void DiskState::SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile)
fprintf(outfile, "%s\n", pNZBInfo->GetName());
fprintf(outfile, "%s\n", pNZBInfo->GetCategory());
fprintf(outfile, "%i\n", pNZBInfo->GetPostProcess() ? 1 : 0);
fprintf(outfile, "%i\n", (int)pNZBInfo->GetParStatus());
fprintf(outfile, "%i\n", (int)pNZBInfo->GetScriptStatus());
fprintf(outfile, "%i,%i,%i\n", (int)pNZBInfo->GetParStatus(), (int)pNZBInfo->GetUnpackStatus(), (int)pNZBInfo->GetScriptStatus());
fprintf(outfile, "%i\n", pNZBInfo->GetFileCount());
fprintf(outfile, "%i\n", pNZBInfo->GetParkedFileCount());
@@ -301,20 +300,29 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
pNZBInfo->SetPostProcess(iPostProcess == 1);
}
if (iFormatVersion >= 8)
if (iFormatVersion >= 8 && iFormatVersion < 18)
{
int iParStatus;
if (fscanf(infile, "%i\n", &iParStatus) != 1) goto error;
pNZBInfo->SetParStatus((NZBInfo::EParStatus)iParStatus);
}
if (iFormatVersion >= 9)
if (iFormatVersion >= 9 && iFormatVersion < 18)
{
int iScriptStatus;
if (fscanf(infile, "%i\n", &iScriptStatus) != 1) goto error;
pNZBInfo->SetScriptStatus((NZBInfo::EScriptStatus)iScriptStatus);
}
if (iFormatVersion >= 18)
{
int iParStatus, iUnpackStatus, iScriptStatus;
if (fscanf(infile, "%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iScriptStatus) != 3) goto error;
pNZBInfo->SetParStatus((NZBInfo::EParStatus)iParStatus);
pNZBInfo->SetUnpackStatus((NZBInfo::EUnpackStatus)iUnpackStatus);
pNZBInfo->SetScriptStatus((NZBInfo::EScriptStatus)iScriptStatus);
}
int iFileCount;
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
pNZBInfo->SetFileCount(iFileCount);
@@ -610,7 +618,7 @@ void DiskState::SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile)
}
}
bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile)
bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion)
{
debug("Loading post-queue from disk");
@@ -625,6 +633,7 @@ bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile)
PostInfo* pPostInfo = NULL;
unsigned int iNZBIndex, iParCheck, iParStatus, iStage;
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iParCheck, &iParStatus, &iStage) != 4) goto error;
if (iFormatVersion < 18 && iStage > (int)PostInfo::ptVerifyingRepaired) iStage++;
if (!bSkipPostQueue)
{
@@ -1023,7 +1032,7 @@ bool DiskState::DiscardDownloadQueue()
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (3 <= iFormatVersion && iFormatVersion <= 17)
if (3 <= iFormatVersion && iFormatVersion <= 18)
{
// skip nzb-infos
int size = 0;
@@ -1046,14 +1055,18 @@ bool DiskState::DiscardDownloadQueue()
if (!fgets(buf, sizeof(buf), infile)) break; // category
if (!fgets(buf, sizeof(buf), infile)) break; // postprocess
}
if (iFormatVersion >= 8)
if (iFormatVersion >= 8 && iFormatVersion < 18)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ParStatus
}
if (iFormatVersion >= 9)
if (iFormatVersion >= 9 && iFormatVersion < 18)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ScriptStatus
}
if (iFormatVersion >= 18)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ParStatus, UnpackStatus, ScriptStatus
}
if (!fgets(buf, sizeof(buf), infile)) break; // file count
if (iFormatVersion >= 10)
{

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@ private:
void SaveFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* outfile);
bool LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* infile, int iFormatVersion);
void SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile);
bool LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
bool LoadOldPostQueue(DownloadQueue* pDownloadQueue);
void SaveUrlQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -130,7 +130,8 @@ NZBInfo::NZBInfo()
m_lSize = 0;
m_iRefCount = 0;
m_bPostProcess = false;
m_eParStatus = prNone;
m_eParStatus = psNone;
m_eUnpackStatus = usNone;
m_eScriptStatus = srNone;
m_bDeleted = false;
m_bParCleanup = false;
@@ -350,9 +351,8 @@ void NZBInfo::AppendMessage(Message::EKind eKind, time_t tTime, const char * szT
tTime = time(NULL);
}
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_Messages.push_back(pMessage);
m_mutexLog.Unlock();
}
@@ -610,6 +610,7 @@ PostInfo::PostInfo()
m_bDeleted = false;
m_bParCheck = false;
m_eParStatus = psNone;
m_eUnpackStatus = usNone;
m_eRequestParCheck = rpNone;
m_eScriptStatus = srNone;
m_szProgressLabel = strdup("");
@@ -696,9 +697,8 @@ void PostInfo::UnlockMessages()
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
{
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())

View File

@@ -216,10 +216,18 @@ class NZBInfo
public:
enum EParStatus
{
prNone,
prFailure,
prRepairPossible,
prSuccess
psNone,
psFailure,
psRepairPossible,
psSuccess
};
enum EUnpackStatus
{
usNone,
usSkipped,
usFailure,
usSuccess
};
enum EScriptStatus
@@ -246,6 +254,7 @@ private:
Files m_completedFiles;
bool m_bPostProcess;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
EScriptStatus m_eScriptStatus;
char* m_szQueuedFilename;
bool m_bDeleted;
@@ -289,6 +298,8 @@ public:
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
@@ -326,6 +337,7 @@ public:
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptUnpacking,
ptExecutingScript,
ptFinished
};
@@ -345,6 +357,14 @@ public:
rpAll
};
enum EUnpackStatus
{
usNone,
usSkipped,
usFailure,
usSuccess
};
enum EScriptStatus
{
srNone,
@@ -364,6 +384,7 @@ private:
bool m_bDeleted;
bool m_bParCheck;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
EScriptStatus m_eScriptStatus;
ERequestParCheck m_eRequestParCheck;
EStage m_eStage;
@@ -410,6 +431,8 @@ public:
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
ERequestParCheck GetRequestParCheck() { return m_eRequestParCheck; }
void SetRequestParCheck(ERequestParCheck eRequestParCheck) { m_eRequestParCheck = eRequestParCheck; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }

View File

@@ -34,7 +34,7 @@ nzbget_SOURCES = \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd nzbget-postprocess.sh \

View File

@@ -97,7 +97,8 @@ am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
ScriptController.$(OBJEXT) ServerPool.$(OBJEXT) \
svn_version.$(OBJEXT) TLS.$(OBJEXT) Thread.$(OBJEXT) \
Util.$(OBJEXT) XmlRpc.$(OBJEXT) WebDownloader.$(OBJEXT) \
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) nzbget.$(OBJEXT)
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) Unpack.$(OBJEXT) \
nzbget.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
@@ -254,7 +255,7 @@ nzbget_SOURCES = \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd nzbget-postprocess.sh \
@@ -456,6 +457,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TLS.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Unpack.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UrlCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebDownloader.Po@am__quote@

View File

@@ -171,6 +171,11 @@ static const char* OPTION_MERGENZB = "MergeNzb";
static const char* OPTION_PARTIMELIMIT = "ParTimeLimit";
static const char* OPTION_KEEPHISTORY = "KeepHistory";
static const char* OPTION_ACCURATERATE = "AccurateRate";
static const char* OPTION_UNPACK = "Unpack";
static const char* OPTION_UNPACKCLEANUPDISK = "UnpackCleanupDisk";
static const char* OPTION_UNRARCMD = "UnrarCmd";
static const char* OPTION_SEVENZIPCMD = "SevenZipCmd";
static const char* OPTION_UNPACKPAUSEQUEUE = "UnpackPauseQueue";
// obsolete options
static const char* OPTION_POSTLOGKIND = "PostLogKind";
@@ -431,6 +436,11 @@ Options::Options(int argc, char* argv[])
m_bAccurateRate = false;
m_EMatchMode = mmID;
m_tResumeTime = 0;
m_bUnpack = false;
m_bUnpackCleanupDisk = false;
m_szUnrarCmd = NULL;
m_szSevenZipCmd = NULL;
m_bUnpackPauseQueue = false;
// Option "ConfigFile" will be initialized later, but we want
// to see it at the top of option list, so we add it first
@@ -603,6 +613,14 @@ Options::~Options()
{
free(m_szAddNZBFilename);
}
if (m_szUnrarCmd)
{
free(m_szUnrarCmd);
}
if (m_szSevenZipCmd)
{
free(m_szSevenZipCmd);
}
for (NameList::iterator it = m_EditQueueNameList.begin(); it != m_EditQueueNameList.end(); it++)
{
@@ -731,6 +749,16 @@ void Options::InitDefault()
SetOption(OPTION_PARTIMELIMIT, "0");
SetOption(OPTION_KEEPHISTORY, "7");
SetOption(OPTION_ACCURATERATE, "no");
SetOption(OPTION_UNPACK, "no");
SetOption(OPTION_UNPACKCLEANUPDISK, "no");
#ifdef WIN32
SetOption(OPTION_UNRARCMD, "unrar.exe");
SetOption(OPTION_SEVENZIPCMD, "7z.exe");
#else
SetOption(OPTION_UNRARCMD, "unrar");
SetOption(OPTION_SEVENZIPCMD, "7z");
#endif
SetOption(OPTION_UNPACKPAUSEQUEUE, "no");
}
void Options::InitOptFile()
@@ -858,6 +886,8 @@ void Options::InitOptions()
m_szLockFile = strdup(GetOption(OPTION_LOCKFILE));
m_szDaemonUserName = strdup(GetOption(OPTION_DAEMONUSERNAME));
m_szLogFile = strdup(GetOption(OPTION_LOGFILE));
m_szUnrarCmd = strdup(GetOption(OPTION_UNRARCMD));
m_szSevenZipCmd = strdup(GetOption(OPTION_SEVENZIPCMD));
m_iDownloadRate = (int)(ParseFloatValue(OPTION_DOWNLOADRATE) * 1024);
m_iConnectionTimeout = ParseIntValue(OPTION_CONNECTIONTIMEOUT, 10);
@@ -912,6 +942,9 @@ void Options::InitOptions()
m_bMergeNzb = (bool)ParseEnumValue(OPTION_MERGENZB, BoolCount, BoolNames, BoolValues);
m_bAccurateRate = (bool)ParseEnumValue(OPTION_ACCURATERATE, BoolCount, BoolNames, BoolValues);
m_bSecureControl = (bool)ParseEnumValue(OPTION_SECURECONTROL, BoolCount, BoolNames, BoolValues);
m_bUnpack = (bool)ParseEnumValue(OPTION_UNPACK, BoolCount, BoolNames, BoolValues);
m_bUnpackCleanupDisk = (bool)ParseEnumValue(OPTION_UNPACKCLEANUPDISK, BoolCount, BoolNames, BoolValues);
m_bUnpackPauseQueue = (bool)ParseEnumValue(OPTION_UNPACKPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
const char* OutputModeNames[] = { "loggable", "logable", "log", "colored", "color", "ncurses", "curses" };
const int OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
@@ -2350,6 +2383,13 @@ void Options::CheckOptions()
{
m_bDirectWrite = false;
}
if (m_bUnpack && m_bAllowReProcess)
{
LocateOptionSrcPos(OPTION_ALLOWREPROCESS);
ConfigError("Options \"%s\" and \"%s\" cannot be both active at the same time", OPTION_UNPACK, OPTION_ALLOWREPROCESS);
m_bAllowReProcess = false;
}
}
void Options::ParseFileIDList(int argc, char* argv[], int optind)

View File

@@ -255,6 +255,11 @@ private:
int m_iParTimeLimit;
int m_iKeepHistory;
bool m_bAccurateRate;
bool m_bUnpack;
bool m_bUnpackCleanupDisk;
char* m_szUnrarCmd;
char* m_szSevenZipCmd;
bool m_bUnpackPauseQueue;
// Parsed command-line parameters
bool m_bServerMode;
@@ -405,6 +410,12 @@ public:
int GetParTimeLimit() { return m_iParTimeLimit; }
int GetKeepHistory() { return m_iKeepHistory; }
bool GetAccurateRate() { return m_bAccurateRate; }
bool GetUnpack() { return m_bUnpack; }
bool GetUnpackCleanupDisk() { return m_bUnpackCleanupDisk; }
const char* GetUnrarCmd() { return m_szUnrarCmd; }
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
Category* FindCategory(const char* szName) { return m_Categories.FindCategory(szName); }
// Parsed command-line parameters

View File

@@ -130,13 +130,22 @@ void ParCoordinator::PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
bool ParCoordinator::FindMainPars(const char* szPath, FileList* pFileList)
{
pFileList->clear();
if (pFileList)
{
pFileList->clear();
}
DirBrowser dir(szPath);
while (const char* filename = dir.Next())
{
int iBaseLen = 0;
if (ParseParFilename(filename, &iBaseLen, NULL))
{
if (!pFileList)
{
return true;
}
// check if the base file already added to list
bool exists = false;
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
@@ -154,7 +163,7 @@ bool ParCoordinator::FindMainPars(const char* szPath, FileList* pFileList)
}
}
}
return !pFileList->empty();
return pFileList && !pFileList->empty();
}
bool ParCoordinator::SameParCollection(const char* szFilename1, const char* szFilename2)
@@ -226,14 +235,6 @@ bool ParCoordinator::ParseParFilename(const char* szParFilename, int* iBaseNameL
*/
void ParCoordinator::StartParJob(PostInfo* pPostInfo)
{
if (g_pOptions->GetParPauseQueue())
{
if (PauseDownload())
{
info("Pausing queue before par-check");
}
}
info("Checking pars for %s", pPostInfo->GetInfoName());
m_ParChecker.SetPostInfo(pPostInfo);
m_ParChecker.SetParFilename(pPostInfo->GetParFilename());
@@ -357,23 +358,23 @@ void ParCoordinator::ParCheckerUpdate(Subject* Caller, void* Aspect)
if (m_ParChecker.GetStatus() == ParChecker::psFailed && !m_ParChecker.GetCancelled())
{
pPostInfo->SetParStatus(PostInfo::psFailure);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::prFailure);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
}
else if (m_ParChecker.GetStatus() == ParChecker::psFinished &&
(g_pOptions->GetParRepair() || m_ParChecker.GetRepairNotNeeded()))
{
pPostInfo->SetParStatus(PostInfo::psSuccess);
if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prNone)
if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psNone)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::prSuccess);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
}
}
else
{
pPostInfo->SetParStatus(PostInfo::psRepairPossible);
if (pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::prFailure)
if (pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::prRepairPossible);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psRepairPossible);
}
}
@@ -383,14 +384,6 @@ void ParCoordinator::ParCheckerUpdate(Subject* Caller, void* Aspect)
}
g_pQueueCoordinator->UnlockQueue();
if (g_pOptions->GetParPauseQueue() && !(g_pOptions->GetPostPauseQueue() && m_bPostScript))
{
if (UnpauseDownload())
{
info("Unpausing queue after par-check");
}
}
}
}

View File

@@ -87,9 +87,9 @@ public:
public:
ParCoordinator();
virtual ~ParCoordinator();
bool FindMainPars(const char* szPath, FileList* pFileList);
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
bool SameParCollection(const char* szFilename1, const char* szFilename2);
static bool FindMainPars(const char* szPath, FileList* pFileList);
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
static bool SameParCollection(const char* szFilename1, const char* szFilename2);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
#ifndef DISABLE_PARCHECK

View File

@@ -49,6 +49,7 @@
#include "DiskState.h"
#include "Util.h"
#include "Scheduler.h"
#include "Unpack.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
@@ -182,7 +183,9 @@ void PrePostProcessor::Stop()
if (!pDownloadQueue->GetPostQueue()->empty())
{
PostInfo* pPostInfo = pDownloadQueue->GetPostQueue()->front();
if (pPostInfo->GetStage() == PostInfo::ptExecutingScript && pPostInfo->GetScriptThread())
if ((pPostInfo->GetStage() == PostInfo::ptUnpacking ||
pPostInfo->GetStage() == PostInfo::ptExecutingScript) &&
pPostInfo->GetScriptThread())
{
Thread* pScriptThread = pPostInfo->GetScriptThread();
pPostInfo->SetScriptThread(NULL);
@@ -261,7 +264,7 @@ void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZ
#else
bool bParCheck = g_pOptions->GetParCheck() && g_pOptions->GetDecode();
#endif
if ((bParCheck || m_bPostScript) &&
if ((bParCheck || m_bPostScript || g_pOptions->GetUnpack()) &&
CreatePostJobs(pDownloadQueue, pNZBInfo, bParCheck, true, false))
{
pNZBInfo->SetPostProcess(true);
@@ -505,24 +508,18 @@ void PrePostProcessor::CheckPostQueue()
if (pPostInfo->GetParCheck() && pPostInfo->GetParStatus() == PostInfo::psNone && !g_pOptions->GetPausePostProcess())
{
UpdatePauseState(g_pOptions->GetParPauseQueue(), "par-check");
m_ParCoordinator.StartParJob(pPostInfo);
}
else
#endif
if (pPostInfo->GetStage() == PostInfo::ptQueued && !g_pOptions->GetPausePostProcess())
{
StartScriptJob(pDownloadQueue, pPostInfo);
StartProcessJob(pDownloadQueue, pPostInfo);
}
else if (pPostInfo->GetStage() == PostInfo::ptFinished)
{
if (m_bPostScript && g_pOptions->GetPostPauseQueue())
{
if (UnpauseDownload())
{
info("Unpausing queue after post-process-script");
}
}
UpdatePauseState(false, NULL);
JobCompleted(pDownloadQueue, pPostInfo);
}
else if (!g_pOptions->GetPausePostProcess())
@@ -565,17 +562,31 @@ void PrePostProcessor::SanitisePostQueue(PostQueue* pPostQueue)
}
}
void PrePostProcessor::StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
void PrePostProcessor::StartProcessJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
{
if (!m_bPostScript)
bool bUnpack = g_pOptions->GetUnpack() && (pPostInfo->GetUnpackStatus() == PostInfo::usNone);
bool bNZBFileCompleted = IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, false);
bool bHasFailedParJobs = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psFailure ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psRepairPossible;
if (bUnpack && bHasFailedParJobs)
{
warn("Skipping unpack due to par-failure for %s", pPostInfo->GetInfoName());
pPostInfo->SetUnpackStatus(PostInfo::usSkipped);
pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
bUnpack = false;
}
if ((!bNZBFileCompleted && !(m_bPostScript && g_pOptions->GetAllowReProcess())) ||
(!bUnpack && !m_bPostScript))
{
pPostInfo->SetStage(PostInfo::ptFinished);
return;
}
pPostInfo->SetProgressLabel("Executing post-process-script");
pPostInfo->SetProgressLabel(bUnpack ? "Unpacking" : "Executing post-process-script");
pPostInfo->SetWorking(true);
pPostInfo->SetStage(PostInfo::ptExecutingScript);
pPostInfo->SetStage(bUnpack ? PostInfo::ptUnpacking : PostInfo::ptExecutingScript);
pPostInfo->SetFileProgress(0);
pPostInfo->SetStageProgress(0);
SaveQueue(pDownloadQueue);
@@ -586,19 +597,16 @@ void PrePostProcessor::StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* p
}
pPostInfo->SetStageTime(time(NULL));
bool bNZBFileCompleted = IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, false);
bool bHasFailedParJobs = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prFailure ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prRepairPossible;
if (g_pOptions->GetPostPauseQueue())
if (bUnpack)
{
if (PauseDownload())
{
info("Pausing queue before post-process-script");
}
UpdatePauseState(g_pOptions->GetUnpackPauseQueue(), "unpack");
UnpackController::StartUnpackJob(pPostInfo);
}
else
{
UpdatePauseState(g_pOptions->GetPostPauseQueue(), "post-process-script");
PostScriptController::StartScriptJob(pPostInfo, bNZBFileCompleted, bHasFailedParJobs);
}
PostScriptController::StartScriptJob(pPostInfo, g_pOptions->GetPostProcess(), bNZBFileCompleted, bHasFailedParJobs);
}
void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
@@ -632,8 +640,8 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
if (IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, false))
{
// Cleaning up queue if all par-checks were successful or all scripts were successful
bool bCanCleanupQueue = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prSuccess ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prRepairPossible ||
bool bCanCleanupQueue = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psRepairPossible ||
pPostInfo->GetNZBInfo()->GetScriptStatus() == NZBInfo::srSuccess;
if ((g_pOptions->GetParCleanupQueue() || g_pOptions->GetNzbCleanupDisk()) && bCanCleanupQueue)
{
@@ -719,7 +727,7 @@ bool PrePostProcessor::IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo
}
bool PrePostProcessor::CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bParCheck, bool bPostScript, bool bAddTop)
bool bParCheck, bool bUnpackOrScript, bool bAddTop)
{
debug("Queueing post-process-jobs");
@@ -750,8 +758,12 @@ bool PrePostProcessor::CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pN
char szParInfoName[1024];
snprintf(szParInfoName, 1024, "%s%c%s", pNZBInfo->GetName(), (int)PATH_SEPARATOR, szInfoName);
szParInfoName[1024-1] = '\0';
info("Queueing %s%c%s for par-check", pNZBInfo->GetName(), (int)PATH_SEPARATOR, szInfoName);
if (cPostQueue.empty())
{
info("Queueing %s for post-processing", pNZBInfo->GetName());
}
PostInfo* pPostInfo = new PostInfo();
pPostInfo->SetNZBInfo(pNZBInfo);
pPostInfo->SetParFilename(szFullParFilename);
@@ -771,9 +783,9 @@ bool PrePostProcessor::CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pN
}
}
if (cPostQueue.empty() && bPostScript && m_bPostScript)
if (cPostQueue.empty() && bUnpackOrScript && (m_bPostScript || g_pOptions->GetUnpack()))
{
info("Queueing %s for post-process-script", pNZBInfo->GetName());
info("Queueing %s for post-processing", pNZBInfo->GetName());
PostInfo* pPostInfo = new PostInfo();
pPostInfo->SetNZBInfo(pNZBInfo);
pPostInfo->SetParFilename("");
@@ -846,6 +858,26 @@ void PrePostProcessor::ApplySchedulerState()
}
}
void PrePostProcessor::UpdatePauseState(bool bNeedPause, const char* szReason)
{
if (bNeedPause)
{
if (PauseDownload())
{
info("Pausing queue before %s", szReason);
}
}
else if (m_bPostPause)
{
if (UnpauseDownload())
{
info("Unpausing queue after %s", m_szPauseReason);
}
}
m_szPauseReason = szReason;
}
bool PrePostProcessor::PauseDownload()
{
debug("PrePostProcessor::PauseDownload()");
@@ -944,7 +976,7 @@ bool PrePostProcessor::PostQueueDelete(IDList* pIDList)
#endif
if (pPostInfo->GetScriptThread())
{
debug("Terminating post-process-script for %s", pPostInfo->GetInfoName());
debug("Terminating %s for %s", (pPostInfo->GetStage() == PostInfo::ptUnpacking ? "unpack" : "post-process-script"), pPostInfo->GetInfoName());
pPostInfo->GetScriptThread()->Stop();
bOK = true;
}
@@ -1155,8 +1187,9 @@ bool PrePostProcessor::HistoryReturn(IDList* pIDList, bool bReprocess)
// reset postprocessing status variables
pNZBInfo->SetPostProcess(false);
pNZBInfo->SetParStatus(NZBInfo::prNone);
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetParCleanup(false);
pNZBInfo->SetUnpackStatus(NZBInfo::usNone);
pNZBInfo->SetScriptStatus(NZBInfo::srNone);
pNZBInfo->SetParkedFileCount(0);
}

View File

@@ -76,24 +76,26 @@ private:
bool m_bSchedulerPause;
bool m_bPostPause;
Scanner m_Scanner;
const char* m_szPauseReason;
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
void CheckPostQueue();
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartProcessJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SaveQueue(DownloadQueue* pDownloadQueue);
void SanitisePostQueue(PostQueue* pPostQueue);
void CheckDiskSpace();
void ApplySchedulerState();
void CheckScheduledResume();
void UpdatePauseState(bool bNeedPause, const char* szReason);
bool PauseDownload();
bool UnpauseDownload();
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
bool CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bParCheck, bool bPostScript, bool bAddTop);
bool CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bParCheck, bool bUnpackOrScript, bool bAddTop);
void DeleteQueuedFile(const char* szQueuedFile);
NZBInfo* MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool PostQueueMove(IDList* pIDList, EEditAction eAction, int iOffset);

View File

@@ -446,6 +446,9 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
// if the file doesn't have any articles left for download, we store that fact and search again,
// ignoring all files which were previously marked as not having any articles.
// special case: if the file has ExtraPriority-flag set, it has the highest priority and the
// Paused-flag is ignored.
//debug("QueueCoordinator::GetNextArticle()");
bool bOK = false;

View File

@@ -46,6 +46,7 @@
#include "nzbget.h"
#include "RemoteClient.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
@@ -1026,15 +1027,14 @@ bool RemoteClient::RequestPostQueue()
int iStageProgress = ntohl(pPostQueueAnswer->m_iStageProgress);
static const int EXECUTING_SCRIPT = 5;
char szCompleted[100];
szCompleted[0] = '\0';
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != EXECUTING_SCRIPT)
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != (int)PostInfo::ptExecutingScript)
{
sprintf(szCompleted, ", %i%s", (int)(iStageProgress / 10), "%");
}
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Executing postprocess-script", "" };
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Unpacking", ", Executing postprocess-script", "" };
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) + ntohl(pPostQueueAnswer->m_iParFilename);
printf("[%i] %s%s%s\n", ntohl(pPostQueueAnswer->m_iID), szInfoName, szPostStageName[ntohl(pPostQueueAnswer->m_iStage)], szCompleted);

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include "nzbget.h"
#include "ScriptController.h"
@@ -243,12 +244,6 @@ void ScriptController::PrepareEnvironmentStrings()
int ScriptController::Execute()
{
if (!Util::FileExists(m_szScript))
{
error("Could not start %s: could not find file %s", m_szInfoName, m_szScript);
return -1;
}
PrepareEnvironmentStrings();
int iExitCode = 0;
@@ -299,8 +294,7 @@ int ScriptController::Execute()
DWORD dwErrCode = GetLastError();
char szErrMsg[255];
szErrMsg[255-1] = '\0';
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL, dwErrCode, 0, szErrMsg, 255, NULL))
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrCode, 0, szErrMsg, 255, NULL))
{
error("Could not start %s: %s", m_szInfoName, szErrMsg);
}
@@ -308,6 +302,10 @@ int ScriptController::Execute()
{
error("Could not start %s: error %i", m_szInfoName, dwErrCode);
}
if (!Util::FileExists(m_szScript))
{
error("Could not find file %s", m_szScript);
}
free(szEnvironmentStrings);
return -1;
}
@@ -370,6 +368,7 @@ int ScriptController::Execute()
fflush(stdout);
#endif
chdir(m_szWorkingDir);
execve(m_szScript, (char* const*)m_szArgs, (char* const*)pEnvironmentStrings);
fprintf(stdout, "[ERROR] Could not start script: %s", strerror(errno));
fflush(stdout);
@@ -409,7 +408,7 @@ int ScriptController::Execute()
debug("Entering pipe-loop");
while (!feof(readpipe) && !m_bTerminated)
{
if (fgets(buf, 10240, readpipe))
if (ReadLine(buf, 10240, readpipe))
{
#ifdef CHILD_WATCHDOG
if (!bChildConfirmed)
@@ -503,6 +502,11 @@ void ScriptController::Terminate()
debug("Stopped %s", m_szInfoName);
}
bool ScriptController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{
return fgets(szBuf, iBufSize, pStream);
}
void ScriptController::ProcessOutput(char* szText)
{
debug("Processing output received from script");
@@ -517,23 +521,23 @@ void ScriptController::ProcessOutput(char* szText)
if (!strncmp(szText, "[INFO] ", 7))
{
AddMessage(Message::mkInfo, false, g_pOptions->GetInfoTarget(), szText + 7);
AddMessage(Message::mkInfo, false, szText + 7);
}
else if (!strncmp(szText, "[WARNING] ", 10))
{
AddMessage(Message::mkWarning, false, g_pOptions->GetWarningTarget(), szText + 10);
AddMessage(Message::mkWarning, false, szText + 10);
}
else if (!strncmp(szText, "[ERROR] ", 8))
{
AddMessage(Message::mkError, false, g_pOptions->GetErrorTarget(), szText + 8);
AddMessage(Message::mkError, false, szText + 8);
}
else if (!strncmp(szText, "[DETAIL] ", 9))
{
AddMessage(Message::mkDetail, false, g_pOptions->GetDetailTarget(), szText + 9);
AddMessage(Message::mkDetail, false, szText + 9);
}
else if (!strncmp(szText, "[DEBUG] ", 8))
{
AddMessage(Message::mkDebug, false, g_pOptions->GetDebugTarget(), szText + 8);
AddMessage(Message::mkDebug, false, szText + 8);
}
else
{
@@ -543,23 +547,23 @@ void ScriptController::ProcessOutput(char* szText)
break;
case Options::slDetail:
AddMessage(Message::mkDetail, true, g_pOptions->GetDetailTarget(), szText);
AddMessage(Message::mkDetail, true, szText);
break;
case Options::slInfo:
AddMessage(Message::mkInfo, true, g_pOptions->GetInfoTarget(), szText);
AddMessage(Message::mkInfo, true, szText);
break;
case Options::slWarning:
AddMessage(Message::mkWarning, true, g_pOptions->GetWarningTarget(), szText);
AddMessage(Message::mkWarning, true, szText);
break;
case Options::slError:
AddMessage(Message::mkError, true, g_pOptions->GetErrorTarget(), szText);
AddMessage(Message::mkError, true, szText);
break;
case Options::slDebug:
AddMessage(Message::mkDebug, true, g_pOptions->GetDebugTarget(), szText);
AddMessage(Message::mkDebug, true, szText);
break;
}
}
@@ -567,7 +571,7 @@ void ScriptController::ProcessOutput(char* szText)
debug("Processing output received from script - completed");
}
void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
switch (eKind)
{
@@ -593,13 +597,26 @@ void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Optio
}
}
void PostScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
void ScriptController::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, szFormat);
vsnprintf(tmp2, 1024, szFormat, ap);
tmp2[1024-1] = '\0';
va_end(ap);
AddMessage(eKind, false, tmp2);
}
void PostScriptController::StartScriptJob(PostInfo* pPostInfo, bool bNZBFileCompleted, bool bHasFailedParJobs)
{
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
PostScriptController* pScriptController = new PostScriptController();
pScriptController->m_pPostInfo = pPostInfo;
pScriptController->SetScript(szScript);
pScriptController->SetScript(g_pOptions->GetPostProcess());
pScriptController->SetWorkingDir(g_pOptions->GetDestDir());
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
@@ -620,9 +637,14 @@ void PostScriptController::Run()
szNZBName[1024-1] = '\0';
char szParStatus[10];
snprintf(szParStatus, 10, "%i", m_pPostInfo->GetParStatus());
snprintf(szParStatus, 10, "%i", g_pOptions->GetAllowReProcess() ? (int)m_pPostInfo->GetParStatus() : (int)m_pPostInfo->GetNZBInfo()->GetParStatus());
szParStatus[10-1] = '\0';
int iUnpackStatus[] = { 0, 0, 1, 2 };
char szUnpackStatus[10];
snprintf(szUnpackStatus, 10, "%i", g_pOptions->GetAllowReProcess() ? 0 : iUnpackStatus[m_pPostInfo->GetNZBInfo()->GetUnpackStatus()]);
szUnpackStatus[10-1] = '\0';
char szCollectionCompleted[10];
snprintf(szCollectionCompleted, 10, "%i", (int)m_bNZBFileCompleted);
szCollectionCompleted[10-1] = '\0';
@@ -648,7 +670,7 @@ void PostScriptController::Run()
szCategory[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "post-process-script for %s", m_pPostInfo->GetInfoName());
snprintf(szInfoName, 1024, "post-process-script for %s", g_pOptions->GetAllowReProcess() ? m_pPostInfo->GetInfoName() : m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
@@ -672,6 +694,7 @@ void PostScriptController::Run()
SetEnvVar("NZBPP_NZBFILENAME", szNZBFilename);
SetEnvVar("NZBPP_PARFILENAME", szParFilename);
SetEnvVar("NZBPP_PARSTATUS", szParStatus);
SetEnvVar("NZBPP_UNPACKSTATUS", szUnpackStatus);
SetEnvVar("NZBPP_NZBCOMPLETED", szCollectionCompleted);
SetEnvVar("NZBPP_PARFAILED", szHasFailedParJobs);
SetEnvVar("NZBPP_CATEGORY", szCategory);
@@ -689,14 +712,17 @@ void PostScriptController::Run()
int iResult = Execute();
szInfoName[0] = 'P'; // uppercase
switch (iResult)
{
case POSTPROCESS_SUCCESS:
info("%s sucessful", szInfoName);
info("%s successful", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
break;
case POSTPROCESS_ERROR:
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
info("%s failed", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
break;
@@ -755,26 +781,18 @@ void PostScriptController::Run()
m_pPostInfo->SetWorking(false);
}
void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
if (!strncmp(szText, "[HISTORY] ", 10))
{
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText + 10);
}
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText + 10);
}
else
{
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(eKind, szText);
}
ScriptController::AddMessage(eKind, bDefaultKind, szText);
m_pPostInfo->AppendMessage(eKind, szText);
}
if (g_pOptions->GetPausePostProcess())
{
time_t tStageTime = m_pPostInfo->GetStageTime();
@@ -854,7 +872,7 @@ void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBF
delete pScriptController;
}
void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
if (!strncmp(szText, "[NZB] ", 6))
{
@@ -890,7 +908,7 @@ void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Op
}
else
{
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
ScriptController::AddMessage(eKind, bDefaultKind, szText);
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -70,11 +70,14 @@ private:
pid_t m_hProcess;
#endif
void ProcessOutput(char* szText);
void PrepareEnvironmentStrings();
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
void ProcessOutput(char* szText);
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
bool GetTerminated() { return m_bTerminated; }
public:
ScriptController();
@@ -93,7 +96,7 @@ public:
void SetEnvVar(const char* szName, const char* szValue);
};
class PostScriptController : public Thread, ScriptController
class PostScriptController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
@@ -101,13 +104,12 @@ private:
bool m_bHasFailedParJobs;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
bool bNZBFileCompleted, bool bHasFailedParJobs);
static void StartScriptJob(PostInfo* pPostInfo, bool bNZBFileCompleted, bool bHasFailedParJobs);
};
class NZBScriptController : public ScriptController
@@ -118,13 +120,13 @@ private:
NZBParameterList* m_pParameterList;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
public:
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList);
};
class NZBAddedScriptController : public Thread, ScriptController
class NZBAddedScriptController : public Thread, public ScriptController
{
private:
char* m_szNZBName;
@@ -134,7 +136,7 @@ public:
static void StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript);
};
class SchedulerScriptController : public Thread, ScriptController
class SchedulerScriptController : public Thread, public ScriptController
{
public:
virtual void Run();

539
Unpack.cpp Normal file
View File

@@ -0,0 +1,539 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#endif
#include "nzbget.h"
#include "Unpack.h"
#include "Log.h"
#include "Util.h"
#include "ParCoordinator.h"
extern Options* g_pOptions;
extern DownloadQueueHolder* g_pDownloadQueueHolder;
UnpackController::~UnpackController()
{
for (FileList::iterator it = m_archiveFiles.begin(); it != m_archiveFiles.end(); it++)
{
free(*it);
}
}
void UnpackController::StartUnpackJob(PostInfo* pPostInfo)
{
UnpackController* pUnpackController = new UnpackController();
pUnpackController->m_pPostInfo = pPostInfo;
pUnpackController->SetAutoDestroy(false);
pPostInfo->SetScriptThread(pUnpackController);
pUnpackController->Start();
}
void UnpackController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
char szName[1024];
strncpy(szName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szName[1024-1] = '\0';
bool bUnpack = true;
m_szPassword[0] = '\0';
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
if (!strcasecmp(pParameter->GetName(), "*Unpack:") && !strcasecmp(pParameter->GetValue(), "no"))
{
bUnpack = false;
}
if (!strcasecmp(pParameter->GetName(), "*Unpack:Password"))
{
strncpy(m_szPassword, pParameter->GetValue(), 1024-1);
m_szPassword[1024-1] = '\0';
}
}
g_pDownloadQueueHolder->UnlockQueue();
snprintf(m_szInfoName, 1024, "unpack for %s", szName);
m_szInfoName[1024-1] = '\0';
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", szName); // first letter in upper case
m_szInfoNameUp[1024-1] = '\0';
#ifndef DISABLE_PARCHECK
if (bUnpack && HasBrokenFiles() && !m_pPostInfo->GetParCheck() && HasParFiles())
{
info("%s has broken files", szName);
RequestParCheck();
m_pPostInfo->SetWorking(false);
return;
}
#endif
if (bUnpack)
{
CheckArchiveFiles();
}
if (bUnpack && (m_bHasRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
{
SetInfoName(m_szInfoName);
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
SetWorkingDir(m_szDestDir);
PrintMessage(Message::mkInfo, "Unpacking %s", szName);
CreateUnpackDir();
m_bUnpackOK = true;
m_bUnpackStartError = false;
if (m_bHasRarFiles)
{
m_pPostInfo->SetProgressLabel("");
ExecuteUnrar();
}
if (m_bHasSevenZipFiles && m_bUnpackOK)
{
m_pPostInfo->SetProgressLabel("");
ExecuteSevenZip(false);
}
if (m_bHasSevenZipMultiFiles && m_bUnpackOK)
{
m_pPostInfo->SetProgressLabel("");
ExecuteSevenZip(true);
}
Completed();
}
else
{
PrintMessage(Message::mkInfo, (bUnpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), szName);
#ifndef DISABLE_PARCHECK
if (bUnpack && !m_pPostInfo->GetParCheck() && HasParFiles())
{
RequestParCheck();
}
else
#endif
{
m_pPostInfo->SetUnpackStatus(PostInfo::usSkipped);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
m_pPostInfo->SetWorking(false);
}
void UnpackController::ExecuteUnrar()
{
// Format:
// unrar x -y -p- -o+ *.rar ./_unpack
char szPasswordParam[1024];
const char* szArgs[8];
szArgs[0] = g_pOptions->GetUnrarCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
szArgs[4] = "-o+";
szArgs[5] = "*.rar";
szArgs[6] = "./_unpack";
szArgs[7] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetUnrarCmd());
SetDefaultKindPrefix("Unrar: ");
m_bAllOKMessageReceived = false;
m_eUnpacker = upUnrar;
int iExitCode = Execute();
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "Unrar error code: %i", iExitCode);
}
}
void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
{
// Format:
// 7z x -y -p- -o./_unpack *.7z
// OR
// 7z x -y -p- -o./_unpack *.7z.001
char szPasswordParam[1024];
const char* szArgs[7];
szArgs[0] = g_pOptions->GetSevenZipCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
szArgs[4] = "-o./_unpack";
szArgs[5] = bMultiVolumes ? "*.7z.001" : "*.7z";
szArgs[6] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetSevenZipCmd());
SetDefaultKindPrefix("7-Zip: ");
m_bAllOKMessageReceived = false;
m_eUnpacker = upSevenZip;
PrintMessage(Message::mkInfo, "Executing 7-Zip");
int iExitCode = Execute();
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "7-Zip error code: %i", iExitCode);
}
}
void UnpackController::Completed()
{
bool bCleanupSuccess = Cleanup();
if (m_bUnpackOK && bCleanupSuccess)
{
PrintMessage(Message::mkInfo, "%s %s", m_szInfoNameUp, "successful");
m_pPostInfo->SetUnpackStatus(PostInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSuccess);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
else
{
#ifndef DISABLE_PARCHECK
if (!m_bUnpackOK && !m_pPostInfo->GetParCheck() && !m_bUnpackStartError && !GetTerminated() && HasParFiles())
{
RequestParCheck();
}
else
#endif
{
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->SetUnpackStatus(PostInfo::usFailure);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
}
#ifndef DISABLE_PARCHECK
void UnpackController::RequestParCheck()
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", m_szInfoNameUp);
m_pPostInfo->SetRequestParCheck(PostInfo::rpAll);
m_pPostInfo->SetStage(PostInfo::ptFinished);
}
#endif
bool UnpackController::HasParFiles()
{
return ParCoordinator::FindMainPars(m_szDestDir, NULL);
}
bool UnpackController::HasBrokenFiles()
{
char szBrokenLog[1024];
snprintf(szBrokenLog, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_brokenlog.txt");
szBrokenLog[1024-1] = '\0';
return Util::FileExists(szBrokenLog);
}
void UnpackController::CreateUnpackDir()
{
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_unpack");
m_szUnpackDir[1024-1] = '\0';
Util::ForceDirectories(m_szUnpackDir);
}
void UnpackController::CheckArchiveFiles()
{
m_bHasRarFiles = false;
m_bHasSevenZipFiles = false;
m_bHasSevenZipMultiFiles = false;
RegEx regExRar(".*\\.rar$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
{
if (regExRar.Match(filename))
{
m_bHasRarFiles = true;
}
if (regExSevenZip.Match(filename))
{
m_bHasSevenZipFiles = true;
}
if (regExSevenZipMulti.Match(filename))
{
m_bHasSevenZipMultiFiles = true;
}
}
}
}
bool UnpackController::Cleanup()
{
// By success:
// - move unpacked files to destination dir;
// - remove _unpack-dir;
// - delete archive-files.
// By failure:
// - remove _unpack-dir.
bool bOK = true;
if (m_bUnpackOK)
{
// moving files back
DirBrowser dir(m_szUnpackDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
snprintf(szDstFile, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szDstFile[1024-1] = '\0';
// silently overwrite existing files
remove(szDstFile);
if (!Util::MoveFile(szSrcFile, szDstFile))
{
PrintMessage(Message::mkError, "Could not move file %s to %s", szSrcFile, szDstFile);
bOK = false;
}
}
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szUnpackDir))
{
PrintMessage(Message::mkError, "Could not remove temporary directory %s", m_szUnpackDir);
}
if (m_bUnpackOK && bOK && g_pOptions->GetUnpackCleanupDisk())
{
PrintMessage(Message::mkInfo, "Deleting archive files");
// Delete rar-files (only files which were used by unrar)
for (FileList::iterator it = m_archiveFiles.begin(); it != m_archiveFiles.end(); it++)
{
char* szFilename = *it;
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szFilename);
szFullFilename[1024-1] = '\0';
PrintMessage(Message::mkDetail, "Deleting file %s", szFilename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
// Unfortunately 7-Zip doesn't print the processed archive-files to the output.
// Therefore we don't know for sure which files were extracted.
// We just delete all 7z-files in the directory.
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
{
if (regExSevenZip.Match(filename))
{
PrintMessage(Message::mkDetail, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
}
}
}
return bOK;
}
/**
* Unrar prints progress information into the same line using backspace control character.
* In order to print progress continuously we analyze the output after every char
* and update post-job progress information.
*/
bool UnpackController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{
bool bPrinted = false;
int i = 0;
for (; i < iBufSize - 1; i++)
{
int ch = fgetc(pStream);
szBuf[i] = ch;
szBuf[i+1] = '\0';
if (ch == EOF)
{
break;
}
if (ch == '\n')
{
i++;
break;
}
char* szBackspace = strrchr(szBuf, '\b');
if (szBackspace)
{
if (!bPrinted)
{
char tmp[1024];
strncpy(tmp, szBuf, 1024);
tmp[1024-1] = '\0';
char* szTmpPercent = strrchr(tmp, '\b');
if (szTmpPercent)
{
*szTmpPercent = '\0';
}
if (strncmp(szBuf, "...", 3))
{
ProcessOutput(tmp);
}
bPrinted = true;
}
if (strchr(szBackspace, '%'))
{
int iPercent = atoi(szBackspace + 1);
m_pPostInfo->SetStageProgress(iPercent * 10);
}
}
}
szBuf[i] = '\0';
if (bPrinted)
{
szBuf[0] = '\0';
}
return i > 0;
}
void UnpackController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
ScriptController::AddMessage(eKind, bDefaultKind, szText);
m_pPostInfo->AppendMessage(eKind, szText);
if (m_eUnpacker == upUnrar && !strncmp(szText, "Extracting from ", 16))
{
const char *szFilename = szText + 16;
debug("Filename: %s", szFilename);
m_archiveFiles.push_back(strdup(szFilename));
m_pPostInfo->SetProgressLabel(szText);
}
if (m_eUnpacker == upUnrar && !strncmp(szText, "Extracting ", 11))
{
m_pPostInfo->SetProgressLabel(szText);
}
if ((m_eUnpacker == upUnrar && !strncmp(szText, "All OK", 6)) ||
(m_eUnpacker == upSevenZip && !strncmp(szText, "Everything is Ok", 16)))
{
m_bAllOKMessageReceived = true;
}
}
void UnpackController::Stop()
{
debug("Stopping unpack");
Thread::Stop();
Terminate();
}

87
Unpack.h Normal file
View File

@@ -0,0 +1,87 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef UNPACK_H
#define UNPACK_H
#include <deque>
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "ScriptController.h"
class UnpackController : public Thread, public ScriptController
{
private:
enum EUnpacker
{
upUnrar,
upSevenZip
};
private:
PostInfo* m_pPostInfo;
char m_szInfoName[1024];
char m_szInfoNameUp[1024];
char m_szDestDir[1024];
char m_szUnpackDir[1024];
char m_szPassword[1024];
bool m_bAllOKMessageReceived;
bool m_bNoFilesMessageReceived;
bool m_bHasRarFiles;
bool m_bHasSevenZipFiles;
bool m_bHasSevenZipMultiFiles;
bool m_bUnpackOK;
bool m_bUnpackStartError;
EUnpacker m_eUnpacker;
typedef std::deque<char*> FileList;
FileList m_archiveFiles;
protected:
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
void ExecuteUnrar();
void ExecuteSevenZip(bool bMultiVolumes);
void Completed();
void CreateUnpackDir();
bool Cleanup();
bool HasParFiles();
bool HasBrokenFiles();
void CheckArchiveFiles();
#ifndef DISABLE_PARCHECK
void RequestParCheck();
#endif
public:
virtual ~UnpackController();
virtual void Run();
virtual void Stop();
static void StartUnpackJob(PostInfo* pPostInfo);
};
#endif

View File

@@ -599,6 +599,42 @@ bool Util::CreateDirectory(const char* szDirFilename)
return DirectoryExists(szDirFilename);
}
bool Util::RemoveDirectory(const char* szDirFilename)
{
#ifdef WIN32
return ::RemoveDirectory(szDirFilename);
#else
return remove(szDirFilename) == 0;
#endif
}
bool Util::DeleteDirectoryWithContent(const char* szDirFilename)
{
bool bOK = true;
DirBrowser dir(szDirFilename);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDirFilename, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
if (Util::DirectoryExists(szFullFilename))
{
bOK &= DeleteDirectoryWithContent(szFullFilename);
}
else
{
bOK &= remove(szFullFilename) == 0;
}
}
}
return bOK && RemoveDirectory(szDirFilename);
}
long long Util::FileSize(const char* szFilename)
{
#ifdef WIN32

2
Util.h
View File

@@ -71,6 +71,8 @@ public:
static bool FileExists(const char* szFilename);
static bool DirectoryExists(const char* szDirFilename);
static bool CreateDirectory(const char* szDirFilename);
static bool RemoveDirectory(const char* szDirFilename);
static bool DeleteDirectoryWithContent(const char* szDirFilename);
static bool ForceDirectories(const char* szPath);
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
static bool SetCurrentDirectory(const char* szDirFilename);

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1629,7 +1629,7 @@ void PostQueueXmlCommand::Execute()
{
PostInfo* pPostInfo = *it;
const char* szPostStageName[] = { "QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING", "VERIFYING_REPAIRED", "EXECUTING_SCRIPT", "FINISHED" };
const char* szPostStageName[] = { "QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING", "VERIFYING_REPAIRED", "UNPACKING", "EXECUTING_SCRIPT", "FINISHED" };
char* xmlNZBNicename = EncodeStr(pPostInfo->GetNZBInfo()->GetName());
char* xmlNZBFilename = EncodeStr(pPostInfo->GetNZBInfo()->GetFilename());
@@ -1786,6 +1786,7 @@ void HistoryXmlCommand::Execute()
"<member><name>DestDir</name><value><string>%s</string></value></member>\n"
"<member><name>Category</name><value><string>%s</string></value></member>\n"
"<member><name>ParStatus</name><value><string>%s</string></value></member>\n"
"<member><name>UnpackStatus</name><value><string>%s</string></value></member>\n"
"<member><name>ScriptStatus</name><value><string>%s</string></value></member>\n"
"<member><name>FileSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>FileSizeHi</name><value><i4>%u</i4></value></member>\n"
@@ -1816,6 +1817,7 @@ void HistoryXmlCommand::Execute()
"\"DestDir\" : \"%s\",\n"
"\"Category\" : \"%s\",\n"
"\"ParStatus\" : \"%s\",\n"
"\"UnpackStatus\" : \"%s\",\n"
"\"ScriptStatus\" : \"%s\",\n"
"\"FileSizeLo\" : %u,\n"
"\"FileSizeHi\" : %u,\n"
@@ -1864,6 +1866,7 @@ void HistoryXmlCommand::Execute()
"}";
const char* szParStatusName[] = { "NONE", "FAILURE", "REPAIR_POSSIBLE", "SUCCESS" };
const char* szUnpackStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS" };
const char* szScriptStatusName[] = { "NONE", "UNKNOWN", "FAILURE", "SUCCESS" };
const char* szUrlStatusName[] = { "UNKNOWN", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN" };
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
@@ -1899,9 +1902,10 @@ void HistoryXmlCommand::Execute()
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START,
pHistoryInfo->GetID(), pHistoryInfo->GetID(), "NZB", xmlNicename, xmlNicename, xmlNZBFilename,
xmlDestDir, xmlCategory, szParStatusName[pNZBInfo->GetParStatus()],
szScriptStatusName[pNZBInfo->GetScriptStatus()], iFileSizeLo, iFileSizeHi, iFileSizeMB,
pNZBInfo->GetFileCount(), pNZBInfo->GetParkedFileCount(), pHistoryInfo->GetTime(), "", "");
xmlDestDir, xmlCategory, szParStatusName[pNZBInfo->GetParStatus()],
szUnpackStatusName[pNZBInfo->GetUnpackStatus()], szScriptStatusName[pNZBInfo->GetScriptStatus()],
iFileSizeLo, iFileSizeHi, iFileSizeMB, pNZBInfo->GetFileCount(),
pNZBInfo->GetParkedFileCount(), pHistoryInfo->GetTime(), "", "");
free(xmlDestDir);
}

View File

@@ -1,10 +1,10 @@
#
# This file if part of nzbget
#
# Template configuration file for postprocessing script "nzbget-postprocess.sh".
# Template configuration file for post-processing script "nzbget-postprocess.sh".
# Please refer to "nzbget-postprocess.sh" for usage instructions.
#
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,45 +22,29 @@
#
#
##############################################################################
### PATHS ###
# Set the full path to unrar if it is not in your PATH.
UnrarCmd=unrar
##############################################################################
### OPTIONS ###
# Delete rar-files after unpacking (yes, no).
DeleteRarFiles=yes
# Rename img-files to iso (yes, no).
RenameIMG=yes
# Joint TS-files (yes, no).
JoinTS=no
JoinTS=yes
##############################################################################
### POSTPROCESSING-PARAMETERS ###
# This section defines parameters, which can be set for each nzb-file
# individually using either web-interface or command line.
# Example command line for setting parameter "password" to value "123" for
# Example command line for setting parameter "PostProcess" to value "no" for
# nzb-file with id=2:
# nzbget -E G O Password=123 2
# nzbget -E G O PostProcess=no 2
# Perform postprocessing (yes, no).
#
# Set to "no" to skip postprocessing for this nzb-file.
PostProcess=yes
# Password for encrypted posts.
#
# If the post requires a password for unpacking.
Password=
# Destination directory.
#
# NOTE: NZBGet must have write-access-rights for that directory.
DestDir=

View File

@@ -6,7 +6,7 @@
#
# Copyright (C) 2008 Peter Roubos <peterroubos@hotmail.com>
# Copyright (C) 2008 Otmar Werner
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,8 +25,7 @@
#
####################### Usage instructions #######################
# o Script will unrar downloaded rar files, join ts-files and rename img-files
# to iso.
# o Script will cleanup, join ts-files and rename img-files to iso.
#
# o To use this script with nzbget set the option "PostProcess" in
# nzbget configuration file to point to this script file. E.g.:
@@ -41,41 +40,29 @@
#
# o There are few options, which can be ajdusted for each nzb-file individually.
#
# o The script supports the feature called "delayed par-check".
# That means it can try to unpack downloaded files without par-checking
# them fisrt. Only if unpack fails, the script schedules par-check,
# then unpacks again.
# To use delayed par-check set following options in nzbget configuration file:
# ParCheck=no
# ParRepair=yes
# LoadPars=one (or) LoadPars=all
#
# o If you want to par-check/repair all files before trying to unpack them,
# set option "ParCheck=yes".
#
####################### End of Usage instructions #######################
# NZBGet passes following arguments to postprocess-programm as environment
# variables:
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
# NZBPP_NZBFILENAME - name of processed nzb-file;
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
# found);
# NZBPP_NZBNAME - user-friendly name of processed nzb-file as it is displayed
# by the program. The file path and extension are removed.
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - name of processed nzb-file. It includes file extension and also
# may include full path;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check disabled or nzb-file does
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled;
# NZBPP_NZBCOMPLETED - state of nzb-job:
# 0 = there are more collections in this nzb-file queued;
# 1 = this was the last collection in nzb-file;
# NZBPP_PARFAILED - indication of failed par-jobs for current nzb-file:
# 0 = no failed par-jobs;
# 1 = current par-job or any of the previous par-jobs for
# the same nzb-files failed;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string).
# 3 = checked and can be repaired but repair is disabled.
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
# Name of script's configuration file
@@ -99,7 +86,7 @@ fi
# (for current nzb-file) via web-interface or via command line with
# "nzbget -E G O PostProcess=no <ID>"
if [ "$NZBPR_PostProcess" = "no" ]; then
echo "[WARNING] Post-Process: Postprocessing disabled for this nzb-file, exiting"
echo "[WARNING] Post-Process: Post-processing disabled for this nzb-file, exiting"
exit $POSTPROCESS_NONE
fi
@@ -129,27 +116,11 @@ if [ "$NZBOP_ALLOWREPROCESS" = "yes" ]; then
BadConfig=1
fi
if [ "$NZBOP_LOADPARS" = "none" ]; then
echo "[ERROR] Post-Process: Please set option \"LoadPars\" to \"One\" or \"All\" in nzbget configuration file"
BadConfig=1
fi
if [ "$NZBOP_PARREPAIR" = "no" ]; then
echo "[ERROR] Post-Process: Please set option \"ParRepair\" to \"Yes\" in nzbget configuration file"
BadConfig=1
fi
if [ "$BadConfig" -eq 1 ]; then
echo "[ERROR] Post-Process: Exiting because of not compatible nzbget configuration"
echo "[ERROR] Post-Process: Exiting due to incompatible nzbget configuration"
exit $POSTPROCESS_ERROR
fi
# Check if all collections in nzb-file were downloaded
if [ ! "$NZBPP_NZBCOMPLETED" -eq 1 ]; then
echo "[INFO] Post-Process: Not the last collection in nzb-file, exiting"
exit $POSTPROCESS_SUCCESS
fi
# Check par status
if [ "$NZBPP_PARSTATUS" -eq 1 -o "$NZBPP_PARSTATUS" -eq 3 -o "$NZBPP_PARFAILED" -eq 1 ]; then
if [ "$NZBPP_PARSTATUS" -eq 3 ]; then
@@ -157,7 +128,13 @@ if [ "$NZBPP_PARSTATUS" -eq 1 -o "$NZBPP_PARSTATUS" -eq 3 -o "$NZBPP_PARFAILED"
else
echo "[WARNING] Post-Process: Par-check failed, exiting"
fi
exit $POSTPROCESS_ERROR
exit $POSTPROCESS_NONE
fi
# Check unpack status
if [ "$NZBPP_UNPACKSTATUS" -ne 2 ]; then
echo "[WARNING] Post-Process: Unpack failed or disabled, exiting"
exit $POSTPROCESS_NONE
fi
# Check if destination directory exists (important for reprocessing of history items)
@@ -168,109 +145,8 @@ fi
cd "$NZBPP_DIRECTORY"
# If not just repaired and file "_brokenlog.txt" exists, the collection is damaged
# exiting with returning code $POSTPROCESS_PARCHECK_ALL to request par-repair
if [ ! "$NZBPP_PARSTATUS" -eq 2 ]; then
if [ -f "_brokenlog.txt" ]; then
if (ls *.[pP][aA][rR]2 >/dev/null 2>&1); then
echo "[INFO] Post-Process: Brokenlog found, requesting par-repair"
exit $POSTPROCESS_PARCHECK_ALL
fi
fi
fi
# All checks done, now processing the files
# Flag indicates that something was unrared
Unrared=0
# Unrar the files (if any) to the temporary directory, if there are no rar files this will do nothing
if (ls *.rar >/dev/null 2>&1); then
# Check if unrar exists
$UnrarCmd >/dev/null 2>&1
if [ "$?" -eq 127 ]; then
echo "[ERROR] Post-Process: Unrar not found. Set the path to unrar in script's configuration"
exit $POSTPROCESS_ERROR
fi
# Make a temporary directory to store the unrarred files
ExtractedDirExists=0
if [ -d extracted ]; then
ExtractedDirExists=1
else
mkdir extracted
fi
echo "[INFO] Post-Process: Unraring"
rarpasswordparam=""
if [ "$NZBPR_Password" != "" ]; then
rarpasswordparam="-p$NZBPR_Password"
fi
$UnrarCmd x -y -p- "$rarpasswordparam" -o+ "*.rar" ./extracted/
if [ "$?" -ne 0 ]; then
echo "[ERROR] Post-Process: Unrar failed"
if [ "$ExtractedDirExists" -eq 0 ]; then
rm -R extracted
fi
# for delayed par-check/-repair at least one par-file must be already downloaded
if (ls *.[pP][aA][rR]2 >/dev/null 2>&1); then
echo "[INFO] Post-Process: Requesting par-repair"
exit $POSTPROCESS_PARCHECK_ALL
fi
exit $POSTPROCESS_ERROR
fi
Unrared=1
# Remove the rar files
if [ "$DeleteRarFiles" = "yes" ]; then
echo "[INFO] Post-Process: Deleting rar-files"
rm *.r[0-9][0-9] >/dev/null 2>&1
rm *.rar >/dev/null 2>&1
rm *.s[0-9][0-9] >/dev/null 2>&1
fi
# Go to the temp directory and try to unrar again.
# If there are any rars inside the extracted rars then these will no also be unrarred
cd extracted
if (ls *.rar >/dev/null 2>&1); then
echo "[INFO] Post-Process: Unraring (second pass)"
$UnrarCmd x -y -p- -o+ "*.rar"
if [ "$?" -ne 0 ]; then
echo "[INFO] Post-Process: Unrar (second pass) failed"
exit $POSTPROCESS_ERROR
fi
# Delete the Rar files
if [ "$DeleteRarFiles" = "yes" ]; then
echo "[INFO] Post-Process: Deleting rar-files (second pass)"
rm *.r[0-9][0-9] >/dev/null 2>&1
rm *.rar >/dev/null 2>&1
rm *.s[0-9][0-9] >/dev/null 2>&1
fi
fi
# Move everything back to the Download folder
mv * ..
cd ..
rmdir extracted
fi
# If there were nothing to unrar and the download was not par-checked,
# we don't know if it's OK. To be sure we force par-check.
# In particular that helps with downloads containing renamed rar-files.
# The par-repair will rename files to correct names, then we can unpack.
if [ "$Unrared" -eq 0 -a "$NZBPP_PARSTATUS" -eq 0 ]; then
if (ls *.[pP][aA][rR]2 >/dev/null 2>&1); then
echo "[INFO] Post-Process: No rar-files found, requesting par-check"
exit $POSTPROCESS_PARCHECK_ALL
fi
fi
# If download contains only nzb-files move them into nzb-directory
# for further download
# Check if command "wc" exists
@@ -291,10 +167,7 @@ rm *.nzb >/dev/null 2>&1
rm *.sfv >/dev/null 2>&1
rm *.1 >/dev/null 2>&1
rm _brokenlog.txt >/dev/null 2>&1
if [ "$Unrared" -eq 1 ]; then
# Delete par2-file only if there were files for unpacking.
rm *.[pP][aA][rR]2 >/dev/null 2>&1
fi
rm *.[pP][aA][rR]2 >/dev/null 2>&1
if [ "$JoinTS" = "yes" ]; then
# Join any split .ts files if they are named xxxx.0000.ts xxxx.0001.ts

View File

@@ -66,7 +66,6 @@ LogFile=${DestDir}/nzbget.log
# it is also used to serve JSON-/XML-RPC requests.
WebDir=
##############################################################################
### NEWS-SERVERS ###
@@ -738,7 +737,11 @@ UpdateInterval=200
# Paused files remain in queue and can be unpaused by parchecker when needed.
LoadPars=one
# Automatic par-verification (yes, no).
# Force par-verification (yes, no).
#
# Force par-check for every download. When set to "no" the par-check is
# performed only if the unpacker or the post-processing script detect a
# damaged download.
#
# To download only needed par2-files (smart par-files loading) set also
# the option <LoadPars> to "one". If option <LoadPars> is set to "all",
@@ -751,7 +754,7 @@ ParCheck=no
#
# If option <ParCheck> is enabled and <ParRepair> is not, the program
# only verifies downloaded files and downloads needed par2-files, but does
# not start repair-process. This is useful if the server does not have
# not start repair-process. This is useful if computer does not have
# enough CPU power, since repairing of large files may take too much
# resources and time on a slow computers.
ParRepair=yes
@@ -823,7 +826,7 @@ ParTimeLimit=0
# NOTE: If parchecker needs additional par-files it temporarily unpauses
# the queue.
#
# NOTE: See also option <PostPauseQueue>.
# NOTE: See also options <PostPauseQueue> and <UnpackPauseQueue>.
ParPauseQueue=no
# Cleanup download queue after successful check/repair (yes, no).
@@ -839,38 +842,110 @@ ParCleanupQueue=yes
NzbCleanupDisk=no
##############################################################################
### UNPACK ###
# Unpack downloaded nzb-files (yes, no).
#
# If the download is damaged and could not be repaired using par-files
# the unpacking is not performed.
#
# If the option <ParCheck> is disabled the program will try to unpack
# downloaded files first. If the unpacking fails the par-check/repair
# is performed and the unpack will be executed again.
Unpack=yes
# Pause download queue during unpack (yes, no).
#
# Enable the option to give CPU more time for unpacking. That helps
# to speed up unpacking on slow CPUs.
#
# NOTE: See also options <ParPauseQueue> and <PostPauseQueue>.
UnpackPauseQueue=no
# Delete archive files after successful unpacking (yes, no).
UnpackCleanupDisk=yes
# Full path to unrar executable.
#
# Example: "/usr/bin/unrar".
#
# If unrar is in your PATH you may leave the path part and set only
# the executable name ("unrar" on POSIX or "unrar.exe" on Windows).
UnrarCmd=unrar
# Full path to 7-Zip executable.
#
# Example: "/usr/bin/7z".
#
# If 7-Zip binary is in your PATH you may leave the path part and set only
# the executable name ("7z" or "7za" on POSIX or "7z.exe" on Windows).
SevenZipCmd=7z
##############################################################################
### POST-PROCESSING ###
# Set path to program, that must be executed after the download of nzb-file
# or one collection in nzb-file is completed and possibly par-checked/repaired.
# is completed and possibly par-checked/repaired and unpacked, depending
# on other options.
#
# Example: "PostProcess=~/nzbget-postprocess.sh".
#
# NOTE: An example script for unrarring is provided within distribution
# in file "nzbget-postprocess.sh".
# NOTE: An example script is provided within distribution in file
# "nzbget-postprocess.sh".
#
# NOTE: Since version 10.0 NZBGet has a built-in support for unpack
# (option <Unpack>). In the previous versions unpack was performed by
# post-processing scripts. If you use a script created for older NZBGet
# version you need to disable the built-in unpack for script to operate
# properly.
#
# INFO FOR DEVELOPERS:
# NZBGet passes following arguments to postprocess-program as environment
# If the option <AllowReProcess> is disabled (that's the default setting)
# the post-processing script is executed once per nzb-file after
# par-check/repair (if needed) and unpacking (if enabled).
#
# If the option <AllowReProcess> is active the post-processing script is
# executed for each collection within nzb-file (for nzb-files having multiple
# collections but at least once). Depending on option <ParCheck> the collection
# could be already par-checked/repaired or the script could be called without
# par-check taken place. In the latest case if the script detects errors
# (such as unpack failed) it has an ability to request par-check from
# NZBGet. After par-check/repair NZBGet calls the script once again.
#
# NZBGet passes following arguments to post-processing script as environment
# variables:
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
# NZBPP_NZBFILENAME - name of processed nzb-file;
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
# found);
# NZBPP_NZBNAME - user-friendly name of processed nzb-file as it is displayed
# by the program. The file path and extension are removed.
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - name of processed nzb-file. It includes file extension and also
# may include full path;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check disabled or nzb-file does
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled;
# NZBPP_NZBCOMPLETED - state of nzb-job:
# 0 = there are more collections in this nzb-file queued;
# 1 = this was the last collection in nzb-file;
# 3 = checked and can be repaired but repair is disabled.
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
#
# In addition the following arguments are passed if the option <AllowReProcess>
# is active:
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
# found);
# NZBPP_PARFAILED - indication of failed par-jobs for current nzb-file:
# 0 = no failed par-jobs;
# 1 = current par-job or any of the previous par-jobs for
# the same nzb-files failed;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string).
# NZBPP_NZBCOMPLETED - state of nzb-job:
# 0 = there are more collections in this nzb-file queued;
# 1 = this was the last collection in nzb-file.
#
# If nzb-file has associated postprocess-parameters (which can be set using
# subcommand <O> of command <-E>, for example: NZBGet -E G O "myvar=hello !" 10)
@@ -888,13 +963,14 @@ NzbCleanupDisk=no
# the values are passed always in lower case.
#
# Return value: NZBGet processes the exit code returned by the script:
# 91 - request NZBGet to do par-check/repair for current collection in the
# current nzb-file;
# 92 - request NZBGet to do par-check/repair for all collections in the
# current nzb-file;
# 93 - post-process successful (status = SUCCESS);
# 94 - post-process failed (status = FAILURE);
# 95 - post-process skipped (status = NONE);
# 91 - request NZBGet to do par-check/repair for current collection in the
# current nzb-file. This return code is accepted only if the
# option <AllowReProcess> is active;
# 92 - request NZBGet to do par-check/repair for all collections (par-sets)
# in the current nzb-file.
# All other return codes are interpreted as "status unknown".
#
# The return value is used to display the status of post-processing in
@@ -903,62 +979,42 @@ NzbCleanupDisk=no
# to standard output. For example:
# echo "[ERROR] [HISTORY] Unpack failed, not enough disk space";
#
# NOTE: The parameter NZBPP_NZBCOMPLETED is very important and MUST be checked
# even in the simplest scripts.
# If par-check is enabled and nzb-file contains more than one collection
# of files the postprocess-program is called after each collection is completed
# and par-checked. If you want to unpack files or clean up the directory
# (delete par-files, etc.) there are two possibilities, when you can do this:
# 1) you parse NZBPP_PARFILENAME to find out the base name of collection and
# clean up only files from this collection (not reliable, because par-files
# sometimes have different names than rar-files);
# 2) or you just check the parameters NZBPP_NZBCOMPLETED and NZBPP_PARFAILED
# and do the processing, only if NZBPP_NZBCOMPLETED is set to "1" (which
# means, that this was the last collection in nzb-file and all files
# are now completed) and NZBPP_PARFAILED is set to "0" (no failed par-jobs);
#
# NOTE: The term "collection" in the above description actually means
# NOTE: If the option <AllowReProcess> is active NZBGet calls the script
# for each collection within nzb-file. The term "collection" actually means
# "par-set". To determine what "collections" are present in nzb-file NZBGet
# looks for par-sets. If any collection of files within nzb-file does
# not have any par-files, this collection will not be detected.
# For example, for nzb-file containing three collections but only two par-sets,
# the postprocess will be called two times - after processing of each par-set.
#
# NOTE: If NZBGet doesn't find any collections it calls PostProcess once
# with empty string for parameter NZBPP_PARFILENAME;
#
# NOTE: The using of special return values (91 and 92) for requesting of
# par-check/repair allows to organize the delayed parcheck. To do that:
# 1) set options: LoadPars=one, ParCheck=no, ParRepair=yes;
# 2) in post-process-script check the parameter NZBPP_PARSTATUS. If it is "0",
# that means, the script is called for the first time. Try to unpack files.
# If unpack fails, exit the script with exit code for par-check/repair;
# 3) NZBGet will start par-check/repair. After that it calls the script again;
# 4) on second pass the parameter NZBPP_PARSTATUS will have value
# greater than "0". If it is "2" ("checked and successfully repaired")
# you can try unpack again.
# If NZBGet doesn't find any collections it calls PostProcess once
# with empty string for parameter NZBPP_PARFILENAME.
PostProcess=
# Allow multiple post-processing for the same nzb-file (yes, no).
#
# NOTE: Enable this option only if you were advised to do that by the author
# of the post-process-script.
# of the post-processing script.
#
# NOTE: By enabling <AllowReProcess> you should disable the option <ParCheck>
# to prevent multiple par-checking.
#
# INFO FOR DEVELOPERS:
# After the post-processing (par-check and call of a postprocess-script) is
# completed, NZBGet adds the nzb-file to a list of completed-jobs. The nzb-file
# stays in the list until the last file from that nzb-file is deleted from
# the download queue (it occurs straight away if the par-check was successful
# and the option <ParCleanupQueue> is enabled).
# That means, if a paused file from a nzb-collection becomes unpaused
# (manually or from a post-process-script) after the collection was already
# postprocessed NZBGet will not post-process nzb-file again.
# This prevents the unwanted multiple post-processings of the same nzb-file.
# But it might be needed if the par-check/-repair are performed not directly
# by NZBGet but from a post-process-script.
# This option affects the post-processing in several ways:
# 1) If the option is active the post-processing script (option <PostProcess>)
# is called for each collection (par-set) within nzb-file;
# 2) The post-processing script may be called multiple times when nzb-file
# reaches the state "completely downloaded". This can be needed if
# the par-check/-repair is performed by the post-processing script
# (instead of relying on NZBGet's built-in par-check-feature).
#
# NOTE: If the built-in unpacking is active (option <Unpack>) this option
# is ignored (as if it were set to "no").
#
# NOTE: If you develop a script depending on this option you should check
# if the option is active when your script is started and generate an
# error message if the option is not set correctly. You should also check
# the option <Unpack> because if it's active the option <AllowReProcess>
# doesn't work too.
AllowReProcess=no
# Pause download queue during executing of postprocess-script (yes, no).
@@ -966,7 +1022,7 @@ AllowReProcess=no
# Enable the option to give CPU more time for postprocess-script. That helps
# to speed up postprocess on slow CPUs with fast connection (e.g. NAS-devices).
#
# NOTE: See also option <ParPauseQueue>.
# NOTE: See also options <ParPauseQueue> and <UnpackPauseQueue>.
PostPauseQueue=no

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -336,6 +336,7 @@ var Options = (new function($)
}
return null;
}
this.findOption = findOption;
function mergeValues(config, values)
{
@@ -662,9 +663,9 @@ var Config = (new function($)
value = option.defvalue;
}
option.formId = section.category + '-' + option.name.replace(/[\.|$]/g, '_');
option.formId = section.category + '-' + option.name.replace(/[\.|$|\:|\*]/g, '_');
var caption = option.name;
var caption = option.caption ? option.caption : option.name;
if (section.multi)
{
caption = '<span class="config-multicaption">' + caption.substring(0, caption.indexOf('.') + 1) + '</span>' + caption.substring(caption.indexOf('.') + 1);

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -273,7 +273,8 @@ var Downloads = (new function($)
case 'VERIFYING_SOURCES': group.status = 'checking'; break;
case 'REPAIRING': group.status = 'repairing'; break;
case 'VERIFYING_REPAIRED': group.status = 'verifying'; break;
case 'EXECUTING_SCRIPT': group.status = 'unpacking'; break;
case 'UNPACKING': group.status = 'unpacking'; break;
case 'EXECUTING_SCRIPT': group.status = 'processing'; break;
case 'FINISHED': group.status = 'finished'; break;
default: group.status = 'error: ' + group.post.Stage; break;
}
@@ -633,15 +634,28 @@ var DownloadsUI = (new function($)
this.buildProgressLabel = function(group)
{
var text = '';
if (group.postprocess && !Status.status.PostPaused && group.post.Stage !== 'REPAIRING')
if (group.postprocess && !Status.status.PostPaused)
{
if (group.post.Log && group.post.Log.length > 0)
switch (group.post.Stage)
{
text = group.post.Log[group.post.Log.length-1].Text;
}
else if (group.post.ProgressLabel !== '')
{
text = group.post.ProgressLabel;
case "REPAIRING":
break;
case "LOADING_PARS":
case "VERIFYING_SOURCES":
case "VERIFYING_REPAIRED":
case "UNPACKING":
text = group.post.ProgressLabel;
break;
case "EXECUTING_SCRIPT":
if (group.post.Log && group.post.Log.length > 0)
{
text = group.post.Log[group.post.Log.length-1].Text;
}
else
{
text = group.post.ProgressLabel;
}
break;
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -149,6 +149,7 @@ var DownloadsEditDialog = (new function($)
table += '<tr><td>Total</td><td class="text-right">' + size + '</td></tr>';
table += '<tr><td>Paused</td><td class="text-right">' + unpausedSize + '</td></tr>';
table += '<tr><td>Unpaused</td><td class="text-right">' + remaining + '</td></tr>';
//table += '<tr><td>Active downloads</td><td class="text-right">' + group.ActiveDownloads + '</td></tr>';
table += '<tr><td>Estimated time</td><td class="text-right">' + estimated + '</td></tr>';
table += '<tr><td>Files (total/remaining/pars)</td><td class="text-center">' + group.FileCount + ' / ' +
group.RemainingFileCount + ' / ' + group.RemainingParCount + '</td></tr>';
@@ -182,13 +183,16 @@ var DownloadsEditDialog = (new function($)
$DownloadsLogTable.fasttable('update', []);
$DownloadsFileTable.fasttable('update', []);
var postParamConfig = Options.postParamConfig;
defineBuiltinParams(postParamConfig);
Util.show('#DownloadsEdit_NZBNameReadonly', group.postprocess);
Util.show('#DownloadsEdit_CancelPPGroup', group.postprocess);
Util.show('#DownloadsEdit_DeleteGroup', !group.postprocess);
Util.show('#DownloadsEdit_PauseGroup', !group.postprocess);
Util.show('#DownloadsEdit_ResumeGroup', false);
Util.show('#DownloadsEdit_Save', !group.postprocess);
var postParam = Options.postParamConfig && Options.postParamConfig.length > 0;
var postParam = postParamConfig && postParamConfig.length > 0;
var postLog = group.postprocess && group.post.Log.length > 0;
Util.show('#DownloadsEdit_Param', postParam);
Util.show('#DownloadsEdit_Log', postLog);
@@ -216,7 +220,7 @@ var DownloadsEditDialog = (new function($)
if (postParam)
{
postParams = $.extend(true, [], Options.postParamConfig);
postParams = $.extend(true, [], postParamConfig);
Options.mergeValues(postParams, group.Parameters);
var content = Config.buildOptionsContent(postParams[0]);
var configData = $('#DownloadsEdit_ParamData');
@@ -427,6 +431,25 @@ var DownloadsEditDialog = (new function($)
/*** TAB: POST-PROCESSING PARAMETERS **************************************************/
function defineBuiltinParams(postParamConfig)
{
if (Options.option('Unpack') !== 'yes')
{
return;
}
if (postParamConfig.length == 0)
{
postParamConfig.push({category: 'P', postparam: true, options: []});
}
if (!Options.findOption(postParamConfig[0].options, '*Unpack:'))
{
postParamConfig[0].options.unshift({name: '*Unpack:Password', value: '', defvalue: '', select: [], caption: 'Password', description: 'Unpack-password for encrypted posts.'});
postParamConfig[0].options.unshift({name: '*Unpack:', value: '', defvalue: 'yes', select: ['yes', 'no'], caption: 'Unpack', description: 'Set to "no" to disable unpack for this nzb-file.'});
}
}
function prepareParamRequest()
{
var request = [];

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -113,19 +113,29 @@ var History = (new function($)
{
if (hist.Kind === 'NZB')
{
switch (hist.ScriptStatus)
if (hist.ParStatus == 'FAILURE' || hist.UnpackStatus == 'FAILURE' || hist.ScriptStatus == 'FAILURE')
{
case 'SUCCESS': hist.status = 'success'; break;
case 'FAILURE': hist.status = 'failure'; break;
case 'UNKNOWN': hist.status = 'unknown'; break;
case 'NONE':
switch (hist.ParStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'REPAIR_POSSIBLE': hist.status = 'repairable'; break;
case 'FAILURE': hist.status = 'failure'; break;
case 'NONE': hist.status = 'none'; break;
}
hist.status = 'failure';
}
else
{
switch (hist.ScriptStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'UNKNOWN': hist.status = 'unknown'; break;
case 'NONE':
switch (hist.UnpackStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'NONE':
switch (hist.ParStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'REPAIR_POSSIBLE': hist.status = 'repairable'; break;
case 'NONE': hist.status = 'unknown'; break;
}
}
}
}
}
else if (hist.Kind === 'URL')
@@ -180,7 +190,7 @@ var History = (new function($)
{
var hist = item.hist;
var status = buildStatus(hist);
var status = buildStatus(hist.status, '');
var name = '<a href="#" histid="' + hist.ID + '">' + Util.textToHtml(Util.formatNZBName(hist.Name)) + '</a>';
var category = Util.textToHtml(hist.Category);
@@ -222,16 +232,27 @@ var History = (new function($)
}
}
function buildStatus(hist)
function buildStatus(status, prefix)
{
switch (hist.status)
switch (status)
{
case 'success': return '<span class="label label-status label-success">success</span>';
case 'failure': return '<span class="label label-status label-important">failure</span>';
case 'unknown': return '<span class="label label-status label-info">unknown</span>';
case 'repairable': return '<span class="label label-status label-success">repairable</span>';
case 'none': return '<span class="label label-status">unknown</span>';
default: return '<span class="label label-status label-important">internal error(' + hist.status + ')</span>';
case 'success':
case 'SUCCESS':
return '<span class="label label-status label-success">' + prefix + 'success</span>';
case 'failure':
case 'FAILURE':
return '<span class="label label-status label-important">' + prefix + 'failure</span>';
case 'unknown':
case 'UNKNOWN':
return '<span class="label label-status label-info">' + prefix + 'unknown</span>';
case 'repairable':
case 'REPAIR_POSSIBLE':
return '<span class="label label-status label-success">' + prefix + 'repairable</span>';
case 'none':
case 'NONE':
return '<span class="label label-status">' + prefix + 'none</span>';
default:
return '<span class="label label-status">' + prefix + status + '</span>';
}
}
@@ -358,7 +379,17 @@ var History = (new function($)
curHist = hist;
var status = buildStatus(hist);
var status;
if (hist.Kind === 'URL')
{
status = buildStatus(hist.status, '');
}
else
{
status = buildStatus(hist.ParStatus, 'Par: ') + ' ' +
(Options.option('Unpack') == 'yes' || hist.UnpackStatus != 'NONE' ? buildStatus(hist.UnpackStatus, 'Unpack: ') + ' ' : '') +
buildStatus(hist.ScriptStatus, 'Script: ');
}
$('#HistoryEdit_Title').text(Util.formatNZBName(hist.Name));
if (hist.Kind === 'URL')

View File

@@ -732,13 +732,13 @@
</table>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="modal-bottom-toolbar">
<button class="btn" data-tab="DownloadsEdit_FileTab" data-fullscreen="true" id="DownloadsEdit_File">Files <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_ParamTab" id="DownloadsEdit_Param" title="Post-processing parameters">PP Parameters <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_LogTab" data-fullscreen="true" id="DownloadsEdit_Log" title="Post-processing messages">PP Messages <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_FileTab" data-fullscreen="true" id="DownloadsEdit_File" title="File list">Files <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_ParamTab" id="DownloadsEdit_Param" title="Post-processing parameters">PP-Parameters <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_LogTab" data-fullscreen="true" id="DownloadsEdit_Log" title="Post-processing messages">PP-Messages <i class="icon-forward" style="opacity:0.6;"></i></button>
</div>
</div>
</div>