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