RemuxQueueModel.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /******************************************************************************
  2. Copyright (C) 2014 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "RemuxQueueModel.hpp"
  15. #include <OBSApp.hpp>
  16. #include <QDir>
  17. #include <QStyle>
  18. #include "moc_RemuxQueueModel.cpp"
  19. int RemuxQueueModel::rowCount(const QModelIndex &) const
  20. {
  21. return queue.length() + (isProcessing ? 0 : 1);
  22. }
  23. int RemuxQueueModel::columnCount(const QModelIndex &) const
  24. {
  25. return RemuxEntryColumn::Count;
  26. }
  27. QVariant RemuxQueueModel::data(const QModelIndex &index, int role) const
  28. {
  29. QVariant result = QVariant();
  30. if (index.row() >= queue.length()) {
  31. return QVariant();
  32. } else if (role == Qt::DisplayRole) {
  33. switch (index.column()) {
  34. case RemuxEntryColumn::InputPath:
  35. result = queue[index.row()].sourcePath;
  36. break;
  37. case RemuxEntryColumn::OutputPath:
  38. result = queue[index.row()].targetPath;
  39. break;
  40. }
  41. } else if (role == Qt::DecorationRole && index.column() == RemuxEntryColumn::State) {
  42. result = getIcon(queue[index.row()].state);
  43. } else if (role == RemuxEntryRole::EntryStateRole) {
  44. result = queue[index.row()].state;
  45. }
  46. return result;
  47. }
  48. QVariant RemuxQueueModel::headerData(int section, Qt::Orientation orientation, int role) const
  49. {
  50. QVariant result = QVariant();
  51. if (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal) {
  52. switch (section) {
  53. case RemuxEntryColumn::State:
  54. result = QString();
  55. break;
  56. case RemuxEntryColumn::InputPath:
  57. result = QTStr("Remux.SourceFile");
  58. break;
  59. case RemuxEntryColumn::OutputPath:
  60. result = QTStr("Remux.TargetFile");
  61. break;
  62. }
  63. }
  64. return result;
  65. }
  66. Qt::ItemFlags RemuxQueueModel::flags(const QModelIndex &index) const
  67. {
  68. Qt::ItemFlags flags = QAbstractTableModel::flags(index);
  69. if (index.column() == RemuxEntryColumn::InputPath) {
  70. flags |= Qt::ItemIsEditable;
  71. } else if (index.column() == RemuxEntryColumn::OutputPath && index.row() != queue.length()) {
  72. flags |= Qt::ItemIsEditable;
  73. }
  74. return flags;
  75. }
  76. bool RemuxQueueModel::setData(const QModelIndex &index, const QVariant &value, int role)
  77. {
  78. bool success = false;
  79. if (role == RemuxEntryRole::NewPathsToProcessRole) {
  80. QStringList pathList = value.toStringList();
  81. if (pathList.size() == 0) {
  82. if (index.row() < queue.size()) {
  83. beginRemoveRows(QModelIndex(), index.row(), index.row());
  84. queue.removeAt(index.row());
  85. endRemoveRows();
  86. }
  87. } else {
  88. if (pathList.size() >= 1 && index.row() < queue.length()) {
  89. queue[index.row()].sourcePath = pathList[0];
  90. checkInputPath(index.row());
  91. pathList.removeAt(0);
  92. success = true;
  93. }
  94. if (pathList.size() > 0) {
  95. int row = index.row();
  96. int lastRow = row + pathList.size() - 1;
  97. beginInsertRows(QModelIndex(), row, lastRow);
  98. for (QString path : pathList) {
  99. RemuxQueueEntry entry;
  100. entry.sourcePath = path;
  101. entry.state = RemuxEntryState::Empty;
  102. queue.insert(row, entry);
  103. row++;
  104. }
  105. endInsertRows();
  106. for (row = index.row(); row <= lastRow; row++) {
  107. checkInputPath(row);
  108. }
  109. success = true;
  110. }
  111. }
  112. } else if (index.row() == queue.length()) {
  113. QString path = value.toString();
  114. if (!path.isEmpty()) {
  115. RemuxQueueEntry entry;
  116. entry.sourcePath = path;
  117. entry.state = RemuxEntryState::Empty;
  118. beginInsertRows(QModelIndex(), queue.length() + 1, queue.length() + 1);
  119. queue.append(entry);
  120. endInsertRows();
  121. checkInputPath(index.row());
  122. success = true;
  123. }
  124. } else {
  125. QString path = value.toString();
  126. if (path.isEmpty()) {
  127. if (index.column() == RemuxEntryColumn::InputPath) {
  128. beginRemoveRows(QModelIndex(), index.row(), index.row());
  129. queue.removeAt(index.row());
  130. endRemoveRows();
  131. }
  132. } else {
  133. switch (index.column()) {
  134. case RemuxEntryColumn::InputPath:
  135. queue[index.row()].sourcePath = value.toString();
  136. checkInputPath(index.row());
  137. success = true;
  138. break;
  139. case RemuxEntryColumn::OutputPath:
  140. queue[index.row()].targetPath = value.toString();
  141. emit dataChanged(index, index);
  142. success = true;
  143. break;
  144. }
  145. }
  146. }
  147. return success;
  148. }
  149. QVariant RemuxQueueModel::getIcon(RemuxEntryState state)
  150. {
  151. QVariant icon;
  152. QStyle *style = QApplication::style();
  153. switch (state) {
  154. case RemuxEntryState::Complete:
  155. icon = style->standardIcon(QStyle::SP_DialogApplyButton);
  156. break;
  157. case RemuxEntryState::InProgress:
  158. icon = style->standardIcon(QStyle::SP_ArrowRight);
  159. break;
  160. case RemuxEntryState::Error:
  161. icon = style->standardIcon(QStyle::SP_DialogCancelButton);
  162. break;
  163. case RemuxEntryState::InvalidPath:
  164. icon = style->standardIcon(QStyle::SP_MessageBoxWarning);
  165. break;
  166. default:
  167. break;
  168. }
  169. return icon;
  170. }
  171. void RemuxQueueModel::checkInputPath(int row)
  172. {
  173. RemuxQueueEntry &entry = queue[row];
  174. if (entry.sourcePath.isEmpty()) {
  175. entry.state = RemuxEntryState::Empty;
  176. } else {
  177. entry.sourcePath = QDir::toNativeSeparators(entry.sourcePath);
  178. QFileInfo fileInfo(entry.sourcePath);
  179. if (fileInfo.exists())
  180. entry.state = RemuxEntryState::Ready;
  181. else
  182. entry.state = RemuxEntryState::InvalidPath;
  183. QString newExt = ".mp4";
  184. QString suffix = fileInfo.suffix();
  185. if (suffix.contains("mov", Qt::CaseInsensitive) || suffix.contains("mp4", Qt::CaseInsensitive)) {
  186. newExt = ".remuxed." + suffix;
  187. }
  188. if (entry.state == RemuxEntryState::Ready)
  189. entry.targetPath = QDir::toNativeSeparators(fileInfo.path() + QDir::separator() +
  190. fileInfo.completeBaseName() + newExt);
  191. }
  192. if (entry.state == RemuxEntryState::Ready && isProcessing)
  193. entry.state = RemuxEntryState::Pending;
  194. emit dataChanged(index(row, 0), index(row, RemuxEntryColumn::Count));
  195. }
  196. QFileInfoList RemuxQueueModel::checkForOverwrites() const
  197. {
  198. QFileInfoList list;
  199. for (const RemuxQueueEntry &entry : queue) {
  200. if (entry.state == RemuxEntryState::Ready) {
  201. QFileInfo fileInfo(entry.targetPath);
  202. if (fileInfo.exists()) {
  203. list.append(fileInfo);
  204. }
  205. }
  206. }
  207. return list;
  208. }
  209. bool RemuxQueueModel::checkForErrors() const
  210. {
  211. bool hasErrors = false;
  212. for (const RemuxQueueEntry &entry : queue) {
  213. if (entry.state == RemuxEntryState::Error) {
  214. hasErrors = true;
  215. break;
  216. }
  217. }
  218. return hasErrors;
  219. }
  220. void RemuxQueueModel::clearAll()
  221. {
  222. beginRemoveRows(QModelIndex(), 0, queue.size() - 1);
  223. queue.clear();
  224. endRemoveRows();
  225. }
  226. void RemuxQueueModel::clearFinished()
  227. {
  228. int index = 0;
  229. for (index = 0; index < queue.size(); index++) {
  230. const RemuxQueueEntry &entry = queue[index];
  231. if (entry.state == RemuxEntryState::Complete) {
  232. beginRemoveRows(QModelIndex(), index, index);
  233. queue.removeAt(index);
  234. endRemoveRows();
  235. index--;
  236. }
  237. }
  238. }
  239. bool RemuxQueueModel::canClearFinished() const
  240. {
  241. bool canClearFinished = false;
  242. for (const RemuxQueueEntry &entry : queue)
  243. if (entry.state == RemuxEntryState::Complete) {
  244. canClearFinished = true;
  245. break;
  246. }
  247. return canClearFinished;
  248. }
  249. void RemuxQueueModel::beginProcessing()
  250. {
  251. for (RemuxQueueEntry &entry : queue)
  252. if (entry.state == RemuxEntryState::Ready)
  253. entry.state = RemuxEntryState::Pending;
  254. // Signal that the insertion point no longer exists.
  255. beginRemoveRows(QModelIndex(), queue.length(), queue.length());
  256. endRemoveRows();
  257. isProcessing = true;
  258. emit dataChanged(index(0, RemuxEntryColumn::State), index(queue.length(), RemuxEntryColumn::State));
  259. }
  260. void RemuxQueueModel::endProcessing()
  261. {
  262. for (RemuxQueueEntry &entry : queue) {
  263. if (entry.state == RemuxEntryState::Pending) {
  264. entry.state = RemuxEntryState::Ready;
  265. }
  266. }
  267. // Signal that the insertion point exists again.
  268. isProcessing = false;
  269. if (!autoRemux) {
  270. beginInsertRows(QModelIndex(), queue.length(), queue.length());
  271. endInsertRows();
  272. }
  273. emit dataChanged(index(0, RemuxEntryColumn::State), index(queue.length(), RemuxEntryColumn::State));
  274. }
  275. bool RemuxQueueModel::beginNextEntry(QString &inputPath, QString &outputPath)
  276. {
  277. bool anyStarted = false;
  278. for (int row = 0; row < queue.length(); row++) {
  279. RemuxQueueEntry &entry = queue[row];
  280. if (entry.state == RemuxEntryState::Pending) {
  281. entry.state = RemuxEntryState::InProgress;
  282. inputPath = entry.sourcePath;
  283. outputPath = entry.targetPath;
  284. QModelIndex index = this->index(row, RemuxEntryColumn::State);
  285. emit dataChanged(index, index);
  286. anyStarted = true;
  287. break;
  288. }
  289. }
  290. return anyStarted;
  291. }
  292. void RemuxQueueModel::finishEntry(bool success)
  293. {
  294. for (int row = 0; row < queue.length(); row++) {
  295. RemuxQueueEntry &entry = queue[row];
  296. if (entry.state == RemuxEntryState::InProgress) {
  297. if (success)
  298. entry.state = RemuxEntryState::Complete;
  299. else
  300. entry.state = RemuxEntryState::Error;
  301. QModelIndex index = this->index(row, RemuxEntryColumn::State);
  302. emit dataChanged(index, index);
  303. break;
  304. }
  305. }
  306. }