MythTV  master
archiveutil.cpp
Go to the documentation of this file.
1 // C++ headers
2 #include <cerrno>
3 #include <cstdlib>
4 #include <iostream>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 using namespace std;
8 
9 // qt
10 #include <QDomDocument>
11 #include <QDir>
12 #include <QCoreApplication>
13 
14 // myth
15 #include <mythcontext.h>
16 #include <programinfo.h>
17 #include <mythmainwindow.h>
18 #include <mythdialogbox.h>
19 #include <mythdate.h>
20 #include <mythsystemlegacy.h>
21 #include <exitcodes.h>
22 #include <mythlogging.h>
23 
24 // mytharchive
25 #include "archiveutil.h"
26 
27 
29 {
30  {AD_DVD_SL,
31  QT_TRANSLATE_NOOP("SelectDestination", "Single Layer DVD"),
32  QT_TRANSLATE_NOOP("SelectDestination", "Single Layer DVD (4,482 MB)"),
33  4482*1024},
34  {AD_DVD_DL,
35  QT_TRANSLATE_NOOP("SelectDestination", "Dual Layer DVD"),
36  QT_TRANSLATE_NOOP("SelectDestination", "Dual Layer DVD (8,964 MB)"),
37  8964*1024},
38  {AD_DVD_RW,
39  QT_TRANSLATE_NOOP("SelectDestination", "DVD +/- RW"),
40  QT_TRANSLATE_NOOP("SelectDestination", "Rewritable DVD"),
41  4482*1024},
42  {AD_FILE,
43  QT_TRANSLATE_NOOP("SelectDestination", "File"),
44  QT_TRANSLATE_NOOP("SelectDestination", "Any file accessable from your filesystem."),
45  -1},
46 };
47 
49 
50 QString formatSize(int64_t sizeKB, int prec)
51 {
52  if (sizeKB>1024*1024*1024) // Terabytes
53  {
54  double sizeGB = sizeKB/(1024*1024*1024.0);
55  return QString("%1 TB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec);
56  }
57  if (sizeKB>1024*1024) // Gigabytes
58  {
59  double sizeGB = sizeKB/(1024*1024.0);
60  return QString("%1 GB").arg(sizeGB, 0, 'f', (sizeGB>10)?0:prec);
61  }
62  if (sizeKB>1024) // Megabytes
63  {
64  double sizeMB = sizeKB/1024.0;
65  return QString("%1 MB").arg(sizeMB, 0, 'f', (sizeMB>10)?0:prec);
66  }
67  // Kilobytes
68  return QString("%1 KB").arg(sizeKB);
69 }
70 
71 QString getTempDirectory(bool showError)
72 {
73  QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
74 
75  if (tempDir == "" && showError)
76  ShowOkPopup(QCoreApplication::translate("(ArchiveUtils)",
77  "Cannot find the MythArchive work directory.\n"
78  "Have you set the correct path in the settings?"));
79 
80  if (tempDir == "")
81  return "";
82 
83  // make sure the temp directory setting ends with a trailing "/"
84  if (!tempDir.endsWith("/"))
85  {
86  tempDir += "/";
87  gCoreContext->SaveSetting("MythArchiveTempDir", tempDir);
88  }
89 
90  return tempDir;
91 }
92 
94 {
95  QString tempDir = getTempDirectory();
96  QString logDir = tempDir + "logs";
97  QString configDir = tempDir + "config";
98  QString workDir = tempDir + "work";
99 
100  // make sure the 'work', 'logs', and 'config' directories exist
101  QDir dir(tempDir);
102  if (!dir.exists())
103  {
104  dir.mkdir(tempDir);
105  if( chmod(qPrintable(tempDir), 0777) != 0 )
106  LOG(VB_GENERAL, LOG_ERR,
107  "Failed to change permissions on archive directory: " + ENO);
108  }
109 
110  dir = QDir(workDir);
111  if (!dir.exists())
112  {
113  dir.mkdir(workDir);
114  if( chmod(qPrintable(workDir), 0777) != 0 )
115  LOG(VB_GENERAL, LOG_ERR,
116  "Failed to change permissions on archive work directory: " +
117  ENO);
118  }
119 
120  dir = QDir(logDir);
121  if (!dir.exists())
122  {
123  dir.mkdir(logDir);
124  if( chmod(qPrintable(logDir), 0777) != 0 )
125  LOG(VB_GENERAL, LOG_ERR,
126  "Failed to change permissions on archive log directory: " +
127  ENO);
128  }
129  dir = QDir(configDir);
130  if (!dir.exists())
131  {
132  dir.mkdir(configDir);
133  if( chmod(qPrintable(configDir), 0777) != 0 )
134  LOG(VB_GENERAL, LOG_ERR,
135  "Failed to change permissions on archive config directory: " +
136  ENO);
137  }
138 }
139 
140 QString getBaseName(const QString &filename)
141 {
142  QString baseName = filename;
143  int pos = filename.lastIndexOf('/');
144  if (pos > 0)
145  baseName = filename.mid(pos + 1);
146 
147  return baseName;
148 }
149 
150 bool extractDetailsFromFilename(const QString &inFile,
151  QString &chanID, QString &startTime)
152 {
153  LOG(VB_JOBQUEUE, LOG_INFO, "Extracting details from: " + inFile);
154 
155  QString baseName = getBaseName(inFile);
156 
157  MSqlQuery query(MSqlQuery::InitCon());
158  query.prepare("SELECT chanid, starttime FROM recorded "
159  "WHERE basename = :BASENAME");
160  query.bindValue(":BASENAME", baseName);
161 
162  if (query.exec() && query.next())
163  {
164  chanID = query.value(0).toString();
165  startTime= query.value(1).toString();
166  }
167  else
168  {
169  LOG(VB_JOBQUEUE, LOG_ERR,
170  QString("Cannot find details for %1").arg(inFile));
171  return false;
172  }
173 
174  LOG(VB_JOBQUEUE, LOG_INFO,
175  QString("chanid: %1 starttime:%2 ").arg(chanID).arg(startTime));
176 
177  return true;
178 }
179 
180 ProgramInfo *getProgramInfoForFile(const QString &inFile)
181 {
182  ProgramInfo *pinfo = nullptr;
183  QString chanID, startTime;
184 
185  bool bIsMythRecording = extractDetailsFromFilename(inFile, chanID, startTime);
186 
187  if (bIsMythRecording)
188  {
189  uint chanid = chanID.toUInt();
190  QDateTime recstartts = MythDate::fromString(startTime);
191  pinfo = new ProgramInfo(chanid, recstartts);
192  if (pinfo->GetChanID())
193  {
194  pinfo->SetPathname(pinfo->GetPlaybackURL(false, true));
195  }
196  else
197  {
198  delete pinfo;
199  pinfo = nullptr;
200  }
201  }
202 
203  if (!pinfo)
204  {
205  // file is not a myth recording or is no longer in the db
206  pinfo = new ProgramInfo(inFile);
207  LOG(VB_JOBQUEUE, LOG_NOTICE, "File is not a MythTV recording.");
208  }
209  else
210  LOG(VB_JOBQUEUE, LOG_NOTICE, "File is a MythTV recording.");
211 
212  return pinfo;
213 }
214 
216 {
217  QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
218 
219  if (!tempDir.endsWith("/"))
220  tempDir += "/";
221 
222  QString inFile;
223  int lenMethod = 0;
224  if (a->type == "Recording")
225  {
226  inFile = a->filename;
227  lenMethod = 2;
228  }
229  else
230  {
231  inFile = a->filename;
232  }
233 
234  inFile.replace("\'", "\\\'");
235  inFile.replace("\"", "\\\"");
236  inFile.replace("`", "\\`");
237 
238  QString outFile = tempDir + "work/file.xml";
239 
240  // call mytharchivehelper to get files stream info etc.
241  QString command = QString("mytharchivehelper --getfileinfo --infile \"%1\" "
242  "--outfile \"%2\" --method %3")
243  .arg(inFile).arg(outFile).arg(lenMethod);
244  command += logPropagateArgs;
245  if (!logPropagateQuiet())
246  command += " --quiet";
247 
249  if (myth_system(command, flags) != GENERIC_EXIT_OK)
250  return false;
251 
252  QDomDocument doc("mydocument");
253  QFile file(outFile);
254  if (!file.open(QIODevice::ReadOnly))
255  return false;
256 
257  if (!doc.setContent( &file ))
258  {
259  file.close();
260  return false;
261  }
262  file.close();
263 
264  // get file type and duration
265  QDomElement docElem = doc.documentElement();
266  QDomNodeList nodeList = doc.elementsByTagName("file");
267  if (nodeList.count() < 1)
268  return false;
269  QDomNode n = nodeList.item(0);
270  QDomElement e = n.toElement();
271  a->fileCodec = e.attribute("type");
272  a->duration = e.attribute("duration").toInt();
273  a->cutDuration = e.attribute("cutduration").toInt();
274 
275  // get frame size and video codec
276  nodeList = doc.elementsByTagName("video");
277  if (nodeList.count() < 1)
278  return false;
279  n = nodeList.item(0);
280  e = n.toElement();
281  a->videoCodec = e.attribute("codec");
282  a->videoWidth = e.attribute("width").toInt();
283  a->videoHeight = e.attribute("height").toInt();
284 
285  return true;
286 }
287 
288 void showWarningDialog(const QString &msg)
289 {
290  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
291  auto *dialog = new MythConfirmationDialog(popupStack, msg, false);
292 
293  if (dialog->Create())
294  popupStack->AddScreen(dialog);
295 }
296 
298 {
300  if (!profile)
301  return;
302 
303  if (profile->name == "NONE")
304  {
305  if (item->hasCutlist && item->useCutlist)
306  item->newsize = (int64_t) (item->size /
307  ((float)item->duration / (float)item->cutDuration));
308  else
309  item->newsize = item->size;
310  }
311  else
312  {
313  if (item->duration == 0)
314  return;
315 
316  int length = 0;
317  if (item->hasCutlist && item->useCutlist)
318  length = item->cutDuration;
319  else
320  length = item->duration;
321 
322  float len = (float) length / 3600;
323  item->newsize = (int64_t) (len * profile->bitrate * 1024 * 1024);
324  }
325 }
326 
327 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:781
int ArchiveDestinationsCount
Definition: archiveutil.cpp:48
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:862
Dialog asking for user confirmation.
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
bool extractDetailsFromFilename(const QString &inFile, QString &chanID, QString &startTime)
QString getTempDirectory(bool showError)
Definition: archiveutil.cpp:71
QString getBaseName(const QString &filename)
bool hasCutlist
Definition: archiveutil.h:69
void SaveSetting(const QString &key, int newValue)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
bool useCutlist
Definition: archiveutil.h:70
QString logPropagateArgs
Definition: logging.cpp:89
MythScreenStack * GetStack(const QString &stackname)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
int videoWidth
Definition: archiveutil.h:68
bool getFileDetails(ArchiveItem *a)
int64_t newsize
Definition: archiveutil.h:62
QString filename
Definition: archiveutil.h:60
QVariant value(int i) const
Definition: mythdbcon.h:198
Holds information on recordings and videos.
Definition: programinfo.h:66
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
QString GetSetting(const QString &key, const QString &defaultval="")
QString fileCodec
Definition: archiveutil.h:66
void showWarningDialog(const QString &msg)
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:700
int cutDuration
Definition: archiveutil.h:64
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
void recalcItemSize(ArchiveItem *item)
QString formatSize(int64_t sizeKB, int prec)
Definition: archiveutil.cpp:50
uint myth_system(const QString &command, uint flags, uint timeout)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
QString type
Definition: archiveutil.h:54
MythMainWindow * GetMythMainWindow(void)
QString videoCodec
Definition: archiveutil.h:67
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:806
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
struct ArchiveDestination ArchiveDestinations[]
Definition: archiveutil.cpp:28
avoid disabling UI drawing
Definition: mythsystem.h:35
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:364
EncoderProfile * encoderProfile
Definition: archiveutil.h:65
int videoHeight
Definition: archiveutil.h:68
void checkTempDirectory()
Definition: archiveutil.cpp:93
int64_t size
Definition: archiveutil.h:61
void SetPathname(const QString &)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
ProgramInfo * getProgramInfoForFile(const QString &inFile)
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false)
Returns filename or URL to be used to play back this recording.
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34