diff --git a/DiskState.cpp b/DiskState.cpp index f7080953..bed6a626 100644 --- a/DiskState.cpp +++ b/DiskState.cpp @@ -82,7 +82,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue) return false; } - fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 10); + fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 11); // save nzb-infos SaveNZBList(pDownloadQueue, outfile); @@ -132,9 +132,9 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue) char FileSignatur[128]; fgets(FileSignatur, sizeof(FileSignatur), infile); int iFormatVersion = ParseFormatVersion(FileSignatur); - if (iFormatVersion < 3 || iFormatVersion > 10) + if (iFormatVersion < 3 || iFormatVersion > 11) { - error("Could not load diskstate due file version mismatch"); + error("Could not load diskstate due to file version mismatch"); fclose(infile); return false; } @@ -215,6 +215,15 @@ void DiskState::SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile) NZBParameter* pParameter = *it; fprintf(outfile, "%s=%s\n", pParameter->GetName(), pParameter->GetValue()); } + + NZBInfo::Messages* pMessages = pNZBInfo->LockMessages(); + fprintf(outfile, "%i\n", pMessages->size()); + for (NZBInfo::Messages::iterator it = pMessages->begin(); it != pMessages->end(); it++) + { + Message* pMessage = *it; + fprintf(outfile, "%i,%i,%s\n", pMessage->GetKind(), (int)pMessage->GetTime(), pMessage->GetText()); + } + pNZBInfo->UnlockMessages(); } } @@ -317,6 +326,25 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo } } } + + if (iFormatVersion >= 11) + { + int iLogCount; + if (fscanf(infile, "%i\n", &iLogCount) != 1) goto error; + for (int i = 0; i < iLogCount; i++) + { + if (!fgets(buf, sizeof(buf), infile)) goto error; + if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n' + + int iKind, iTime; + sscanf(buf, "%i,%i", &iKind, &iTime); + char* szText = strchr(buf + 2, ','); + if (szText) { + szText++; + } + pNZBInfo->AppendMessage((Message::EKind)iKind, (time_t)iTime, szText); + } + } } return true; @@ -594,7 +622,7 @@ bool DiskState::LoadOldPostQueue(DownloadQueue* pDownloadQueue) int iFormatVersion = ParseFormatVersion(FileSignatur); if (iFormatVersion < 3 || iFormatVersion > 7) { - error("Could not load diskstate due file version mismatch"); + error("Could not load diskstate due to file version mismatch"); fclose(infile); return false; } @@ -814,7 +842,7 @@ bool DiskState::DiscardDownloadQueue() char FileSignatur[128]; fgets(FileSignatur, sizeof(FileSignatur), infile); int iFormatVersion = ParseFormatVersion(FileSignatur); - if (3 <= iFormatVersion && iFormatVersion <= 10) + if (3 <= iFormatVersion && iFormatVersion <= 11) { // skip nzb-infos int size = 0; @@ -867,6 +895,17 @@ bool DiskState::DiscardDownloadQueue() if (!fgets(buf, sizeof(buf), infile)) break; } } + + if (iFormatVersion >= 11) + { + // log-messages + int iLogCount; + if (fscanf(infile, "%i\n", &iLogCount) != 1) break; + for (int i = 0; i < iLogCount; i++) + { + if (!fgets(buf, sizeof(buf), infile)) break; + } + } } // file-infos @@ -886,7 +925,7 @@ bool DiskState::DiscardDownloadQueue() } else { - error("Could not discard diskstate due file version mismatch"); + error("Could not discard diskstate due to file version mismatch"); res = false; } diff --git a/DownloadInfo.cpp b/DownloadInfo.cpp index f4f622f1..ecce9fe3 100644 --- a/DownloadInfo.cpp +++ b/DownloadInfo.cpp @@ -98,6 +98,8 @@ NZBInfo::NZBInfo() m_szQueuedFilename = strdup(""); m_tHistoryTime = 0; m_Owner = NULL; + m_Messages.clear(); + m_iIDMessageGen = 0; m_iIDGen++; m_iID = m_iIDGen; } @@ -131,6 +133,12 @@ NZBInfo::~NZBInfo() } m_ppParameters.clear(); + for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++) + { + delete *it; + } + m_Messages.clear(); + if (m_Owner) { m_Owner->Remove(this); @@ -323,6 +331,31 @@ void NZBInfo::SetParameter(const char* szName, const char* szValue) pParameter->SetValue(szValue); } +NZBInfo::Messages* NZBInfo::LockMessages() +{ + m_mutexLog.Lock(); + return &m_Messages; +} + +void NZBInfo::UnlockMessages() +{ + m_mutexLog.Unlock(); +} + +void NZBInfo::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText) +{ + if (tTime == 0) + { + tTime = time(NULL); + } + + Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText); + + m_mutexLog.Lock(); + m_Messages.push_back(pMessage); + m_mutexLog.Unlock(); +} + void NZBInfoList::Add(NZBInfo* pNZBInfo) { pNZBInfo->m_Owner = this; diff --git a/DownloadInfo.h b/DownloadInfo.h index a1397220..8de51eea 100644 --- a/DownloadInfo.h +++ b/DownloadInfo.h @@ -201,6 +201,7 @@ public: }; typedef std::vector Files; + typedef std::deque Messages; private: int m_iID; @@ -222,6 +223,9 @@ private: time_t m_tHistoryTime; NZBInfoList* m_Owner; NZBParameterList m_ppParameters; + Mutex m_mutexLog; + Messages m_Messages; + int m_iIDMessageGen; static int m_iIDGen; @@ -268,6 +272,9 @@ public: void SetHistoryTime(time_t tHistoryTime) { m_tHistoryTime = tHistoryTime; } NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects) void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects) + void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText); + Messages* LockMessages(); + void UnlockMessages(); }; typedef std::deque NZBInfoListBase; diff --git a/ScriptController.cpp b/ScriptController.cpp index c8839d76..754c0efe 100644 --- a/ScriptController.cpp +++ b/ScriptController.cpp @@ -746,6 +746,15 @@ void PostScriptController::Run() void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText) { + if (!strncmp(szText, "[HISTORY] ", 10)) + { + if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth) + { + m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText + 10); + } + return; + } + ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText); if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth) diff --git a/XmlRpc.cpp b/XmlRpc.cpp index 1f401124..370a3d78 100644 --- a/XmlRpc.cpp +++ b/XmlRpc.cpp @@ -926,6 +926,7 @@ void LogXmlCommand::Execute() "}"; const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL" }; + int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; @@ -1459,6 +1460,8 @@ void PostQueueXmlCommand::Execute() "\"Text\" : \"%s\"\n" "}"; + const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"}; + PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue(); time_t tCurTime = time(NULL); @@ -1515,9 +1518,7 @@ void PostQueueXmlCommand::Execute() } iStart = pMessages->size() - iNrEntries; - const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"}; int index = 0; - for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++) { Message* pMessage = (*pMessages)[i]; @@ -1630,6 +1631,10 @@ void HistoryXmlCommand::Execute() "HistoryTime%i\n" "Parameters\n"; + const char* XML_HISTORY_ITEM_LOG_START = + "\n" + "Log\n"; + const char* XML_HISTORY_ITEM_END = "\n" "\n"; @@ -1651,6 +1656,10 @@ void HistoryXmlCommand::Execute() "\"HistoryTime\" : %i,\n" "\"Parameters\" : [\n"; + const char* JSON_HISTORY_ITEM_LOG_START = + "],\n" + "\"Log\" : [\n"; + const char* JSON_HISTORY_ITEM_END = "]\n" "}"; @@ -1667,8 +1676,25 @@ void HistoryXmlCommand::Execute() "\"Value\" : \"%s\"\n" "}"; + const char* XML_LOG_ITEM = + "\n" + "ID%i\n" + "Kind%s\n" + "Time%i\n" + "Text%s\n" + "\n"; + + const char* JSON_LOG_ITEM = + "{\n" + "\"ID\" : %i,\n" + "\"Kind\" : \"%s\",\n" + "\"Time\" : %i,\n" + "\"Text\" : \"%s\"\n" + "}"; + const char* szParStatusName[] = { "NONE", "FAILURE", "REPAIR_POSSIBLE", "SUCCESS" }; const char* szScriptStatusName[] = { "NONE", "UNKNOWN", "FAILURE", "SUCCESS" }; + const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"}; DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue(); @@ -1708,8 +1734,8 @@ void HistoryXmlCommand::Execute() } AppendResponse(szItemBuf); + // Post-processing parameters int iParamIndex = 0; - for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++) { NZBParameter* pParameter = *it; @@ -1730,6 +1756,31 @@ void HistoryXmlCommand::Execute() AppendResponse(szItemBuf); } + AppendResponse(IsJson() ? JSON_HISTORY_ITEM_LOG_START : XML_HISTORY_ITEM_LOG_START); + + // Log-Messages + NZBInfo::Messages* pMessages = pNZBInfo->LockMessages(); + if (!pMessages->empty()) + { + int iLogIndex = 0; + for (NZBInfo::Messages::iterator it = pMessages->begin(); it != pMessages->end(); it++) + { + Message* pMessage = *it; + char* xmltext = EncodeStr(pMessage->GetText()); + snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM, + pMessage->GetID(), szMessageType[pMessage->GetKind()], pMessage->GetTime(), xmltext); + szItemBuf[szItemBufSize-1] = '\0'; + free(xmltext); + + if (IsJson() && iLogIndex++ > 0) + { + AppendResponse(",\n"); + } + AppendResponse(szItemBuf); + } + } + pNZBInfo->UnlockMessages(); + AppendResponse(IsJson() ? JSON_HISTORY_ITEM_END : XML_HISTORY_ITEM_END); } free(szItemBuf); diff --git a/nzbget.conf.example b/nzbget.conf.example index 4c2c2d27..3b16b2ca 100644 --- a/nzbget.conf.example +++ b/nzbget.conf.example @@ -680,10 +680,16 @@ NzbCleanupDisk=no # current nzb-file; # 92 - request nzbget to do par-check/repair for all collections in the # current nzb-file; -# 93 - post-process successfull; +# 93 - post-process successful; # 94 - post-process failed; # All other return codes are interpreted as "status unknown". # +# The return value is used to display the status of post-processing in +# a history view. In addition to status one or more text messages can be +# passed to history using a special prefix "[HISTORY]" by printing messages +# 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