From ea3dc2d7f5f00e96535e21006b4b810abd7ba9ad Mon Sep 17 00:00:00 2001 From: Paul Harrison Date: Wed, 31 Jul 2013 19:55:53 +0100 Subject: [PATCH] mythutil: add an option to check recordings This adds an option to check the integrity of all recordings. Currently it checks if the file exists, they are not zero bytes in size and they have a seek table. You can optionally fix the seek tables of any recordings were it is missing. You run the command like this: mythutil -v none --checkrecordings --fixseektable --- .../programs/mythutil/commandlineparser.cpp | 15 +- mythtv/programs/mythutil/main.cpp | 2 + mythtv/programs/mythutil/mythutil.pro | 4 +- mythtv/programs/mythutil/recordingutils.cpp | 157 ++++++++++++++++++ mythtv/programs/mythutil/recordingutils.h | 4 + 5 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 mythtv/programs/mythutil/recordingutils.cpp create mode 100644 mythtv/programs/mythutil/recordingutils.h diff --git a/mythtv/programs/mythutil/commandlineparser.cpp b/mythtv/programs/mythutil/commandlineparser.cpp index 4ffa672c56b..f5e870fc1c3 100644 --- a/mythtv/programs/mythutil/commandlineparser.cpp +++ b/mythtv/programs/mythutil/commandlineparser.cpp @@ -72,6 +72,11 @@ void MythUtilCommandLineParser::LoadArguments(void) ->SetGroup("Recording Markup") ->SetRequiredChild(QStringList("chanid") << "starttime") + // recordingutils.cpp + << add("--checkrecordings", "checkrecordings", false, + "Check all recordings exist and have a seektable etc.", "") + ->SetGroup("Recording Utils") + // backendutils.cpp << add("--resched", "resched", false, "Trigger a run of the recording scheduler on the existing " @@ -177,11 +182,15 @@ void MythUtilCommandLineParser::LoadArguments(void) add("--fullscreen", "fullscreen", false, "(optional) display notification in full screen mode", "") ->SetChildOf("notification"); add("--error", "error", false, "(optional) set notification to be displayed as an error", "") - ->SetChildOf("notification"); + ->SetChildOf("notification"); add("--visibility", "visibility", 0, "(optional) bitmask indicating where to show the notification", "") - ->SetChildOf("notification"); + ->SetChildOf("notification"); add("--type", "type", "type", "(optional) type of notification (normal, error, warning, check", "") - ->SetChildOf("notification"); + ->SetChildOf("notification"); + + // recordingutils.cpp + add("--fixseektable", "fixseektable", false, "(optional) fix the seektable if missing for a recording", "") + ->SetChildOf("checkrecordings"); // Generic Options used by more than one utility addRecording(); diff --git a/mythtv/programs/mythutil/main.cpp b/mythtv/programs/mythutil/main.cpp index 0bc49f22bc6..85e4d4f618d 100644 --- a/mythtv/programs/mythutil/main.cpp +++ b/mythtv/programs/mythutil/main.cpp @@ -21,6 +21,7 @@ #include "jobutils.h" #include "markuputils.h" #include "messageutils.h" +#include "recordingutils.h" #include "signalhandling.h" @@ -105,6 +106,7 @@ int main(int argc, char *argv[]) registerJobUtils(utilMap); registerMarkupUtils(utilMap); registerMessageUtils(utilMap); + registerRecordingUtils(utilMap); bool cmdFound = false; int cmdResult = GENERIC_EXIT_OK; diff --git a/mythtv/programs/mythutil/mythutil.pro b/mythtv/programs/mythutil/mythutil.pro index dd401b5a3bc..2c4a7c0b6b2 100644 --- a/mythtv/programs/mythutil/mythutil.pro +++ b/mythtv/programs/mythutil/mythutil.pro @@ -21,9 +21,9 @@ QMAKE_CLEAN += $(TARGET) # Input HEADERS += mythutil.h commandlineparser.h HEADERS += backendutils.h fileutils.h jobutils.h markuputils.h -HEADERS += messageutils.h mpegutils.h +HEADERS += messageutils.h mpegutils.h recordingutils.h SOURCES += main.cpp mythutil.cpp commandlineparser.cpp SOURCES += backendutils.cpp fileutils.cpp jobutils.cpp markuputils.cpp -SOURCES += messageutils.cpp mpegutils.cpp +SOURCES += messageutils.cpp mpegutils.cpp recordingutils.cpp mingw: LIBS += -lwinmm -lws2_32 diff --git a/mythtv/programs/mythutil/recordingutils.cpp b/mythtv/programs/mythutil/recordingutils.cpp new file mode 100644 index 00000000000..288aa412ce4 --- /dev/null +++ b/mythtv/programs/mythutil/recordingutils.cpp @@ -0,0 +1,157 @@ +// C++ includes +#include +#include + +// Qt +#include +#include + +// libmyth* includes +#include "exitcodes.h" +#include "mythlogging.h" +#include "remoteutil.h" +#include "remotefile.h" +#include "mythsystem.h" + +// Local includes +#include "recordingutils.h" + +static QString CreateProgramInfoString(const ProgramInfo &pginfo) +{ + QDateTime recstartts = pginfo.GetRecordingStartTime(); + QDateTime recendts = pginfo.GetRecordingEndTime(); + + QString timedate = QString("%1 - %2") + .arg(MythDate::toString( + recstartts, MythDate::kDateTimeFull | MythDate::kSimplify)) + .arg(MythDate::toString(recendts, MythDate::kTime)); + + QString title = pginfo.GetTitle(); + + QString extra; + + if (!pginfo.GetSubtitle().isEmpty()) + { + extra = QString(" ~ ") + pginfo.GetSubtitle(); + int maxll = max(title.length(), 20); + if (extra.length() > maxll) + extra = extra.left(maxll - 3) + "..."; + } + + return QString("%1%2 - %3").arg(title).arg(extra).arg(timedate); +} + +static int CheckRecordings(const MythUtilCommandLineParser &cmdline) +{ + cout << "Checking Recordings" << endl; + + ProgramInfo *p; + std::vector *recordingList = RemoteGetRecordedList(-1); + int recordingCount = 0; + int missingFileCount = 0; + int zeroByteCount = 0; + int missingSeetableCount = 0; + bool foundFile = false; + bool fixSeektable = cmdline.toBool("fixseektable"); + + if (recordingList && !recordingList->empty()) + { + vector::iterator i = recordingList->begin(); + for ( ; i != recordingList->end(); ++i) + { + p = *i; + // ignore live tv and deleted recordings + if (p->GetRecordingGroup() == "LiveTV" || + p->GetRecordingGroup() == "Deleted") + { + i = recordingList->erase(i); + --i; + continue; + } + + cout << "Checking: " << qPrintable(CreateProgramInfoString(*p)) << endl; + recordingCount++; + foundFile = true; + + QString url = p->GetPlaybackURL(); + + if (url.startsWith('/')) + { + QFileInfo fi(url); + if (!fi.exists()) + { + cout << "File not found" << endl; + missingFileCount++; + foundFile = false; + } + else + { + if (fi.size() == 0) + { + cout << "File was found but has zero length" << endl; + zeroByteCount++; + } + } + } + else if (url.startsWith("myth:")) + { + if (!RemoteFile::Exists(url)) + { + cout << "File not found" << endl; + missingFileCount++; + foundFile = false; + } + else + { + RemoteFile rf(url); + if (rf.GetFileSize() == 0) + { + cout << "File was found but has zero length" << endl; + zeroByteCount++; + } + } + } + + frm_pos_map_t posMap; + p->QueryPositionMap(posMap, MARK_GOP_BYFRAME); + if (posMap.isEmpty()) + p->QueryPositionMap(posMap, MARK_GOP_START); + if (posMap.isEmpty()) + p->QueryPositionMap(posMap, MARK_KEYFRAME); + + if (posMap.isEmpty()) + { + cout << "No seektable found" << endl; + + missingSeetableCount++; + + if (foundFile && fixSeektable) + { + QString command = QString("mythcommflag --rebuild --chanid %1 --starttime %2") + .arg(p->GetChanID()) + .arg(p->GetRecordingStartTime(MythDate::ISODate)); + cout << "Running - " << qPrintable(command) << endl; + QScopedPointer cmd(MythSystem::Create(command)); + cmd->Wait(0); + } + } + + cout << "-------------------------------------------------------------------" << endl; + } + } + + cout << endl << endl << "SUMMARY" << endl; + cout << "Recordings : " << recordingCount << endl; + cout << "Missing Recordings : " << missingFileCount << endl; + cout << "Zero byte Recordings : " << zeroByteCount << endl; + cout << "Missing Seektables : " << missingSeetableCount << endl; + + return GENERIC_EXIT_OK; +} + +void registerRecordingUtils(UtilMap &utilMap) +{ + utilMap["checkrecordings"] = &CheckRecordings; +} + +/* vim: set expandtab tabstop=4 shiftwidth=4: */ diff --git a/mythtv/programs/mythutil/recordingutils.h b/mythtv/programs/mythutil/recordingutils.h new file mode 100644 index 00000000000..ab0a80b8d6f --- /dev/null +++ b/mythtv/programs/mythutil/recordingutils.h @@ -0,0 +1,4 @@ +#include "mythutil.h" + +void registerRecordingUtils(UtilMap &utilMap); +