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  LOG(VB_JOBQUEUE, LOG_NOTICE, "File is a MythTV recording.");
195 
196  return pinfo;
197 }
198 
200 {
201  QString tempDir = gCoreContext->GetSetting("MythArchiveTempDir", "");
202 
203  if (!tempDir.endsWith("/"))
204  tempDir += "/";
205 
206  QString inFile;
207  int lenMethod = 0;
208  if (a->type == "Recording")
209  {
210  inFile = a->filename;
211  lenMethod = 2;
212  }
213  else
214  {
215  inFile = a->filename;
216  }
217 
218  inFile.replace("\'", "\\\'");
219  inFile.replace("\"", "\\\"");
220  inFile.replace("`", "\\`");
221 
222  QString outFile = tempDir + "work/file.xml";
223 
224  // call mytharchivehelper to get files stream info etc.
225  QString command = QString("mytharchivehelper --getfileinfo --infile \"%1\" "
226  "--outfile \"%2\" --method %3")
227  .arg(inFile, outFile, QString::number(lenMethod));
228  command += logPropagateArgs;
229  if (!logPropagateQuiet())
230  command += " --quiet";
231 
233  if (myth_system(command, flags) != GENERIC_EXIT_OK)
234  return false;
235 
236  QDomDocument doc("mydocument");
237  QFile file(outFile);
238  if (!file.open(QIODevice::ReadOnly))
239  return false;
240 
241  if (!doc.setContent( &file ))
242  {
243  file.close();
244  return false;
245  }
246  file.close();
247 
248  // get file type and duration
249  QDomElement docElem = doc.documentElement();
250  QDomNodeList nodeList = doc.elementsByTagName("file");
251  if (nodeList.count() < 1)
252  return false;
253  QDomNode n = nodeList.item(0);
254  QDomElement e = n.toElement();
255  a->fileCodec = e.attribute("type");
256  a->duration = e.attribute("duration").toInt();
257  a->cutDuration = e.attribute("cutduration").toInt();
258 
259  // get frame size and video codec
260  nodeList = doc.elementsByTagName("video");
261  if (nodeList.count() < 1)
262  return false;
263  n = nodeList.item(0);
264  e = n.toElement();
265  a->videoCodec = e.attribute("codec");
266  a->videoWidth = e.attribute("width").toInt();
267  a->videoHeight = e.attribute("height").toInt();
268 
269  return true;
270 }
271 
272 void showWarningDialog(const QString &msg)
273 {
274  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
275  auto *dialog = new MythConfirmationDialog(popupStack, msg, false);
276 
277  if (dialog->Create())
278  popupStack->AddScreen(dialog);
279 }
280 
282 {
284  if (!profile)
285  return;
286 
287  if (profile->name == "NONE")
288  {
289  if (item->hasCutlist && item->useCutlist)
290  {
291  item->newsize = (int64_t) (item->size /
292  ((float)item->duration / (float)item->cutDuration));
293  }
294  else
295  {
296  item->newsize = item->size;
297  }
298  }
299  else
300  {
301  if (item->duration == 0)
302  return;
303 
304  int length = 0;
305  if (item->hasCutlist && item->useCutlist)
306  length = item->cutDuration;
307  else
308  length = item->duration;
309 
310  float len = (float) length / 3600;
311  item->newsize = (int64_t) (len * profile->bitrate * 1024 * 1024);
312  }
313 }
314 
315 /* 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:807
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
ArchiveItem::useCutlist
bool useCutlist
Definition: archiveutil.h:70
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:73
kMSDontBlockInputDevs
@ kMSDontBlockInputDevs
avoid blocking LIRC & Joystick Menu
Definition: mythsystem.h:36
EncoderProfile
Definition: archiveutil.h:36
AD_DVD_SL
@ AD_DVD_SL
Definition: archiveutil.h:18
ArchiveItem::newsize
int64_t newsize
Definition: archiveutil.h:61
mythdialogbox.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
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:608
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
GENERIC_EXIT_OK
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:11
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:272
ArchiveItem::type
QString type
Definition: archiveutil.h:53
mythdate.h
ProgramInfo::SetPathname
void SetPathname(const QString &pn)
Definition: programinfo.cpp:2438
programinfo.h
mythlogging.h
ArchiveItem::size
int64_t size
Definition: archiveutil.h:60
hardwareprofile.scan.profile
profile
Definition: scan.py:99
AD_DVD_RW
@ AD_DVD_RW
Definition: archiveutil.h:20
archiveutil.h
getFileDetails
bool getFileDetails(ArchiveItem *a)
Definition: archiveutil.cpp:199
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:540
checkTempDirectory
void checkTempDirectory()
Definition: archiveutil.cpp:70
uint
unsigned int uint
Definition: compat.h:79
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
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:34
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:372
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:2546
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:883
ArchiveDestinations
std::vector< ArchiveDestination > ArchiveDestinations
Definition: archiveutil.cpp:27
mythcontext.h
AD_DVD_DL
@ AD_DVD_DL
Definition: archiveutil.h:19
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
AD_FILE
@ AD_FILE
Definition: archiveutil.h:21
ArchiveItem::hasCutlist
bool hasCutlist
Definition: archiveutil.h:69
recalcItemSize
void recalcItemSize(ArchiveItem *item)
Definition: archiveutil.cpp:281
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:320
logPropagateQuiet
bool logPropagateQuiet(void)
Check if we are propagating a "--quiet".
Definition: logging.cpp:663
logPropagateArgs
QString logPropagateArgs
Definition: logging.cpp:86
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:879
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:50
ShowOkPopup
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
Definition: mythdialogbox.cpp:563
ArchiveItem::filename
QString filename
Definition: archiveutil.h:59
extractDetailsFromFilename
bool extractDetailsFromFilename(const QString &inFile, QString &chanID, QString &startTime)
Definition: archiveutil.cpp:133
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:896
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:832