/* * This file is part of nzbget * * Copyright (C) 2007-2015 Andrey Prygunkov * * 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: 1221 $ * $Date: 2015-02-26 15:57:38 -0500 (Thu, 26 Feb 2015) $ * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WIN32 #include "win32.h" #endif #include #include #include #include #include #include #ifndef WIN32 #include #include #endif #include #include "nzbget.h" #include "DownloadInfo.h" #include "QueueEditor.h" #include "Options.h" #include "Log.h" #include "Util.h" #include "QueueCoordinator.h" #include "PrePostProcessor.h" #include "HistoryCoordinator.h" #include "UrlCoordinator.h" extern QueueCoordinator* g_pQueueCoordinator; extern HistoryCoordinator* g_pHistoryCoordinator; extern UrlCoordinator* g_pUrlCoordinator; extern PrePostProcessor* g_pPrePostProcessor; extern Options* g_pOptions; const int MAX_ID = 1000000000; class GroupSorter { public: enum ESortCriteria { scName, scSize, scRemainingSize, scAge, scCategory, scPriority }; enum ESortOrder { soAscending, soDescending, soAuto }; private: NZBList* m_pNZBList; QueueEditor::ItemList* m_pSortItemList; ESortCriteria m_eSortCriteria; ESortOrder m_eSortOrder; void AlignSelectedGroups(); public: GroupSorter(NZBList* pNZBList, QueueEditor::ItemList* pSortItemList) : m_pNZBList(pNZBList), m_pSortItemList(pSortItemList) {} bool Execute(const char* szSort); bool operator()(NZBInfo* pNZBInfo1, NZBInfo* pNZBInfo2) const; }; bool GroupSorter::Execute(const char* szSort) { if (!strcasecmp(szSort, "name") || !strcasecmp(szSort, "name+") || !strcasecmp(szSort, "name-")) { m_eSortCriteria = scName; } else if (!strcasecmp(szSort, "size") || !strcasecmp(szSort, "size+") || !strcasecmp(szSort, "size-")) { m_eSortCriteria = scSize; } else if (!strcasecmp(szSort, "left") || !strcasecmp(szSort, "left+") || !strcasecmp(szSort, "left-")) { m_eSortCriteria = scRemainingSize; } else if (!strcasecmp(szSort, "age") || !strcasecmp(szSort, "age+") || !strcasecmp(szSort, "age-")) { m_eSortCriteria = scAge; } else if (!strcasecmp(szSort, "category") || !strcasecmp(szSort, "category+") || !strcasecmp(szSort, "category-")) { m_eSortCriteria = scCategory; } else if (!strcasecmp(szSort, "priority") || !strcasecmp(szSort, "priority+") || !strcasecmp(szSort, "priority-")) { m_eSortCriteria = scPriority; } else { error("Could not sort groups: incorrect sort order (%s)", szSort); return false; } char lastCh = szSort[strlen(szSort) - 1]; if (lastCh == '+') { m_eSortOrder = soAscending; } else if (lastCh == '-') { m_eSortOrder = soDescending; } else { m_eSortOrder = soAuto; } AlignSelectedGroups(); NZBList tempList = *m_pNZBList; ESortOrder eOrigSortOrder = m_eSortOrder; if (m_eSortOrder == soAuto && m_eSortCriteria == scPriority) { m_eSortOrder = soDescending; } std::sort(m_pNZBList->begin(), m_pNZBList->end(), *this); if (eOrigSortOrder == soAuto && tempList == *m_pNZBList) { m_eSortOrder = m_eSortOrder == soDescending ? soAscending : soDescending; std::sort(m_pNZBList->begin(), m_pNZBList->end(), *this); } tempList.clear(); // prevent destroying of elements return true; } bool GroupSorter::operator()(NZBInfo* pNZBInfo1, NZBInfo* pNZBInfo2) const { // if list of ID is empty - sort all items bool bSortItem1 = m_pSortItemList->empty(); bool bSortItem2 = m_pSortItemList->empty(); for (QueueEditor::ItemList::iterator it = m_pSortItemList->begin(); it != m_pSortItemList->end(); it++) { QueueEditor::EditItem* pItem = *it; bSortItem1 |= pItem->m_pNZBInfo == pNZBInfo1; bSortItem2 |= pItem->m_pNZBInfo == pNZBInfo2; } if (!bSortItem1 || !bSortItem2) { return false; } bool ret = false; if (m_eSortOrder == soDescending) { std::swap(pNZBInfo1, pNZBInfo2); } switch (m_eSortCriteria) { case scName: ret = strcmp(pNZBInfo1->GetName(), pNZBInfo2->GetName()) < 0; break; case scSize: ret = pNZBInfo1->GetSize() < pNZBInfo2->GetSize(); break; case scRemainingSize: ret = pNZBInfo1->GetRemainingSize() - pNZBInfo1->GetPausedSize() < pNZBInfo2->GetRemainingSize() - pNZBInfo2->GetPausedSize(); break; case scAge: ret = pNZBInfo1->GetMinTime() > pNZBInfo2->GetMinTime(); break; case scCategory: ret = strcmp(pNZBInfo1->GetCategory(), pNZBInfo2->GetCategory()) < 0; break; case scPriority: ret = pNZBInfo1->GetPriority() < pNZBInfo2->GetPriority(); break; } return ret; } void GroupSorter::AlignSelectedGroups() { NZBInfo* pLastNZBInfo = NULL; unsigned int iLastNum = 0; unsigned int iNum = 0; while (iNum < m_pNZBList->size()) { NZBInfo* pNZBInfo = m_pNZBList->at(iNum); bool bSelected = false; for (QueueEditor::ItemList::iterator it = m_pSortItemList->begin(); it != m_pSortItemList->end(); it++) { QueueEditor::EditItem* pItem = *it; if (pItem->m_pNZBInfo == pNZBInfo) { bSelected = true; break; } } if (bSelected) { if (pLastNZBInfo && iNum - iLastNum > 1) { m_pNZBList->erase(m_pNZBList->begin() + iNum); m_pNZBList->insert(m_pNZBList->begin() + iLastNum + 1, pNZBInfo); iLastNum++; } else { iLastNum = iNum; } pLastNZBInfo = pNZBInfo; } iNum++; } } QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, NZBInfo* pNZBInfo, int iOffset) { m_pFileInfo = pFileInfo; m_pNZBInfo = pNZBInfo; m_iOffset = iOffset; } QueueEditor::QueueEditor() { debug("Creating QueueEditor"); } QueueEditor::~QueueEditor() { debug("Destroying QueueEditor"); } FileInfo* QueueEditor::FindFileInfo(int iID) { for (NZBList::iterator it = m_pDownloadQueue->GetQueue()->begin(); it != m_pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; for (FileList::iterator it2 = pNZBInfo->GetFileList()->begin(); it2 != pNZBInfo->GetFileList()->end(); it2++) { FileInfo* pFileInfo = *it2; if (pFileInfo->GetID() == iID) { return pFileInfo; } } } return NULL; } /* * Set the pause flag of the specific entry in the queue */ void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause) { pFileInfo->SetPaused(bPause); } /* * Removes entry */ void QueueEditor::DeleteEntry(FileInfo* pFileInfo) { pFileInfo->GetNZBInfo()->PrintMessage( pFileInfo->GetNZBInfo()->GetDeleting() ? Message::mkDetail : Message::mkInfo, "Deleting file %s from download queue", pFileInfo->GetFilename()); g_pQueueCoordinator->DeleteQueueEntry(m_pDownloadQueue, pFileInfo); } /* * Moves entry in the queue */ void QueueEditor::MoveEntry(FileInfo* pFileInfo, int iOffset) { int iEntry = 0; for (FileList::iterator it = pFileInfo->GetNZBInfo()->GetFileList()->begin(); it != pFileInfo->GetNZBInfo()->GetFileList()->end(); it++) { FileInfo* pFileInfo2 = *it; if (pFileInfo2 == pFileInfo) { break; } iEntry ++; } int iNewEntry = iEntry + iOffset; int iSize = (int)pFileInfo->GetNZBInfo()->GetFileList()->size(); if (iNewEntry < 0) { iNewEntry = 0; } if (iNewEntry > iSize - 1) { iNewEntry = (int)iSize - 1; } if (iNewEntry >= 0 && iNewEntry <= iSize - 1) { pFileInfo->GetNZBInfo()->GetFileList()->erase(pFileInfo->GetNZBInfo()->GetFileList()->begin() + iEntry); pFileInfo->GetNZBInfo()->GetFileList()->insert(pFileInfo->GetNZBInfo()->GetFileList()->begin() + iNewEntry, pFileInfo); } } /* * Moves group in the queue */ void QueueEditor::MoveGroup(NZBInfo* pNZBInfo, int iOffset) { int iEntry = 0; for (NZBList::iterator it = m_pDownloadQueue->GetQueue()->begin(); it != m_pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo2 = *it; if (pNZBInfo2 == pNZBInfo) { break; } iEntry ++; } int iNewEntry = iEntry + iOffset; int iSize = (int)m_pDownloadQueue->GetQueue()->size(); if (iNewEntry < 0) { iNewEntry = 0; } if (iNewEntry > iSize - 1) { iNewEntry = (int)iSize - 1; } if (iNewEntry >= 0 && iNewEntry <= iSize - 1) { m_pDownloadQueue->GetQueue()->erase(m_pDownloadQueue->GetQueue()->begin() + iEntry); m_pDownloadQueue->GetQueue()->insert(m_pDownloadQueue->GetQueue()->begin() + iNewEntry, pNZBInfo); } } bool QueueEditor::EditEntry(DownloadQueue* pDownloadQueue, int ID, DownloadQueue::EEditAction eAction, int iOffset, const char* szText) { m_pDownloadQueue = pDownloadQueue; IDList cIDList; cIDList.push_back(ID); return InternEditList(NULL, &cIDList, eAction, iOffset, szText); } bool QueueEditor::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, DownloadQueue::EMatchMode eMatchMode, DownloadQueue::EEditAction eAction, int iOffset, const char* szText) { if (eAction == DownloadQueue::eaPostDelete) { return g_pPrePostProcessor->EditList(pDownloadQueue, pIDList, eAction, iOffset, szText); } else if (DownloadQueue::eaHistoryDelete <= eAction && eAction <= DownloadQueue::eaHistorySetName) { return g_pHistoryCoordinator->EditList(pDownloadQueue, pIDList, eAction, iOffset, szText); } m_pDownloadQueue = pDownloadQueue; bool bOK = true; if (pNameList) { pIDList = new IDList(); bOK = BuildIDListFromNameList(pIDList, pNameList, eMatchMode, eAction); } bOK = bOK && (InternEditList(NULL, pIDList, eAction, iOffset, szText) || eMatchMode == DownloadQueue::mmRegEx); m_pDownloadQueue->Save(); if (pNameList) { delete pIDList; } return bOK; } bool QueueEditor::InternEditList(ItemList* pItemList, IDList* pIDList, DownloadQueue::EEditAction eAction, int iOffset, const char* szText) { ItemList itemList; if (!pItemList) { pItemList = &itemList; PrepareList(pItemList, pIDList, eAction, iOffset); } switch (eAction) { case DownloadQueue::eaFilePauseAllPars: case DownloadQueue::eaFilePauseExtraPars: PauseParsInGroups(pItemList, eAction == DownloadQueue::eaFilePauseExtraPars); break; case DownloadQueue::eaGroupMerge: return MergeGroups(pItemList); case DownloadQueue::eaGroupSort: return SortGroups(pItemList, szText); case DownloadQueue::eaFileSplit: return SplitGroup(pItemList, szText); case DownloadQueue::eaFileReorder: ReorderFiles(pItemList); break; default: for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++) { EditItem* pItem = *it; switch (eAction) { case DownloadQueue::eaFilePause: PauseUnpauseEntry(pItem->m_pFileInfo, true); break; case DownloadQueue::eaFileResume: PauseUnpauseEntry(pItem->m_pFileInfo, false); break; case DownloadQueue::eaFileMoveOffset: case DownloadQueue::eaFileMoveTop: case DownloadQueue::eaFileMoveBottom: MoveEntry(pItem->m_pFileInfo, pItem->m_iOffset); break; case DownloadQueue::eaFileDelete: DeleteEntry(pItem->m_pFileInfo); break; case DownloadQueue::eaGroupSetPriority: SetNZBPriority(pItem->m_pNZBInfo, szText); break; case DownloadQueue::eaGroupSetCategory: case DownloadQueue::eaGroupApplyCategory: SetNZBCategory(pItem->m_pNZBInfo, szText, eAction == DownloadQueue::eaGroupApplyCategory); break; case DownloadQueue::eaGroupSetName: SetNZBName(pItem->m_pNZBInfo, szText); break; case DownloadQueue::eaGroupSetDupeKey: case DownloadQueue::eaGroupSetDupeScore: case DownloadQueue::eaGroupSetDupeMode: SetNZBDupeParam(pItem->m_pNZBInfo, eAction, szText); break; case DownloadQueue::eaGroupSetParameter: SetNZBParameter(pItem->m_pNZBInfo, szText); break; case DownloadQueue::eaGroupMoveTop: case DownloadQueue::eaGroupMoveBottom: case DownloadQueue::eaGroupMoveOffset: MoveGroup(pItem->m_pNZBInfo, pItem->m_iOffset); break; case DownloadQueue::eaGroupPause: case DownloadQueue::eaGroupResume: case DownloadQueue::eaGroupPauseAllPars: case DownloadQueue::eaGroupPauseExtraPars: EditGroup(pItem->m_pNZBInfo, eAction, iOffset, szText); break; case DownloadQueue::eaGroupDelete: case DownloadQueue::eaGroupDupeDelete: case DownloadQueue::eaGroupFinalDelete: if (pItem->m_pNZBInfo->GetKind() == NZBInfo::nkUrl) { DeleteUrl(pItem->m_pNZBInfo, eAction); } else { EditGroup(pItem->m_pNZBInfo, eAction, iOffset, szText); } default: // suppress compiler warning "enumeration not handled in switch" break; } delete pItem; } } return pItemList->size() > 0; } void QueueEditor::PrepareList(ItemList* pItemList, IDList* pIDList, DownloadQueue::EEditAction eAction, int iOffset) { if (eAction == DownloadQueue::eaFileMoveTop || eAction == DownloadQueue::eaGroupMoveTop) { iOffset = -MAX_ID; } else if (eAction == DownloadQueue::eaFileMoveBottom || eAction == DownloadQueue::eaGroupMoveBottom) { iOffset = MAX_ID; } pItemList->reserve(pIDList->size()); if ((iOffset != 0) && (eAction == DownloadQueue::eaFileMoveOffset || eAction == DownloadQueue::eaFileMoveTop || eAction == DownloadQueue::eaFileMoveBottom)) { // add IDs to list in order they currently have in download queue for (NZBList::iterator it = m_pDownloadQueue->GetQueue()->begin(); it != m_pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; int iNrEntries = (int)pNZBInfo->GetFileList()->size(); int iLastDestPos = -1; int iStart, iEnd, iStep; if (iOffset < 0) { iStart = 0; iEnd = iNrEntries; iStep = 1; } else { iStart = iNrEntries - 1; iEnd = -1; iStep = -1; } for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep) { FileInfo* pFileInfo = pNZBInfo->GetFileList()->at(iIndex); IDList::iterator it2 = std::find(pIDList->begin(), pIDList->end(), pFileInfo->GetID()); if (it2 != pIDList->end()) { int iWorkOffset = iOffset; int iDestPos = iIndex + iWorkOffset; if (iLastDestPos == -1) { if (iDestPos < 0) { iWorkOffset = -iIndex; } else if (iDestPos > iNrEntries - 1) { iWorkOffset = iNrEntries - 1 - iIndex; } } else { if (iWorkOffset < 0 && iDestPos <= iLastDestPos) { iWorkOffset = iLastDestPos - iIndex + 1; } else if (iWorkOffset > 0 && iDestPos >= iLastDestPos) { iWorkOffset = iLastDestPos - iIndex - 1; } } iLastDestPos = iIndex + iWorkOffset; pItemList->push_back(new EditItem(pFileInfo, NULL, iWorkOffset)); } } } } else if ((iOffset != 0) && (eAction == DownloadQueue::eaGroupMoveOffset || eAction == DownloadQueue::eaGroupMoveTop || eAction == DownloadQueue::eaGroupMoveBottom)) { // add IDs to list in order they currently have in download queue // per group only one FileInfo is added to the list int iNrEntries = (int)m_pDownloadQueue->GetQueue()->size(); int iLastDestPos = -1; int iStart, iEnd, iStep; if (iOffset < 0) { iStart = 0; iEnd = iNrEntries; iStep = 1; } else { iStart = iNrEntries - 1; iEnd = -1; iStep = -1; } for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep) { NZBInfo* pNZBInfo = m_pDownloadQueue->GetQueue()->at(iIndex); IDList::iterator it2 = std::find(pIDList->begin(), pIDList->end(), pNZBInfo->GetID()); if (it2 != pIDList->end()) { int iWorkOffset = iOffset; int iDestPos = iIndex + iWorkOffset; if (iLastDestPos == -1) { if (iDestPos < 0) { iWorkOffset = -iIndex; } else if (iDestPos > iNrEntries - 1) { iWorkOffset = iNrEntries - 1 - iIndex; } } else { if (iWorkOffset < 0 && iDestPos <= iLastDestPos) { iWorkOffset = iLastDestPos - iIndex + 1; } else if (iWorkOffset > 0 && iDestPos >= iLastDestPos) { iWorkOffset = iLastDestPos - iIndex - 1; } } iLastDestPos = iIndex + iWorkOffset; pItemList->push_back(new EditItem(NULL, pNZBInfo, iWorkOffset)); } } } else if (eAction < DownloadQueue::eaGroupMoveOffset) { // check ID range int iMaxID = 0; int iMinID = MAX_ID; for (NZBList::iterator it = m_pDownloadQueue->GetQueue()->begin(); it != m_pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; for (FileList::iterator it2 = pNZBInfo->GetFileList()->begin(); it2 != pNZBInfo->GetFileList()->end(); it2++) { FileInfo* pFileInfo = *it2; int ID = pFileInfo->GetID(); if (ID > iMaxID) { iMaxID = ID; } if (ID < iMinID) { iMinID = ID; } } } //add IDs to list in order they were transmitted in command for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++) { int iID = *it; if (iMinID <= iID && iID <= iMaxID) { FileInfo* pFileInfo = FindFileInfo(iID); if (pFileInfo) { pItemList->push_back(new EditItem(pFileInfo, NULL, iOffset)); } } } } else { // check ID range int iMaxID = 0; int iMinID = MAX_ID; for (NZBList::iterator it = m_pDownloadQueue->GetQueue()->begin(); it != m_pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; int ID = pNZBInfo->GetID(); if (ID > iMaxID) { iMaxID = ID; } if (ID < iMinID) { iMinID = ID; } } //add IDs to list in order they were transmitted in command for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++) { int iID = *it; if (iMinID <= iID && iID <= iMaxID) { for (NZBList::iterator it2 = m_pDownloadQueue->GetQueue()->begin(); it2 != m_pDownloadQueue->GetQueue()->end(); it2++) { NZBInfo* pNZBInfo = *it2; if (iID == pNZBInfo->GetID()) { pItemList->push_back(new EditItem(NULL, pNZBInfo, iOffset)); } } } } } } bool QueueEditor::BuildIDListFromNameList(IDList* pIDList, NameList* pNameList, DownloadQueue::EMatchMode eMatchMode, DownloadQueue::EEditAction eAction) { #ifndef HAVE_REGEX_H if (eMatchMode == mmRegEx) { return false; } #endif std::set uniqueIDs; for (NameList::iterator it = pNameList->begin(); it != pNameList->end(); it++) { const char* szName = *it; RegEx *pRegEx = NULL; if (eMatchMode == DownloadQueue::mmRegEx) { pRegEx = new RegEx(szName); if (!pRegEx->IsValid()) { delete pRegEx; return false; } } bool bFound = false; for (NZBList::iterator it3 = m_pDownloadQueue->GetQueue()->begin(); it3 != m_pDownloadQueue->GetQueue()->end(); it3++) { NZBInfo* pNZBInfo = *it3; for (FileList::iterator it2 = pNZBInfo->GetFileList()->begin(); it2 != pNZBInfo->GetFileList()->end(); it2++) { FileInfo* pFileInfo = *it2; if (eAction < DownloadQueue::eaGroupMoveOffset) { // file action char szFilename[MAX_PATH]; snprintf(szFilename, sizeof(szFilename) - 1, "%s/%s", pFileInfo->GetNZBInfo()->GetName(), Util::BaseFileName(pFileInfo->GetFilename())); if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) && (uniqueIDs.find(pFileInfo->GetID()) == uniqueIDs.end())) { uniqueIDs.insert(pFileInfo->GetID()); pIDList->push_back(pFileInfo->GetID()); bFound = true; } } } if (eAction >= DownloadQueue::eaGroupMoveOffset) { // group action const char *szFilename = pNZBInfo->GetName(); if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) && (uniqueIDs.find(pNZBInfo->GetID()) == uniqueIDs.end())) { uniqueIDs.insert(pNZBInfo->GetID()); pIDList->push_back(pNZBInfo->GetID()); bFound = true; } } } delete pRegEx; if (!bFound && (eMatchMode == DownloadQueue::mmName)) { return false; } } return true; } bool QueueEditor::EditGroup(NZBInfo* pNZBInfo, DownloadQueue::EEditAction eAction, int iOffset, const char* szText) { ItemList itemList; bool bAllPaused = true; int iID = pNZBInfo->GetID(); // collecting files belonging to group for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++) { FileInfo* pFileInfo = *it; itemList.push_back(new EditItem(pFileInfo, NULL, 0)); bAllPaused &= pFileInfo->GetPaused(); } if (eAction == DownloadQueue::eaGroupDelete || eAction == DownloadQueue::eaGroupDupeDelete || eAction == DownloadQueue::eaGroupFinalDelete) { pNZBInfo->SetDeleting(true); pNZBInfo->SetAvoidHistory(eAction == DownloadQueue::eaGroupFinalDelete); pNZBInfo->SetDeletePaused(bAllPaused); if (eAction == DownloadQueue::eaGroupDupeDelete) { pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe); } pNZBInfo->SetCleanupDisk(CanCleanupDisk(pNZBInfo)); } DownloadQueue::EEditAction GroupToFileMap[] = { (DownloadQueue::EEditAction)0, DownloadQueue::eaFileMoveOffset, DownloadQueue::eaFileMoveTop, DownloadQueue::eaFileMoveBottom, DownloadQueue::eaFilePause, DownloadQueue::eaFileResume, DownloadQueue::eaFileDelete, DownloadQueue::eaFilePauseAllPars, DownloadQueue::eaFilePauseExtraPars, DownloadQueue::eaFileReorder, DownloadQueue::eaFileSplit, DownloadQueue::eaFileMoveOffset, DownloadQueue::eaFileMoveTop, DownloadQueue::eaFileMoveBottom, DownloadQueue::eaFilePause, DownloadQueue::eaFileResume, DownloadQueue::eaFileDelete, DownloadQueue::eaFileDelete, DownloadQueue::eaFileDelete, DownloadQueue::eaFilePauseAllPars, DownloadQueue::eaFilePauseExtraPars, (DownloadQueue::EEditAction)0, (DownloadQueue::EEditAction)0, (DownloadQueue::EEditAction)0, (DownloadQueue::EEditAction)0 }; bool bOK = InternEditList(&itemList, NULL, GroupToFileMap[eAction], iOffset, szText); if ((eAction == DownloadQueue::eaGroupDelete || eAction == DownloadQueue::eaGroupDupeDelete || eAction == DownloadQueue::eaGroupFinalDelete) && // NZBInfo could have been destroyed already m_pDownloadQueue->GetQueue()->Find(iID)) { DownloadQueue::Aspect deleteAspect = { DownloadQueue::eaNzbDeleted, m_pDownloadQueue, pNZBInfo, NULL }; m_pDownloadQueue->Notify(&deleteAspect); } return bOK; } void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly) { while (true) { FileList GroupFileList; FileInfo* pFirstFileInfo = NULL; for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); ) { EditItem* pItem = *it; if (!pFirstFileInfo || (pFirstFileInfo->GetNZBInfo() == pItem->m_pFileInfo->GetNZBInfo())) { GroupFileList.push_back(pItem->m_pFileInfo); if (!pFirstFileInfo) { pFirstFileInfo = pItem->m_pFileInfo; } delete pItem; pItemList->erase(it); it = pItemList->begin(); continue; } it++; } if (!GroupFileList.empty()) { PausePars(&GroupFileList, bExtraParsOnly); } else { break; } } } /** * If the parameter "bExtraParsOnly" is set to "false", then we pause all par2-files. * If the parameter "bExtraParsOnly" is set to "true", we use the following strategy: * At first we find all par-files, which do not have "vol" in their names, then we pause * all vols and do not affect all just-pars. * In a case, if there are no just-pars, but only vols, we find the smallest vol-file * and do not affect it, but pause all other pars. */ void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly) { debug("QueueEditor: Pausing pars"); FileList Pars, Vols; for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++) { FileInfo* pFileInfo = *it; char szLoFileName[1024]; strncpy(szLoFileName, pFileInfo->GetFilename(), 1024); szLoFileName[1024-1] = '\0'; for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase if (strstr(szLoFileName, ".par2")) { if (!bExtraParsOnly) { pFileInfo->SetPaused(true); } else { if (strstr(szLoFileName, ".vol")) { Vols.push_back(pFileInfo); } else { Pars.push_back(pFileInfo); } } } } if (bExtraParsOnly) { if (!Pars.empty()) { for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++) { FileInfo* pFileInfo = *it; pFileInfo->SetPaused(true); } } else { // pausing all Vol-files except the smallest one FileInfo* pSmallest = NULL; for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++) { FileInfo* pFileInfo = *it; if (!pSmallest) { pSmallest = pFileInfo; } else if (pSmallest->GetSize() > pFileInfo->GetSize()) { pSmallest->SetPaused(true); pSmallest = pFileInfo; } else { pFileInfo->SetPaused(true); } } } } } void QueueEditor::SetNZBPriority(NZBInfo* pNZBInfo, const char* szPriority) { debug("Setting priority %s for %s", szPriority, pNZBInfo->GetName()); int iPriority = atoi(szPriority); pNZBInfo->SetPriority(iPriority); } void QueueEditor::SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory, bool bApplyParams) { debug("QueueEditor: setting category '%s' for '%s'", szCategory, pNZBInfo->GetName()); bool bOldUnpack = g_pOptions->GetUnpack(); const char* szOldPostScript = g_pOptions->GetPostScript(); if (bApplyParams && !Util::EmptyStr(pNZBInfo->GetCategory())) { Options::Category* pCategory = g_pOptions->FindCategory(pNZBInfo->GetCategory(), false); if (pCategory) { bOldUnpack = pCategory->GetUnpack(); if (!Util::EmptyStr(pCategory->GetPostScript())) { szOldPostScript = pCategory->GetPostScript(); } } } g_pQueueCoordinator->SetQueueEntryCategory(m_pDownloadQueue, pNZBInfo, szCategory); if (!bApplyParams) { return; } bool bNewUnpack = g_pOptions->GetUnpack(); const char* szNewPostScript = g_pOptions->GetPostScript(); if (!Util::EmptyStr(pNZBInfo->GetCategory())) { Options::Category* pCategory = g_pOptions->FindCategory(pNZBInfo->GetCategory(), false); if (pCategory) { bNewUnpack = pCategory->GetUnpack(); if (!Util::EmptyStr(pCategory->GetPostScript())) { szNewPostScript = pCategory->GetPostScript(); } } } if (bOldUnpack != bNewUnpack) { pNZBInfo->GetParameters()->SetParameter("*Unpack:", bNewUnpack ? "yes" : "no"); } if (strcasecmp(szOldPostScript, szNewPostScript)) { // add new params not existed in old category Tokenizer tokNew(szNewPostScript, ",;"); while (const char* szNewScriptName = tokNew.Next()) { bool bFound = false; const char* szOldScriptName; Tokenizer tokOld(szOldPostScript, ",;"); while ((szOldScriptName = tokOld.Next()) && !bFound) { bFound = !strcasecmp(szNewScriptName, szOldScriptName); } if (!bFound) { char szParam[1024]; snprintf(szParam, 1024, "%s:", szNewScriptName); szParam[1024-1] = '\0'; pNZBInfo->GetParameters()->SetParameter(szParam, "yes"); } } // remove old params not existed in new category Tokenizer tokOld(szOldPostScript, ",;"); while (const char* szOldScriptName = tokOld.Next()) { bool bFound = false; const char* szNewScriptName; Tokenizer tokNew(szNewPostScript, ",;"); while ((szNewScriptName = tokNew.Next()) && !bFound) { bFound = !strcasecmp(szNewScriptName, szOldScriptName); } if (!bFound) { char szParam[1024]; snprintf(szParam, 1024, "%s:", szOldScriptName); szParam[1024-1] = '\0'; pNZBInfo->GetParameters()->SetParameter(szParam, "no"); } } } } void QueueEditor::SetNZBName(NZBInfo* pNZBInfo, const char* szName) { debug("QueueEditor: renaming '%s' to '%s'", pNZBInfo->GetName(), szName); g_pQueueCoordinator->SetQueueEntryName(m_pDownloadQueue, pNZBInfo, szName); } /** * Check if deletion of already downloaded files is possible (when nzb id deleted from queue). * The deletion is most always possible, except the case if all remaining files in queue * (belonging to this nzb-file) are PARS. */ bool QueueEditor::CanCleanupDisk(NZBInfo* pNZBInfo) { if (pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone) { return true; } for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++) { FileInfo* pFileInfo = *it; char szLoFileName[1024]; strncpy(szLoFileName, pFileInfo->GetFilename(), 1024); szLoFileName[1024-1] = '\0'; for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase if (!strstr(szLoFileName, ".par2")) { // non-par file found return true; } } return false; } bool QueueEditor::MergeGroups(ItemList* pItemList) { if (pItemList->size() == 0) { return false; } bool bOK = true; EditItem* pDestItem = pItemList->front(); for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++) { EditItem* pItem = *it; if (pItem->m_pNZBInfo != pDestItem->m_pNZBInfo) { debug("merge %s to %s", pItem->m_pNZBInfo->GetFilename(), pDestItem->m_pNZBInfo->GetFilename()); if (g_pQueueCoordinator->MergeQueueEntries(m_pDownloadQueue, pDestItem->m_pNZBInfo, pItem->m_pNZBInfo)) { bOK = false; } } delete pItem; } delete pDestItem; return bOK; } bool QueueEditor::SplitGroup(ItemList* pItemList, const char* szName) { if (pItemList->size() == 0) { return false; } FileList fileList(false); for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++) { EditItem* pItem = *it; fileList.push_back(pItem->m_pFileInfo); delete pItem; } NZBInfo* pNewNZBInfo = NULL; bool bOK = g_pQueueCoordinator->SplitQueueEntries(m_pDownloadQueue, &fileList, szName, &pNewNZBInfo); return bOK; } bool QueueEditor::SortGroups(ItemList* pItemList, const char* szSort) { GroupSorter sorter(m_pDownloadQueue->GetQueue(), pItemList); return sorter.Execute(szSort); } void QueueEditor::ReorderFiles(ItemList* pItemList) { if (pItemList->size() == 0) { return; } EditItem* pFirstItem = pItemList->front(); NZBInfo* pNZBInfo = pFirstItem->m_pFileInfo->GetNZBInfo(); unsigned int iInsertPos = 0; // now can reorder for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++) { EditItem* pItem = *it; FileInfo* pFileInfo = pItem->m_pFileInfo; // move file item FileList::iterator it2 = std::find(pNZBInfo->GetFileList()->begin(), pNZBInfo->GetFileList()->end(), pFileInfo); if (it2 != pNZBInfo->GetFileList()->end()) { pNZBInfo->GetFileList()->erase(it2); pNZBInfo->GetFileList()->insert(pNZBInfo->GetFileList()->begin() + iInsertPos, pFileInfo); iInsertPos++; } delete pItem; } } void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString) { debug("QueueEditor: setting nzb parameter '%s' for '%s'", szParamString, Util::BaseFileName(pNZBInfo->GetFilename())); char* szStr = strdup(szParamString); char* szValue = strchr(szStr, '='); if (szValue) { *szValue = '\0'; szValue++; pNZBInfo->GetParameters()->SetParameter(szStr, szValue); } else { error("Could not set nzb parameter for %s: invalid argument: %s", pNZBInfo->GetName(), szParamString); } free(szStr); } void QueueEditor::SetNZBDupeParam(NZBInfo* pNZBInfo, DownloadQueue::EEditAction eAction, const char* szText) { debug("QueueEditor: setting dupe parameter %i='%s' for '%s'", (int)eAction, szText, pNZBInfo->GetName()); switch (eAction) { case DownloadQueue::eaGroupSetDupeKey: pNZBInfo->SetDupeKey(szText); break; case DownloadQueue::eaGroupSetDupeScore: pNZBInfo->SetDupeScore(atoi(szText)); break; case DownloadQueue::eaGroupSetDupeMode: { EDupeMode eMode = dmScore; if (!strcasecmp(szText, "SCORE")) { eMode = dmScore; } else if (!strcasecmp(szText, "ALL")) { eMode = dmAll; } else if (!strcasecmp(szText, "FORCE")) { eMode = dmForce; } else { error("Could not set duplicate mode for %s: incorrect mode (%s)", pNZBInfo->GetName(), szText); return; } pNZBInfo->SetDupeMode(eMode); break; } default: // suppress compiler warning break; } } bool QueueEditor::DeleteUrl(NZBInfo* pNZBInfo, DownloadQueue::EEditAction eAction) { return g_pUrlCoordinator->DeleteQueueEntry(m_pDownloadQueue, pNZBInfo, eAction == DownloadQueue::eaGroupFinalDelete); }