00001
00002 #include <sys/stat.h>
00003 #include <unistd.h>
00004 #include <errno.h>
00005
00006
00007 #include <cstdlib>
00008
00009
00010 #include <iostream>
00011 using namespace std;
00012
00013
00014 #include <QDomDocument>
00015 #include <QDir>
00016
00017
00018 #include <mythcontext.h>
00019 #include <programinfo.h>
00020 #include <mythmainwindow.h>
00021 #include <mythdialogbox.h>
00022 #include <mythmiscutil.h>
00023 #include <mythsystem.h>
00024 #include <exitcodes.h>
00025 #include <mythlogging.h>
00026
00027
00028 #include "archiveutil.h"
00029
00030
00031 struct ArchiveDestination ArchiveDestinations[] =
00032 {
00033 {AD_DVD_SL,
00034 QT_TRANSLATE_NOOP("SelectDestination", "Single Layer DVD"),
00035 QT_TRANSLATE_NOOP("SelectDestination", "Single Layer DVD (4,482 MB)"),
00036 4482*1024},
00037 {AD_DVD_DL,
00038 QT_TRANSLATE_NOOP("SelectDestination", "Dual Layer DVD"),
00039 QT_TRANSLATE_NOOP("SelectDestination", "Dual Layer DVD (8,964 MB)"),
00040 8964*1024},
00041 {AD_DVD_RW,
00042 QT_TRANSLATE_NOOP("SelectDestination", "DVD +/- RW"),
00043 QT_TRANSLATE_NOOP("SelectDestination", "Rewritable DVD"),
00044 4482*1024},
00045 {AD_FILE,
00046 QT_TRANSLATE_NOOP("SelectDestination", "File"),
00047 QT_TRANSLATE_NOOP("SelectDestination", "Any file accessable from your filesystem."),
00048 -1},
00049 };
00050
00051 int ArchiveDestinationsCount = sizeof(ArchiveDestinations) / sizeof(ArchiveDestinations[0]);
00052
00053 QString formatSize(int64_t sizeKB, int prec)
00054 {
00055 if (sizeKB>1024*1024*1024)
00056 {
00057 double sizeGB = sizeKB/(1024*1024*1024.0);
00058 return QString("%1 TB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec);
00059 }
00060 else if (sizeKB>1024*1024)
00061 {
00062 double sizeGB = sizeKB/(1024*1024.0);
00063 return QString("%1 GB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec);
00064 }
00065 else if (sizeKB>1024)
00066 {
00067 double sizeMB = sizeKB/1024.0;
00068 return QString("%1 MB").arg(sizeMB, 0, 'f', (sizeMB>10)?0:prec);
00069 }
00070
00071 return QString("%1 KB").arg(sizeKB);
00072 }
00073
00074 QString getTempDirectory(bool showError)
00075 {
00076 QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
00077
00078 if (tempDir == "" && showError)
00079 ShowOkPopup(QObject::tr("Cannot find the MythArchive work directory.\n"
00080 "Have you set the correct path in the settings?"),
00081 NULL, NULL);
00082
00083 if (tempDir == "")
00084 return "";
00085
00086
00087 if (!tempDir.endsWith("/"))
00088 {
00089 tempDir += "/";
00090 gCoreContext->SaveSetting("MythArchiveTempDir", tempDir);
00091 }
00092
00093 return tempDir;
00094 }
00095
00096 void checkTempDirectory()
00097 {
00098 QString tempDir = getTempDirectory();
00099 QString logDir = tempDir + "logs";
00100 QString configDir = tempDir + "config";
00101 QString workDir = tempDir + "work";
00102
00103
00104 QDir dir(tempDir);
00105 if (!dir.exists())
00106 {
00107 dir.mkdir(tempDir);
00108 if( chmod(qPrintable(tempDir), 0777) )
00109 LOG(VB_GENERAL, LOG_ERR,
00110 "Failed to change permissions on archive directory: " + ENO);
00111 }
00112
00113 dir = QDir(workDir);
00114 if (!dir.exists())
00115 {
00116 dir.mkdir(workDir);
00117 if( chmod(qPrintable(workDir), 0777) )
00118 LOG(VB_GENERAL, LOG_ERR,
00119 "Failed to change permissions on archive work directory: " +
00120 ENO);
00121 }
00122
00123 dir = QDir(logDir);
00124 if (!dir.exists())
00125 {
00126 dir.mkdir(logDir);
00127 if( chmod(qPrintable(logDir), 0777) )
00128 LOG(VB_GENERAL, LOG_ERR,
00129 "Failed to change permissions on archive log directory: " +
00130 ENO);
00131 }
00132 dir = QDir(configDir);
00133 if (!dir.exists())
00134 {
00135 dir.mkdir(configDir);
00136 if( chmod(qPrintable(configDir), 0777) )
00137 LOG(VB_GENERAL, LOG_ERR,
00138 "Failed to change permissions on archive config directory: " +
00139 ENO);
00140 }
00141 }
00142
00143 QString getBaseName(const QString &filename)
00144 {
00145 QString baseName = filename;
00146 int pos = filename.lastIndexOf('/');
00147 if (pos > 0)
00148 baseName = filename.mid(pos + 1);
00149
00150 return baseName;
00151 }
00152
00153 bool extractDetailsFromFilename(const QString &inFile,
00154 QString &chanID, QString &startTime)
00155 {
00156 LOG(VB_JOBQUEUE, LOG_INFO, "Extracting details from: " + inFile);
00157
00158 QString baseName = getBaseName(inFile);
00159
00160 MSqlQuery query(MSqlQuery::InitCon());
00161 query.prepare("SELECT chanid, starttime FROM recorded "
00162 "WHERE basename = :BASENAME");
00163 query.bindValue(":BASENAME", baseName);
00164
00165 if (query.exec() && query.next())
00166 {
00167 chanID = query.value(0).toString();
00168 startTime= query.value(1).toString();
00169 }
00170 else
00171 {
00172 LOG(VB_JOBQUEUE, LOG_ERR,
00173 QString("Cannot find details for %1").arg(inFile));
00174 return false;
00175 }
00176
00177 LOG(VB_JOBQUEUE, LOG_INFO,
00178 QString("chanid: %1 starttime:%2 ").arg(chanID).arg(startTime));
00179
00180 return true;
00181 }
00182
00183 ProgramInfo *getProgramInfoForFile(const QString &inFile)
00184 {
00185 ProgramInfo *pinfo = NULL;
00186 QString chanID, startTime;
00187 bool bIsMythRecording = false;
00188
00189 bIsMythRecording = extractDetailsFromFilename(inFile, chanID, startTime);
00190
00191 if (bIsMythRecording)
00192 {
00193 uint chanid = chanID.toUInt();
00194 QDateTime recstartts = myth_dt_from_string(startTime);
00195 pinfo = new ProgramInfo(chanid, recstartts);
00196 if (pinfo->GetChanID())
00197 {
00198 pinfo->SetPathname(pinfo->GetPlaybackURL(false, true));
00199 }
00200 else
00201 {
00202 delete pinfo;
00203 pinfo = NULL;
00204 }
00205 }
00206
00207 if (!pinfo)
00208 {
00209
00210 pinfo = new ProgramInfo(inFile);
00211 LOG(VB_JOBQUEUE, LOG_NOTICE, "File is not a MythTV recording.");
00212 }
00213 else
00214 LOG(VB_JOBQUEUE, LOG_NOTICE, "File is a MythTV recording.");
00215
00216 return pinfo;
00217 }
00218
00219 bool getFileDetails(ArchiveItem *a)
00220 {
00221 QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
00222
00223 if (!tempDir.endsWith("/"))
00224 tempDir += "/";
00225
00226 QString inFile;
00227 int lenMethod = 0;
00228 if (a->type == "Recording")
00229 {
00230 inFile = a->filename;
00231 lenMethod = 2;
00232 }
00233 else
00234 {
00235 inFile = a->filename;
00236 }
00237
00238 inFile.replace("\'", "\\\'");
00239 inFile.replace("\"", "\\\"");
00240 inFile.replace("`", "\\`");
00241
00242 QString outFile = tempDir + "work/file.xml";
00243
00244
00245 QString command = QString("mytharchivehelper --getfileinfo --infile \"%1\" "
00246 "--outfile \"%2\" --method %3")
00247 .arg(inFile).arg(outFile).arg(lenMethod);
00248 command += logPropagateArgs;
00249 if (!logPropagateQuiet())
00250 command += " --quiet";
00251
00252 uint flags = kMSDontBlockInputDevs | kMSDontDisableDrawing;
00253 if (myth_system(command, flags) != GENERIC_EXIT_OK)
00254 return false;
00255
00256 QDomDocument doc("mydocument");
00257 QFile file(outFile);
00258 if (!file.open(QIODevice::ReadOnly))
00259 return false;
00260
00261 if (!doc.setContent( &file ))
00262 {
00263 file.close();
00264 return false;
00265 }
00266 file.close();
00267
00268
00269 QDomElement docElem = doc.documentElement();
00270 QDomNodeList nodeList = doc.elementsByTagName("file");
00271 if (nodeList.count() < 1)
00272 return false;
00273 QDomNode n = nodeList.item(0);
00274 QDomElement e = n.toElement();
00275 a->fileCodec = e.attribute("type");
00276 a->duration = e.attribute("duration").toInt();
00277 a->cutDuration = e.attribute("cutduration").toInt();
00278
00279
00280 nodeList = doc.elementsByTagName("video");
00281 if (nodeList.count() < 1)
00282 return false;
00283 n = nodeList.item(0);
00284 e = n.toElement();
00285 a->videoCodec = e.attribute("codec");
00286 a->videoWidth = e.attribute("width").toInt();
00287 a->videoHeight = e.attribute("height").toInt();
00288
00289 return true;
00290 }
00291
00292 void showWarningDialog(const QString msg)
00293 {
00294 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00295 MythConfirmationDialog *dialog = new MythConfirmationDialog(popupStack, msg, false);
00296
00297 if (dialog->Create())
00298 popupStack->AddScreen(dialog);
00299 }
00300
00301 void recalcItemSize(ArchiveItem *item)
00302 {
00303 EncoderProfile *profile = item->encoderProfile;
00304 if (!profile)
00305 return;
00306
00307 if (profile->name == "NONE")
00308 {
00309 if (item->hasCutlist && item->useCutlist)
00310 item->newsize = (int64_t) (item->size /
00311 ((float)item->duration / (float)item->cutDuration));
00312 else
00313 item->newsize = item->size;
00314 }
00315 else
00316 {
00317 if (item->duration == 0)
00318 return;
00319
00320 int length;
00321
00322 if (item->hasCutlist && item->useCutlist)
00323 length = item->cutDuration;
00324 else
00325 length = item->duration;
00326
00327 float len = (float) length / 3600;
00328 item->newsize = (int64_t) (len * profile->bitrate * 1024 * 1024);
00329 }
00330 }
00331
00332