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  {
77  ShowOkPopup(QCoreApplication::translate("(ArchiveUtils)",
78  "Cannot find the MythArchive work directory.\n"
79  "Have you set the correct path in the settings?"));
80  }
81 
82  if (tempDir == "")
83  return "";
84 
85  // make sure the temp directory setting ends with a trailing "/"
86  if (!tempDir.endsWith("/"))
87  {
88  tempDir += "/";
89  gCoreContext->SaveSetting("MythArchiveTempDir", tempDir);
90  }
91 
92  return tempDir;
93 }
94 
96 {
97  QString tempDir = getTempDirectory();
98  QString logDir = tempDir + "logs";
99  QString configDir = tempDir + "config";
100  QString workDir = tempDir + "work";
101 
102  // make sure the 'work', 'logs', and 'config' directories exist
103  QDir dir(tempDir);
104  if (!dir.exists())
105  {
106  dir.mkdir(tempDir);
107  if( chmod(qPrintable(tempDir), 0777) != 0 )
108  LOG(VB_GENERAL, LOG_ERR,
109  "Failed to change permissions on archive directory: " + ENO);
110  }
111 
112  dir = QDir(workDir);
113  if (!dir.exists())
114  {
115  dir.mkdir(workDir);
116  if( chmod(qPrintable(workDir), 0777) != 0 )
117  {
118  LOG(VB_GENERAL, LOG_ERR,
119  "Failed to change permissions on archive work directory: " +
120  ENO);
121  }
122  }
123 
124  dir = QDir(logDir);
125  if (!dir.exists())
126  {
127  dir.mkdir(logDir);
128  if( chmod(qPrintable(logDir), 0777) != 0 )
129  {
130  LOG(VB_GENERAL, LOG_ERR,
131  "Failed to change permissions on archive log directory: " +
132  ENO);
133  }
134  }
135  dir = QDir(configDir);
136  if (!dir.exists())
137  {
138  dir.mkdir(configDir);
139  if( chmod(qPrintable(configDir), 0777) != 0 )
140  {
141  LOG(VB_GENERAL, LOG_ERR,
142  "Failed to change permissions on archive config directory: " +
143  ENO);
144  }
145  }
146 }
147 
148 QString getBaseName(const QString &filename)
149 {
150  QString baseName = filename;
151  int pos = filename.lastIndexOf('/');
152  if (pos > 0)
153  baseName = filename.mid(pos + 1);
154 
155  return baseName;
156 }
157 
158 bool extractDetailsFromFilename(const QString &inFile,
159  QString &chanID, QString &startTime)
160 {
161  LOG(VB_JOBQUEUE, LOG_INFO, "Extracting details from: " + inFile);
162 
163  QString baseName = getBaseName(inFile);
164 
166  query.prepare("SELECT chanid, starttime FROM recorded "
167  "WHERE basename = :BASENAME");
168  query.bindValue(":BASENAME", baseName);
169 
170  if (query.exec() && query.next())
171  {
172  chanID = query.value(0).toString();
173  startTime= query.value(1).toString();
174  }
175  else
176  {
177  LOG(VB_JOBQUEUE, LOG_ERR,
178  QString("Cannot find details for %1").arg(inFile));
179  return false;
180  }
181 
182  LOG(VB_JOBQUEUE, LOG_INFO,
183  QString("chanid: %1 starttime:%2 ").arg(chanID).arg(startTime));
184 
185  return true;
186 }
187 
188 ProgramInfo *getProgramInfoForFile(const QString &inFile)
189 {
190  ProgramInfo *pinfo = nullptr;
191  QString chanID;
192  QString startTime;
193 
194  bool bIsMythRecording = extractDetailsFromFilename(inFile, chanID, startTime);
195 
196  if (bIsMythRecording)
197  {
198  uint chanid = chanID.toUInt();
199  QDateTime recstartts = MythDate::fromString(startTime);
200  pinfo = new ProgramInfo(chanid, recstartts);
201  if (pinfo->GetChanID())
202  {
203  pinfo->SetPathname(pinfo->GetPlaybackURL(false, true));
204  }
205  else
206  {
207  delete pinfo;
208  pinfo = nullptr;
209  }
210  }
211 
212  if (!pinfo)
213  {
214  // file is not a myth recording or is no longer in the db
215  pinfo = new ProgramInfo(inFile);
216  LOG(VB_JOBQUEUE, LOG_NOTICE, "File is not a MythTV recording.");
217  }
218  else
219  LOG(VB_JOBQUEUE, LOG_NOTICE, "File is a MythTV recording.");
220 
221  return pinfo;
222 }
223 
225 {
226  QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
227 
228  if (!tempDir.endsWith("/"))
229  tempDir += "/";
230 
231  QString inFile;
232  int lenMethod = 0;
233  if (a->type == "Recording")
234  {
235  inFile = a->filename;
236  lenMethod = 2;
237  }
238  else
239  {
240  inFile = a->filename;
241  }
242 
243  inFile.replace("\'", "\\\'");
244  inFile.replace("\"", "\\\"");
245  inFile.replace("`", "\\`");
246 
247  QString outFile = tempDir + "work/file.xml";
248 
249  // call mytharchivehelper to get files stream info etc.
250  QString command = QString("mytharchivehelper --getfileinfo --infile \"%1\" "
251  "--outfile \"%2\" --method %3")
252  .arg(inFile).arg(outFile).arg(lenMethod);
253  command += logPropagateArgs;
254  if (!logPropagateQuiet())
255  command += " --quiet";
256 
258  if (myth_system(command, flags) != GENERIC_EXIT_OK)
259  return false;
260 
261  QDomDocument doc("mydocument");
262  QFile file(outFile);
263  if (!file.open(QIODevice::ReadOnly))
264  return false;
265 
266  if (!doc.setContent( &file ))
267  {
268  file.close();
269  return false;
270  }
271  file.close();
272 
273  // get file type and duration
274  QDomElement docElem = doc.documentElement();
275  QDomNodeList nodeList = doc.elementsByTagName("file");
276  if (nodeList.count() < 1)
277  return false;
278  QDomNode n = nodeList.item(0);
279  QDomElement e = n.toElement();
280  a->fileCodec = e.attribute("type");
281  a->duration = e.attribute("duration").toInt();
282  a->cutDuration = e.attribute("cutduration").toInt();
283 
284  // get frame size and video codec
285  nodeList = doc.elementsByTagName("video");
286  if (nodeList.count() < 1)
287  return false;
288  n = nodeList.item(0);
289  e = n.toElement();
290  a->videoCodec = e.attribute("codec");
291  a->videoWidth = e.attribute("width").toInt();
292  a->videoHeight = e.attribute("height").toInt();
293 
294  return true;
295 }
296 
297 void showWarningDialog(const QString &msg)
298 {
299  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
300  auto *dialog = new MythConfirmationDialog(popupStack, msg, false);
301 
302  if (dialog->Create())
303  popupStack->AddScreen(dialog);
304 }
305 
307 {
309  if (!profile)
310  return;
311 
312  if (profile->name == "NONE")
313  {
314  if (item->hasCutlist && item->useCutlist)
315  {
316  item->newsize = (int64_t) (item->size /
317  ((float)item->duration / (float)item->cutDuration));
318  }
319  else
320  {
321  item->newsize = item->size;
322  }
323  }
324  else
325  {
326  if (item->duration == 0)
327  return;
328 
329  int length = 0;
330  if (item->hasCutlist && item->useCutlist)
331  length = item->cutDuration;
332  else
333  length = item->duration;
334 
335  float len = (float) length / 3600;
336  item->newsize = (int64_t) (len * profile->bitrate * 1024 * 1024);
337  }
338 }
339 
340 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
int ArchiveDestinationsCount
Definition: archiveutil.cpp:48
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
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:70
void SaveSetting(const QString &key, int newValue)
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
bool useCutlist
Definition: archiveutil.h:71
QString logPropagateArgs
Definition: logging.cpp:89
QDomDocument doc("MYTHARCHIVEITEM")
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:67
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MSqlQuery query(MSqlQuery::InitCon())
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:695
int cutDuration
Definition: archiveutil.h:64
unsigned int uint
Definition: compat.h:140
void recalcItemSize(ArchiveItem *item)
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
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:69
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:808
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:366
EncoderProfile * encoderProfile
Definition: archiveutil.h:65
int videoHeight
Definition: archiveutil.h:69
void checkTempDirectory()
Definition: archiveutil.cpp:95
int64_t size
Definition: archiveutil.h:61
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
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.
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:34
void SetPathname(const QString &pn)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23