From 92fb5134a296c08559405afb110356b149208587 Mon Sep 17 00:00:00 2001 From: John Patrick Poet Date: Sat, 23 Nov 2024 10:30:34 -0800 Subject: [PATCH] ExternalRecorder: minor fixes and cleanup of side-band messages. --- .../libmythtv/recorders/ExternalChannel.cpp | 1 - .../libmythtv/recorders/ExternalRecorder.cpp | 9 ++ .../recorders/ExternalStreamHandler.cpp | 90 +++++++++++++++---- .../recorders/ExternalStreamHandler.h | 3 + .../mythexternrecorder/MythExternControl.cpp | 18 ++-- .../mythexternrecorder/MythExternControl.h | 3 +- .../mythexternrecorder/MythExternRecApp.cpp | 66 +++++++++----- .../mythexternrecorder/MythExternRecApp.h | 1 + 8 files changed, 144 insertions(+), 47 deletions(-) diff --git a/mythtv/libs/libmythtv/recorders/ExternalChannel.cpp b/mythtv/libs/libmythtv/recorders/ExternalChannel.cpp index 43b0f88b01f..c162229b083 100644 --- a/mythtv/libs/libmythtv/recorders/ExternalChannel.cpp +++ b/mythtv/libs/libmythtv/recorders/ExternalChannel.cpp @@ -215,7 +215,6 @@ bool ExternalChannel::EnterPowerSavingMode(void) uint ExternalChannel::GetTuneStatus(void) { - if (!m_backgroundTuning) return 3; diff --git a/mythtv/libs/libmythtv/recorders/ExternalRecorder.cpp b/mythtv/libs/libmythtv/recorders/ExternalRecorder.cpp index b36fb9a6a87..f9d7625cc52 100644 --- a/mythtv/libs/libmythtv/recorders/ExternalRecorder.cpp +++ b/mythtv/libs/libmythtv/recorders/ExternalRecorder.cpp @@ -103,6 +103,15 @@ void ExternalRecorder::run(void) m_error = "Stream handler died unexpectedly."; LOG(VB_GENERAL, LOG_ERR, LOC + m_error); } + + if (m_streamHandler->IsDamaged()) + { + LOG(VB_GENERAL, LOG_INFO, LOC + + QString("Recording is damaged. Setting status to %1") + .arg(RecStatus::toString(RecStatus::Failing, kSingleRecord))); + SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__); + m_streamHandler->ClearDamaged(); + } } StopStreaming(); diff --git a/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp b/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp index a8ae929eae4..147923726bb 100644 --- a/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp +++ b/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.cpp @@ -677,7 +677,8 @@ void ExternalStreamHandler::run(void) std::this_thread::sleep_for(20s); if (!RestartStream()) { - LOG(VB_RECORD, LOG_ERR, LOC + "Failed to restart stream."); + LOG(VB_RECORD, LOG_ERR, LOC + + "Failed to restart stream."); m_bError = true; } continue; @@ -1073,6 +1074,7 @@ bool ExternalStreamHandler::RestartStream(void) bool streaming = (StreamingCount() > 0); LOG(VB_RECORD, LOG_INFO, LOC + "Restarting stream."); + m_damaged = true; if (streaming) StopStreaming(); @@ -1637,7 +1639,7 @@ bool ExternalStreamHandler::ProcessJson(const QVariantMap & vmsg, if (!okay) level = LOG_WARNING; else if (cmd == "SendBytes" || - (cmd == "TuneStatus" && + (cmd == "TuneStatus?" && elements["message"] == "InProgress")) level = LOG_DEBUG; @@ -1672,8 +1674,8 @@ bool ExternalStreamHandler::ProcessJson(const QVariantMap & vmsg, bool ExternalStreamHandler::CheckForError(void) { - QString result; - bool err = false; + QByteArray response; + bool err = false; QMutexLocker locker(&m_ioLock); @@ -1692,26 +1694,82 @@ bool ExternalStreamHandler::CheckForError(void) do { - result = m_io->GetStatus(0ms); - if (!result.isEmpty()) + response = m_io->GetStatus(0ms); + if (!response.isEmpty()) { - if (m_apiVersion > 1) + if (m_apiVersion > 2) { - QStringList tokens = result.split(':', Qt::SkipEmptyParts); - tokens.removeFirst(); - result = tokens.join(':'); - for (int idx = 1; idx < tokens.size(); ++idx) - err |= tokens[idx].startsWith("ERR"); + QJsonParseError parseError; + QJsonDocument doc; + QVariantMap elements; + + doc = QJsonDocument::fromJson(response, &parseError); + + if (parseError.error != QJsonParseError::NoError) + { + LOG(VB_GENERAL, LOG_ERR, LOC + + QString("ExternalRecorder returned invalid JSON message: %1: %2\n%3\n") + .arg(parseError.offset).arg(parseError.errorString()) + .arg(QString(response))); + } + else + { + LogLevel_t level; + elements = doc.toVariant().toMap(); + if (elements.find("command") != elements.end() && + elements["command"] == "STATUS") + { + QString status = elements["status"].toString(); + if (status.startsWith("err", Qt::CaseInsensitive)) + { + level = LOG_ERR; + err |= true; + } + else if (status.startsWith("warn", + Qt::CaseInsensitive)) + level = LOG_WARNING; + else if (status.startsWith("damage", + Qt::CaseInsensitive)) + { + level = LOG_WARNING; + m_damaged |= true; + } + else + level = LOG_INFO; + LOG(VB_RECORD, level, + LOC + elements["message"].toString()); + } + } } else { - err |= result.startsWith("STATUS:ERR"); - } + QString res = QString(response); + if (m_apiVersion == 2) + { + QStringList tokens = res.split(':', Qt::SkipEmptyParts); + tokens.removeFirst(); + res = tokens.join(':'); + for (int idx = 1; idx < tokens.size(); ++idx) + { + err |= tokens[idx].startsWith("ERR", + Qt::CaseInsensitive); + m_damaged |= tokens[idx].startsWith("damage", + Qt::CaseInsensitive); + } + } + else + { + err |= res.startsWith("STATUS:ERR", + Qt::CaseInsensitive); + m_damaged |= res.startsWith("STATUS:DAMAGE", + Qt::CaseInsensitive); + } - LOG(VB_RECORD, (err ? LOG_WARNING : LOG_INFO), LOC + result); + LOG(VB_RECORD, (err ? LOG_WARNING : LOG_INFO), LOC + res); + } } } - while (!result.isEmpty()); + while (!response.isEmpty()); return err; } diff --git a/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.h b/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.h index 56873113aae..8ae55e2c7af 100644 --- a/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.h +++ b/mythtv/libs/libmythtv/recorders/ExternalStreamHandler.h @@ -88,6 +88,8 @@ class ExternalStreamHandler : public StreamHandler QString GetDescription(void) { return m_loc; } QString UpdateDescription(void); + bool IsDamaged(void) const { return m_damaged; } + void ClearDamaged(void) { m_damaged = false; } bool IsAppOpen(void); bool IsTSOpen(void); bool HasTuner(void) const { return m_hasTuner; } @@ -144,6 +146,7 @@ class ExternalStreamHandler : public StreamHandler QByteArray m_replayBuffer; bool m_replay {false}; bool m_xon {false}; + bool m_damaged {false}; // for implementing Get & Return static QMutex s_handlersLock; diff --git a/mythtv/programs/mythexternrecorder/MythExternControl.cpp b/mythtv/programs/mythexternrecorder/MythExternControl.cpp index 7649da8ffb7..8019c120cac 100644 --- a/mythtv/programs/mythexternrecorder/MythExternControl.cpp +++ b/mythtv/programs/mythexternrecorder/MythExternControl.cpp @@ -229,11 +229,11 @@ bool Commands::SendStatus(const QString & command, if (!command.isEmpty()) { - if (command == m_prevCmd) + if (command + response + status == m_prevStatus) { if (++m_repCmdCnt % 25 == 0) { - LOG(VB_RECORD, LOG_INFO, LOC + + LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Processing '%1' --> '%2' (Repeated 25 times)") .arg(command, QString(msgbuf))); } @@ -242,20 +242,22 @@ bool Commands::SendStatus(const QString & command, { if (m_repCmdCnt) { - LOG(VB_RECORD, LOG_INFO, - LOC + QString("Processing '%1' (Repeated %2 times)") - .arg(m_prevCmd).arg(m_repCmdCnt % 25)); + LOG(VB_RECORD, LOG_DEBUG, + LOC + QString("Processing '%1' --> '%2' (Repeated %2 times)") + .arg(m_prevMsgBuf).arg(m_repCmdCnt % 25)); m_repCmdCnt = 0; } - LOG(VB_RECORD, LOG_INFO, LOC + + LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Processing '%1' --> '%2'") .arg(command, QString(msgbuf))); } - m_prevCmd = command; + m_prevStatus = command + response + status; + m_prevMsgBuf = QString(msgbuf); } else { - m_prevCmd.clear(); + m_prevStatus.clear(); + m_prevMsgBuf.clear(); m_repCmdCnt = 0; } diff --git a/mythtv/programs/mythexternrecorder/MythExternControl.h b/mythtv/programs/mythexternrecorder/MythExternControl.h index 15296926488..9d877ead113 100644 --- a/mythtv/programs/mythexternrecorder/MythExternControl.h +++ b/mythtv/programs/mythexternrecorder/MythExternControl.h @@ -114,7 +114,8 @@ class Commands : public QObject std::thread m_thread; size_t m_repCmdCnt { 0 }; - QString m_prevCmd; + QString m_prevStatus; + QString m_prevMsgBuf; MythExternControl* m_parent { nullptr }; int m_apiVersion { -1 }; diff --git a/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp b/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp index bbab3348ebc..a433faccc2d 100644 --- a/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp +++ b/mythtv/programs/mythexternrecorder/MythExternRecApp.cpp @@ -327,13 +327,7 @@ bool MythExternRecApp::Open(void) void MythExternRecApp::TerminateProcess(QProcess & proc, const QString & desc) const { - if (proc.state() == QProcess::Running) - { - LOG(VB_RECORD, LOG_INFO, LOC + - QString("Sending SIGINT to %1(%2)").arg(desc).arg(proc.processId())); - kill(proc.processId(), SIGINT); - proc.waitForFinished(5000); - } + m_terminating = true; if (proc.state() == QProcess::Running) { LOG(VB_RECORD, LOG_INFO, LOC + @@ -348,6 +342,7 @@ void MythExternRecApp::TerminateProcess(QProcess & proc, const QString & desc) c proc.kill(); proc.waitForFinished(); } + m_terminating = false; } Q_SLOT void MythExternRecApp::Close(void) @@ -974,7 +969,7 @@ Q_SLOT void MythExternRecApp::StopStreaming(const QString & serial, bool silent) if (silent) { emit SendMessage("StopStreaming", serial, - "Already not Streaming", "STATUS"); + "Already not Streaming", "INFO"); } else { @@ -1031,32 +1026,61 @@ Q_SLOT void MythExternRecApp::ProcStateChanged(QProcess::ProcessState newState) if (unexpected) { emit Streaming(false); - MythLog("ERR Unexpected " + msg); + emit SendMessage("STATUS", "0", "Unexpected: " + msg, "ERR"); } } Q_SLOT void MythExternRecApp::ProcError(QProcess::ProcessError /*error */) { - LOG(VB_RECORD, LOG_ERR, LOC + QString(": Error: %1") - .arg(m_proc.errorString())); - MythLog(m_proc.errorString()); + if (m_terminating) + { + LOG(VB_RECORD, LOG_INFO, LOC + QString(": %1") + .arg(m_proc.errorString())); + emit SendMessage("STATUS", "0", m_proc.errorString(), "INFO"); + } + else + { + LOG(VB_RECORD, LOG_ERR, LOC + QString(": Error: %1") + .arg(m_proc.errorString())); + emit SendMessage("STATUS", "0", m_proc.errorString(), "ERR"); + } } Q_SLOT void MythExternRecApp::ProcReadStandardError(void) { QByteArray buf = m_proc.readAllStandardError(); QString msg = QString::fromUtf8(buf).trimmed(); + QList msgs = msg.split('\n'); + QString message; - // Log any error messages - if (!msg.isEmpty()) + for (int idx=0; idx < msgs.count(); ++idx) { - LOG(VB_RECORD, LOG_INFO, LOC + QString(">>> %1") - .arg(msg)); -#if 0 // Show even long messages in mythbackend log - if (msg.size() > 79) - msg = QString("Application message: see '%1'").arg(m_logFile); -#endif - MythLog(msg); + // Log any error messages + if (!msgs[idx].isEmpty()) + { + QStringList tokens = QString(msgs[idx]) + .split(':', Qt::SkipEmptyParts); + tokens.removeFirst(); + if (tokens.empty()) + message = msgs[idx]; + else + message = tokens.join(':'); + if (msgs[idx].startsWith("err", Qt::CaseInsensitive)) + { + LOG(VB_RECORD, LOG_ERR, LOC + QString(">>> %1").arg(msgs[idx])); + emit SendMessage("STATUS", "0", message, "ERR"); + } + else if (msgs[idx].startsWith("warn", Qt::CaseInsensitive)) + { + LOG(VB_RECORD, LOG_WARNING, LOC + QString(">>> %1").arg(msgs[idx])); + emit SendMessage("STATUS", "0", message, "WARN"); + } + else + { + LOG(VB_RECORD, LOG_DEBUG, LOC + QString(">>> %1").arg(msgs[idx])); + emit SendMessage("STATUS", "0", message, "INFO"); + } + } } } diff --git a/mythtv/programs/mythexternrecorder/MythExternRecApp.h b/mythtv/programs/mythexternrecorder/MythExternRecApp.h index e96f5680a81..944b6ddccc5 100644 --- a/mythtv/programs/mythexternrecorder/MythExternRecApp.h +++ b/mythtv/programs/mythexternrecorder/MythExternRecApp.h @@ -99,6 +99,7 @@ class MythExternRecApp : public QObject QString replace_extra_args(const QString & var, const QVariantMap & extra_args) const; + mutable bool m_terminating { false }; bool m_fatal { false }; QString m_fatalMsg;