diff --git a/WechatExporter.xcodeproj/xcshareddata/xcschemes/WechatExporter.xcscheme b/WechatExporter.xcodeproj/xcshareddata/xcschemes/WechatExporter.xcscheme index 0bed96b..2aaeba5 100644 --- a/WechatExporter.xcodeproj/xcshareddata/xcschemes/WechatExporter.xcscheme +++ b/WechatExporter.xcodeproj/xcshareddata/xcschemes/WechatExporter.xcscheme @@ -31,7 +31,7 @@ m_tag + std::to_string(++m_executor->m_tid); setThreadName(tname.c_str()); #endif @@ -121,6 +121,11 @@ size_t AsyncExecutor::getNumberOfQueue() const return size; } +void AsyncExecutor::shutdown() +{ + +} + void AsyncExecutor::cancel() { std::queue tasks; @@ -137,7 +142,6 @@ void AsyncExecutor::cancel() } } - void AsyncExecutor::ThreadFunc() { for (;;) diff --git a/WechatExporter/core/AsyncExecutor.h b/WechatExporter/core/AsyncExecutor.h index 1832e3d..58258ad 100755 --- a/WechatExporter/core/AsyncExecutor.h +++ b/WechatExporter/core/AsyncExecutor.h @@ -68,7 +68,7 @@ public: virtual ~Callback() {} }; -private: +protected: class Thread { public: @@ -78,6 +78,7 @@ private: AsyncExecutor* m_executor; std::unique_ptr m_thread; void ThreadFunc(); + }; public: @@ -85,23 +86,23 @@ public: ~AsyncExecutor(); static uint32_t genNextTaskId(); - void addTask(Task *task); + virtual void addTask(Task *task); size_t getNumberOfQueue() const; - + void shutdown(); void cancel(); -#ifndef NDEBUG +#if !defined(NDEBUG) || defined(DBG_PERF) void setTag(const std::string& tag) { m_tag = tag; } #endif -private: +protected: Callback* m_callback; -#ifndef NDEBUG +#if !defined(NDEBUG) || defined(DBG_PERF) std::string m_tag; uint32_t m_tid; #endif @@ -114,6 +115,7 @@ private: int m_reserve_threads; int m_max_threads; int m_nthreads; + int m_threads_waiting; std::list m_dead_threads; diff --git a/WechatExporter/core/AsyncTask.cpp b/WechatExporter/core/AsyncTask.cpp index 29ca9b7..822d1c1 100755 --- a/WechatExporter/core/AsyncTask.cpp +++ b/WechatExporter/core/AsyncTask.cpp @@ -270,6 +270,11 @@ Mp3Task::Mp3Task(const std::string &pcm, const std::string& mp3, unsigned int mt { } +void Mp3Task::swapBuffer(std::vector& buffer) +{ + m_pcmData.swap(buffer); +} + bool Mp3Task::run() { std::vector pcmData; @@ -278,6 +283,7 @@ bool Mp3Task::run() if (pcmToMp3(pcmData, m_mp3)) { updateFileTime(m_mp3, m_mtime); + std::this_thread::sleep_for(std::chrono::milliseconds(192)); return true; } } diff --git a/WechatExporter/core/AsyncTask.h b/WechatExporter/core/AsyncTask.h index b732b4a..9e5ce99 100644 --- a/WechatExporter/core/AsyncTask.h +++ b/WechatExporter/core/AsyncTask.h @@ -118,12 +118,16 @@ public: return "Mp3: " + m_mp3; } + void swapBuffer(std::vector& buffer); + bool run(); private: std::string m_pcm; std::string m_mp3; unsigned int m_mtime; + + std::vector m_pcmData; }; class PdfTask : public AsyncExecutor::Task diff --git a/WechatExporter/core/Exporter.cpp b/WechatExporter/core/Exporter.cpp index 2b4600f..cbb8fc1 100755 --- a/WechatExporter/core/Exporter.cpp +++ b/WechatExporter/core/Exporter.cpp @@ -213,7 +213,7 @@ void Exporter::swapUsersAndSessions(std::vectordebug("UA: " + m_wechatInfo.buildUserAgent()); @@ -469,14 +475,14 @@ bool Exporter::exportUser(Friend& user, std::string& userOutputPath) int count = exportSession(*myself, msgParser, *it, userBase, outputBase); m_logger->write(formatString(getLocaleString("Succeeded handling %d messages."), count)); - + if (count > 0) { std::string userItem = getTemplate("listitem"); replaceAll(userItem, "%%ITEMPICPATH%%", "Portrait/" + it->getLocalPortrait()); if ((m_options & SPO_IGNORE_HTML_ENC) == 0) { - replaceAll(userItem, "%%ITEMLINK%%", encodeUrl(it->getOutputFileName()) + "." + ((m_options & SPO_PDF_MODE && NULL != m_pdfConverter) ? "pdf" : m_extName)); + replaceAll(userItem, "%%ITEMLINK%%", encodeUrl(it->getOutputFileName()) + "." + m_extName); replaceAll(userItem, "%%ITEMTEXT%%", safeHTML(sessionDisplayName)); } else @@ -492,14 +498,15 @@ bool Exporter::exportUser(Friend& user, std::string& userOutputPath) if (pdfOutput) { + // std::string std::string htmlFileName = combinePath(outputBase, it->getOutputFileName() + "." + m_extName); if (existsFile(htmlFileName)) { - std::string pdfFileName = combinePath(outputBase, it->getOutputFileName() + ".pdf"); - taskManager.convertPdf(&(*it), htmlFileName, pdfFileName, m_pdfConverter); + std::string pdfFileName = combinePath(m_output, "pdf", userOutputPath, it->getOutputFileName() + ".pdf"); + // taskManager.convertPdf(&(*it), htmlFileName, pdfFileName, m_pdfConverter); + m_pdfConverter->convert(htmlFileName, pdfFileName); } - - pdfSessions.push_back(&(*it)); + // pdfSessions.push_back(&(*it)); } } diff --git a/WechatExporter/core/TaskManager.cpp b/WechatExporter/core/TaskManager.cpp old mode 100644 new mode 100755 index e4fa6c0..0877cfc --- a/WechatExporter/core/TaskManager.cpp +++ b/WechatExporter/core/TaskManager.cpp @@ -12,14 +12,15 @@ TaskManager::TaskManager(bool needPdfExecutor, Logger* logger) : m_logger(logger), m_downloadExecutor(NULL), m_audioExecutor(NULL), m_pdfExecutor(NULL) { - m_downloadExecutor = new AsyncExecutor(4, 8, this); - m_audioExecutor = new AsyncExecutor(2, 4, this); + m_downloadExecutor = new AsyncExecutor(2, 2, this); + m_audioExecutor = new AsyncExecutor(2, 2, this); + // m_audioExecutor = m_downloadExecutor; if (needPdfExecutor) { m_pdfExecutor = new AsyncExecutor(4, 4, this); } -#ifndef NDEBUG +#if !defined(NDEBUG) || defined(DBG_PERF) m_downloadExecutor->setTag("dl"); m_audioExecutor->setTag("audio"); if (m_pdfExecutor) @@ -93,15 +94,39 @@ void TaskManager::cancel() size_t TaskManager::getNumberOfQueue() const { - size_t size = m_downloadExecutor->getNumberOfQueue(); - size += m_audioExecutor->getNumberOfQueue(); - size += m_pdfTaskQueue.size(); + size_t numberOfDownloads = m_downloadExecutor->getNumberOfQueue(); + size_t numberOfAudio = m_audioExecutor->getNumberOfQueue(); + size_t numberOfPdf = m_pdfTaskQueue.size(); - std::unique_lock lock(m_mutex); - size += m_copyTaskQueue.size(); - size += m_pdfTaskQueue.size(); + { + std::unique_lock lock(m_mutex); + numberOfDownloads += m_copyTaskQueue.size(); + numberOfPdf += m_pdfTaskQueue.size(); + } - return size; + std::string queueDesc = ""; + if (numberOfDownloads > 0) + { + queueDesc += std::to_string(numberOfDownloads) + " downloads"; + } + if (numberOfAudio > 0) + { + if (!queueDesc.empty()) + { + queueDesc += ", "; + } + queueDesc += std::to_string(numberOfAudio) + " audios"; + } + if (numberOfPdf > 0) + { + if (!queueDesc.empty()) + { + queueDesc += ", "; + } + queueDesc += std::to_string(numberOfPdf) + " pdf files"; + } + + return numberOfDownloads + numberOfAudio + numberOfPdf; } void TaskManager::shutdownExecutors() diff --git a/WechatExporter/core/TaskManager.h b/WechatExporter/core/TaskManager.h index cfad887..d95741e 100644 --- a/WechatExporter/core/TaskManager.h +++ b/WechatExporter/core/TaskManager.h @@ -37,6 +37,8 @@ private: std::map> m_copyTaskQueue; std::map m_pdfTaskQueue; + std::queue> m_Buffers; + public: TaskManager(bool needPdfExecutor, Logger* logger); diff --git a/vcproject/AppConfiguration.cpp b/vcproject/AppConfiguration.cpp index b79c570..eb9e41e 100755 --- a/vcproject/AppConfiguration.cpp +++ b/vcproject/AppConfiguration.cpp @@ -194,7 +194,7 @@ BOOL AppConfiguration::GetSupportingFilter() BOOL AppConfiguration::IsPdfSupported() { - PdfConverterImpl converter; + PdfConverterImpl converter(NULL); return converter.isPdfSupported() ? TRUE : FALSE; } diff --git a/vcproject/PdfConverterImpl.h b/vcproject/PdfConverterImpl.h index 91c1630..079fd72 100755 --- a/vcproject/PdfConverterImpl.h +++ b/vcproject/PdfConverterImpl.h @@ -2,11 +2,12 @@ #include "stdafx.h" #include "Core.h" +#include class PdfConverterImpl : public PdfConverter { public: - PdfConverterImpl() : m_pdfSupported(false) + PdfConverterImpl(LPCTSTR outputDir) : m_pdfSupported(false) { if (isChromeInstalled(m_assemblyPath)) { @@ -18,6 +19,11 @@ public: m_pdfSupported = true; m_param = TEXT("--headless --disable-extensions --disable-gpu --print-to-pdf=\"%%DEST%%\" --print-to-pdf-no-header \"file://%%SRC%%\""); } + + if (NULL != outputDir) + { + initShellFile(outputDir); + } } bool isPdfSupported() const @@ -27,10 +33,89 @@ public: ~PdfConverterImpl() { + } + + void executeCommand() + { + if (!::PathFileExists(m_shellPath)) + { + return; + } + + const char *pauseCmd = "pause\r\n"; + appendFile(m_shellPath, reinterpret_cast(pauseCmd), strlen(pauseCmd)); + + ShellExecute(NULL, TEXT("open"), m_shellPath, NULL, NULL, SW_SHOW); + } + + void buildCommands(LPCTSTR outputBase, LPCTSTR userName) + { } + CString buildCommand(const CString& htmlPath, const CString& pdfPath) + { + if (!m_pdfSupported) + { + return ""; + } + + TCHAR output[MAX_PATH * 4] = { 0 }; + DWORD cchUrl = MAX_PATH * 4; // max posible buffer size + + HRESULT res = UrlCreateFromPath(htmlPath, output, &cchUrl, NULL); + if (FAILED(res)) + { + return ""; + } + + CString command(TEXT("\"")); + command += m_assemblyPath; + command += TEXT("\" "); + command += TEXT("--headless --print-to-pdf-no-header --print-to-pdf=\"") + pdfPath + TEXT("\" "); + command += output; + + return command; + } + bool convert(const std::string& htmlPath, const std::string& pdfPath) + { + if (!m_pdfSupported) + { + return false; + } + + CW2T pszHtmlPath(CA2W(htmlPath.c_str(), CP_UTF8)); + TCHAR url[MAX_PATH * 4] = { 0 }; + DWORD cchUrl = MAX_PATH * 4; // max posible buffer size + + HRESULT res = UrlCreateFromPath(pszHtmlPath, url, &cchUrl, NULL); + if (FAILED(res)) + { + return false; + } + + CW2T pszPdfPath(CA2W(pdfPath.c_str(), CP_UTF8)); + + CString command(TEXT("\"")); + command += m_assemblyPath; + command += TEXT("\" "); + command += TEXT("--headless --disable-extensions --disable-gpu --print-to-pdf-no-header --print-to-pdf=\""); + command += (LPCTSTR)pszPdfPath; + command += TEXT("\" "); + command += url; + command += TEXT("\r\n"); + + CT2A content(command, CP_UTF8); + + appendFile((LPCTSTR)m_shellPath, reinterpret_cast((LPCSTR)content), strlen(content)); + // appendFile((LPCTSTR)m_shellPath, reinterpret_cast((LPCTSTR)command), _tcslen(command) * sizeof(TCHAR)); + + return true; + } + + + bool convert2(const std::string& htmlPath, const std::string& pdfPath) { CString param = m_param; CW2T pszSrc(CA2W(htmlPath.c_str(), CP_UTF8)); @@ -97,8 +182,39 @@ protected: return installed; } + void initShellFile(LPCTSTR outputDir) + { + TCHAR shellFile[MAX_PATH] = { 0 }; + PathCombine(shellFile, outputDir, TEXT("pdf.bat")); + m_shellPath = shellFile; + ::DeleteFile(shellFile); + + LPCTSTR chcp = TEXT("chcp 65001\r\n"); + CW2A pszU(chcp, CP_UTF8); + + appendFile((LPCTSTR)m_shellPath, reinterpret_cast((LPCSTR)pszU), strlen(pszU)); + } + + bool appendFile(LPCTSTR path, const unsigned char* data, size_t dataLength) + { + HANDLE hFile = ::CreateFile(path, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + return false; + } + + ::SetFilePointer(hFile, 0, 0, FILE_END); + DWORD dwBytesToWrite = static_cast(dataLength); + DWORD dwBytesWritten = 0; + BOOL bErrorFlag = WriteFile(hFile, data, dwBytesToWrite, &dwBytesWritten, NULL); + ::CloseHandle(hFile); + return (TRUE == bErrorFlag); + + } + private: bool m_pdfSupported; CString m_assemblyPath; + CString m_shellPath; CString m_param; }; diff --git a/vcproject/View.h b/vcproject/View.h index 82d733d..9e020a2 100755 --- a/vcproject/View.h +++ b/vcproject/View.h @@ -666,16 +666,17 @@ public: std::string backup = manifest.getPath(); - TCHAR buffer[MAX_PATH] = { 0 }; - GetDlgItemText(IDC_OUTPUT, buffer, MAX_PATH); - if (!::PathFileExists(buffer)) + TCHAR outputDir[MAX_PATH] = { 0 }; + GetDlgItemText(IDC_OUTPUT, outputDir, MAX_PATH); + if (!::PathFileExists(outputDir)) { MsgBox(m_hWnd, IDS_INVALID_OUTPUT_DIR); return 0; } - CW2A output(CT2W(buffer), CP_UTF8); + CW2A output(CT2W(outputDir), CP_UTF8); - DWORD dwRet = GetCurrentDirectory(MAX_PATH, buffer); + TCHAR curDir[MAX_PATH] = { 0 }; + DWORD dwRet = GetCurrentDirectory(MAX_PATH, curDir); if (dwRet == 0) { // printf("GetCurrentDirectory failed (%d)\n", GetLastError()); @@ -700,7 +701,7 @@ public: CListBox lstboxLogs = GetDlgItem(IDC_LOGS); lstboxLogs.ResetContent(); - CW2A resDir(CT2W(buffer), CP_UTF8); + CW2A resDir(CT2W(curDir), CP_UTF8); std::map> usersAndSessions; int numberOfSessions = 0; @@ -720,9 +721,13 @@ public: std::string log = "Record Count:" + std::to_string(numberOfRecords); m_logger->debug(log); #endif - if (outputFormat == AppConfiguration::OUTPUT_FORMAT_PDF && NULL == m_pdfConverter) + if (NULL != m_pdfConverter) { - m_pdfConverter = new PdfConverterImpl(); + delete m_pdfConverter; + } + if (outputFormat == AppConfiguration::OUTPUT_FORMAT_PDF) + { + m_pdfConverter = new PdfConverterImpl(outputDir); } m_exporter = new Exporter((LPCSTR)resDir, backup, (LPCSTR)output, m_logger, m_pdfConverter); @@ -743,6 +748,10 @@ public: } else if (outputFormat == AppConfiguration::OUTPUT_FORMAT_PDF) { + TCHAR pdfDir[MAX_PATH] = { 0 }; + PathCombine(pdfDir, outputDir, TEXT("pdf")); + CreateDirectory(pdfDir, NULL); + m_exporter->setPdfMode(); } @@ -767,6 +776,13 @@ public: LRESULT OnComplete(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { + if (NULL != m_pdfConverter) + { + m_pdfConverter->executeCommand(); + delete m_pdfConverter; + m_pdfConverter = NULL; + } + if (m_exporter) { m_exporter->waitForComplition(); @@ -779,7 +795,7 @@ public: KillTimer(m_eventIdProgress); m_eventIdProgress = 0; } - + PostMessage(WM_UPD_VIEWSTATE, VS_IDLE, 1); return 0; } @@ -830,7 +846,6 @@ public: m_progressListCtrl.ClearProgressBar(); int nItem = wParam; - CString text; text.LoadString((lParam != 0) ? IDS_SESSION_CANCELLED : IDS_SESSION_DONE); m_progressListCtrl.SetItemText(nItem, SUBITEM_PROGRESS, text);