| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990 | 
							- /******************************************************************************
 
-     Copyright (C) 2014 by Ruwen Hahn <[email protected]>
 
-     This program is free software: you can redistribute it and/or modify
 
-     it under the terms of the GNU General Public License as published by
 
-     the Free Software Foundation, either version 2 of the License, or
 
-     (at your option) any later version.
 
-     This program is distributed in the hope that it will be useful,
 
-     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
-     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
-     GNU General Public License for more details.
 
-     You should have received a copy of the GNU General Public License
 
-     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
- ******************************************************************************/
 
- #include "window-remux.hpp"
 
- #include "obs-app.hpp"
 
- #include <QCloseEvent>
 
- #include <QDirIterator>
 
- #include <QItemDelegate>
 
- #include <QLineEdit>
 
- #include <QMessageBox>
 
- #include <QMimeData>
 
- #include <QPainter>
 
- #include <QPushButton>
 
- #include <QStandardItemModel>
 
- #include <QStyledItemDelegate>
 
- #include <QToolButton>
 
- #include <QTimer>
 
- #include "qt-wrappers.hpp"
 
- #include <memory>
 
- #include <cmath>
 
- using namespace std;
 
- enum RemuxEntryColumn {
 
- 	State,
 
- 	InputPath,
 
- 	OutputPath,
 
- 	Count
 
- };
 
- enum RemuxEntryRole { EntryStateRole = Qt::UserRole, NewPathsToProcessRole };
 
- /**********************************************************
 
-   Delegate - Presents cells in the grid.
 
- **********************************************************/
 
- RemuxEntryPathItemDelegate::RemuxEntryPathItemDelegate(
 
- 	bool isOutput, const QString &defaultPath)
 
- 	: QStyledItemDelegate(), isOutput(isOutput), defaultPath(defaultPath)
 
- {
 
- }
 
- QWidget *RemuxEntryPathItemDelegate::createEditor(
 
- 	QWidget *parent, const QStyleOptionViewItem & /* option */,
 
- 	const QModelIndex &index) const
 
- {
 
- 	RemuxEntryState state =
 
- 		index.model()
 
- 			->index(index.row(), RemuxEntryColumn::State)
 
- 			.data(RemuxEntryRole::EntryStateRole)
 
- 			.value<RemuxEntryState>();
 
- 	if (state == RemuxEntryState::Pending ||
 
- 	    state == RemuxEntryState::InProgress) {
 
- 		// Never allow modification of rows that are
 
- 		// in progress.
 
- 		return Q_NULLPTR;
 
- 	} else if (isOutput && state != RemuxEntryState::Ready) {
 
- 		// Do not allow modification of output rows
 
- 		// that aren't associated with a valid input.
 
- 		return Q_NULLPTR;
 
- 	} else if (!isOutput && state == RemuxEntryState::Complete) {
 
- 		// Don't allow modification of rows that are
 
- 		// already complete.
 
- 		return Q_NULLPTR;
 
- 	} else {
 
- 		QSizePolicy buttonSizePolicy(
 
- 			QSizePolicy::Policy::Minimum,
 
- 			QSizePolicy::Policy::Expanding,
 
- 			QSizePolicy::ControlType::PushButton);
 
- 		QWidget *container = new QWidget(parent);
 
- 		auto browseCallback = [this, container]() {
 
- 			const_cast<RemuxEntryPathItemDelegate *>(this)
 
- 				->handleBrowse(container);
 
- 		};
 
- 		auto clearCallback = [this, container]() {
 
- 			const_cast<RemuxEntryPathItemDelegate *>(this)
 
- 				->handleClear(container);
 
- 		};
 
- 		QHBoxLayout *layout = new QHBoxLayout();
 
- 		layout->setMargin(0);
 
- 		layout->setSpacing(0);
 
- 		QLineEdit *text = new QLineEdit();
 
- 		text->setObjectName(QStringLiteral("text"));
 
- 		text->setSizePolicy(
 
- 			QSizePolicy(QSizePolicy::Policy::Expanding,
 
- 				    QSizePolicy::Policy::Expanding,
 
- 				    QSizePolicy::ControlType::LineEdit));
 
- 		layout->addWidget(text);
 
- 		QObject::connect(text, SIGNAL(editingFinished()), this,
 
- 				 SLOT(updateText()));
 
- 		QToolButton *browseButton = new QToolButton();
 
- 		browseButton->setText("...");
 
- 		browseButton->setSizePolicy(buttonSizePolicy);
 
- 		layout->addWidget(browseButton);
 
- 		container->connect(browseButton, &QToolButton::clicked,
 
- 				   browseCallback);
 
- 		// The "clear" button is not shown in output cells
 
- 		// or the insertion point's input cell.
 
- 		if (!isOutput && state != RemuxEntryState::Empty) {
 
- 			QToolButton *clearButton = new QToolButton();
 
- 			clearButton->setText("X");
 
- 			clearButton->setSizePolicy(buttonSizePolicy);
 
- 			layout->addWidget(clearButton);
 
- 			container->connect(clearButton, &QToolButton::clicked,
 
- 					   clearCallback);
 
- 		}
 
- 		container->setLayout(layout);
 
- 		container->setFocusProxy(text);
 
- 		return container;
 
- 	}
 
- }
 
- void RemuxEntryPathItemDelegate::setEditorData(QWidget *editor,
 
- 					       const QModelIndex &index) const
 
- {
 
- 	QLineEdit *text = editor->findChild<QLineEdit *>();
 
- 	text->setText(index.data().toString());
 
- 	editor->setProperty(PATH_LIST_PROP, QVariant());
 
- }
 
- void RemuxEntryPathItemDelegate::setModelData(QWidget *editor,
 
- 					      QAbstractItemModel *model,
 
- 					      const QModelIndex &index) const
 
- {
 
- 	// We use the PATH_LIST_PROP property to pass a list of
 
- 	// path strings from the editor widget into the model's
 
- 	// NewPathsToProcessRole. This is only used when paths
 
- 	// are selected through the "browse" or "delete" buttons
 
- 	// in the editor. If the user enters new text in the
 
- 	// text box, we simply pass that text on to the model
 
- 	// as normal text data in the default role.
 
- 	QVariant pathListProp = editor->property(PATH_LIST_PROP);
 
- 	if (pathListProp.isValid()) {
 
- 		QStringList list =
 
- 			editor->property(PATH_LIST_PROP).toStringList();
 
- 		if (isOutput) {
 
- 			if (list.size() > 0)
 
- 				model->setData(index, list);
 
- 		} else
 
- 			model->setData(index, list,
 
- 				       RemuxEntryRole::NewPathsToProcessRole);
 
- 	} else {
 
- 		QLineEdit *lineEdit = editor->findChild<QLineEdit *>();
 
- 		model->setData(index, lineEdit->text());
 
- 	}
 
- }
 
- void RemuxEntryPathItemDelegate::paint(QPainter *painter,
 
- 				       const QStyleOptionViewItem &option,
 
- 				       const QModelIndex &index) const
 
- {
 
- 	RemuxEntryState state =
 
- 		index.model()
 
- 			->index(index.row(), RemuxEntryColumn::State)
 
- 			.data(RemuxEntryRole::EntryStateRole)
 
- 			.value<RemuxEntryState>();
 
- 	QStyleOptionViewItem localOption = option;
 
- 	initStyleOption(&localOption, index);
 
- 	if (isOutput) {
 
- 		if (state != Ready) {
 
- 			QColor background = localOption.palette.color(
 
- 				QPalette::ColorGroup::Disabled,
 
- 				QPalette::ColorRole::Window);
 
- 			localOption.backgroundBrush = QBrush(background);
 
- 		}
 
- 	}
 
- 	QApplication::style()->drawControl(QStyle::CE_ItemViewItem,
 
- 					   &localOption, painter);
 
- }
 
- void RemuxEntryPathItemDelegate::handleBrowse(QWidget *container)
 
- {
 
- 	QString OutputPattern = "(*.mp4 *.flv *.mov *.mkv *.ts *.m3u8)";
 
- 	QString InputPattern = "(*.flv *.mov *.mkv *.ts *.m3u8)";
 
- 	QLineEdit *text = container->findChild<QLineEdit *>();
 
- 	QString currentPath = text->text();
 
- 	if (currentPath.isEmpty())
 
- 		currentPath = defaultPath;
 
- 	bool isSet = false;
 
- 	if (isOutput) {
 
- 		QString newPath = SaveFile(container,
 
- 					   QTStr("Remux.SelectTarget"),
 
- 					   currentPath, OutputPattern);
 
- 		if (!newPath.isEmpty()) {
 
- 			container->setProperty(PATH_LIST_PROP,
 
- 					       QStringList() << newPath);
 
- 			isSet = true;
 
- 		}
 
- 	} else {
 
- 		QStringList paths = OpenFiles(
 
- 			container, QTStr("Remux.SelectRecording"), currentPath,
 
- 			QTStr("Remux.OBSRecording") + QString(" ") +
 
- 				InputPattern);
 
- 		if (!paths.empty()) {
 
- 			container->setProperty(PATH_LIST_PROP, paths);
 
- 			isSet = true;
 
- 		}
 
- 	}
 
- 	if (isSet)
 
- 		emit commitData(container);
 
- }
 
- void RemuxEntryPathItemDelegate::handleClear(QWidget *container)
 
- {
 
- 	// An empty string list will indicate that the entry is being
 
- 	// blanked and should be deleted.
 
- 	container->setProperty(PATH_LIST_PROP, QStringList());
 
- 	emit commitData(container);
 
- }
 
- void RemuxEntryPathItemDelegate::updateText()
 
- {
 
- 	QLineEdit *lineEdit = dynamic_cast<QLineEdit *>(sender());
 
- 	QWidget *editor = lineEdit->parentWidget();
 
- 	emit commitData(editor);
 
- }
 
- /**********************************************************
 
-   Model - Manages the queue's data
 
- **********************************************************/
 
- int RemuxQueueModel::rowCount(const QModelIndex &) const
 
- {
 
- 	return queue.length() + (isProcessing ? 0 : 1);
 
- }
 
- int RemuxQueueModel::columnCount(const QModelIndex &) const
 
- {
 
- 	return RemuxEntryColumn::Count;
 
- }
 
- QVariant RemuxQueueModel::data(const QModelIndex &index, int role) const
 
- {
 
- 	QVariant result = QVariant();
 
- 	if (index.row() >= queue.length()) {
 
- 		return QVariant();
 
- 	} else if (role == Qt::DisplayRole) {
 
- 		switch (index.column()) {
 
- 		case RemuxEntryColumn::InputPath:
 
- 			result = queue[index.row()].sourcePath;
 
- 			break;
 
- 		case RemuxEntryColumn::OutputPath:
 
- 			result = queue[index.row()].targetPath;
 
- 			break;
 
- 		}
 
- 	} else if (role == Qt::DecorationRole &&
 
- 		   index.column() == RemuxEntryColumn::State) {
 
- 		result = getIcon(queue[index.row()].state);
 
- 	} else if (role == RemuxEntryRole::EntryStateRole) {
 
- 		result = queue[index.row()].state;
 
- 	}
 
- 	return result;
 
- }
 
- QVariant RemuxQueueModel::headerData(int section, Qt::Orientation orientation,
 
- 				     int role) const
 
- {
 
- 	QVariant result = QVariant();
 
- 	if (role == Qt::DisplayRole &&
 
- 	    orientation == Qt::Orientation::Horizontal) {
 
- 		switch (section) {
 
- 		case RemuxEntryColumn::State:
 
- 			result = QString();
 
- 			break;
 
- 		case RemuxEntryColumn::InputPath:
 
- 			result = QTStr("Remux.SourceFile");
 
- 			break;
 
- 		case RemuxEntryColumn::OutputPath:
 
- 			result = QTStr("Remux.TargetFile");
 
- 			break;
 
- 		}
 
- 	}
 
- 	return result;
 
- }
 
- Qt::ItemFlags RemuxQueueModel::flags(const QModelIndex &index) const
 
- {
 
- 	Qt::ItemFlags flags = QAbstractTableModel::flags(index);
 
- 	if (index.column() == RemuxEntryColumn::InputPath) {
 
- 		flags |= Qt::ItemIsEditable;
 
- 	} else if (index.column() == RemuxEntryColumn::OutputPath &&
 
- 		   index.row() != queue.length()) {
 
- 		flags |= Qt::ItemIsEditable;
 
- 	}
 
- 	return flags;
 
- }
 
- bool RemuxQueueModel::setData(const QModelIndex &index, const QVariant &value,
 
- 			      int role)
 
- {
 
- 	bool success = false;
 
- 	if (role == RemuxEntryRole::NewPathsToProcessRole) {
 
- 		QStringList pathList = value.toStringList();
 
- 		if (pathList.size() == 0) {
 
- 			if (index.row() < queue.size()) {
 
- 				beginRemoveRows(QModelIndex(), index.row(),
 
- 						index.row());
 
- 				queue.removeAt(index.row());
 
- 				endRemoveRows();
 
- 			}
 
- 		} else {
 
- 			if (pathList.size() > 1 &&
 
- 			    index.row() < queue.length()) {
 
- 				queue[index.row()].sourcePath = pathList[0];
 
- 				checkInputPath(index.row());
 
- 				pathList.removeAt(0);
 
- 				success = true;
 
- 			}
 
- 			if (pathList.size() > 0) {
 
- 				int row = index.row();
 
- 				int lastRow = row + pathList.size() - 1;
 
- 				beginInsertRows(QModelIndex(), row, lastRow);
 
- 				for (QString path : pathList) {
 
- 					RemuxQueueEntry entry;
 
- 					entry.sourcePath = path;
 
- 					queue.insert(row, entry);
 
- 					row++;
 
- 				}
 
- 				endInsertRows();
 
- 				for (row = index.row(); row <= lastRow; row++) {
 
- 					checkInputPath(row);
 
- 				}
 
- 				success = true;
 
- 			}
 
- 		}
 
- 	} else if (index.row() == queue.length()) {
 
- 		QString path = value.toString();
 
- 		if (!path.isEmpty()) {
 
- 			RemuxQueueEntry entry;
 
- 			entry.sourcePath = path;
 
- 			beginInsertRows(QModelIndex(), queue.length() + 1,
 
- 					queue.length() + 1);
 
- 			queue.append(entry);
 
- 			endInsertRows();
 
- 			checkInputPath(index.row());
 
- 			success = true;
 
- 		}
 
- 	} else {
 
- 		QString path = value.toString();
 
- 		if (path.isEmpty()) {
 
- 			if (index.column() == RemuxEntryColumn::InputPath) {
 
- 				beginRemoveRows(QModelIndex(), index.row(),
 
- 						index.row());
 
- 				queue.removeAt(index.row());
 
- 				endRemoveRows();
 
- 			}
 
- 		} else {
 
- 			switch (index.column()) {
 
- 			case RemuxEntryColumn::InputPath:
 
- 				queue[index.row()].sourcePath =
 
- 					value.toString();
 
- 				checkInputPath(index.row());
 
- 				success = true;
 
- 				break;
 
- 			case RemuxEntryColumn::OutputPath:
 
- 				queue[index.row()].targetPath =
 
- 					value.toString();
 
- 				emit dataChanged(index, index);
 
- 				success = true;
 
- 				break;
 
- 			}
 
- 		}
 
- 	}
 
- 	return success;
 
- }
 
- QVariant RemuxQueueModel::getIcon(RemuxEntryState state)
 
- {
 
- 	QVariant icon;
 
- 	QStyle *style = QApplication::style();
 
- 	switch (state) {
 
- 	case RemuxEntryState::Complete:
 
- 		icon = style->standardIcon(QStyle::SP_DialogApplyButton);
 
- 		break;
 
- 	case RemuxEntryState::InProgress:
 
- 		icon = style->standardIcon(QStyle::SP_ArrowRight);
 
- 		break;
 
- 	case RemuxEntryState::Error:
 
- 		icon = style->standardIcon(QStyle::SP_DialogCancelButton);
 
- 		break;
 
- 	case RemuxEntryState::InvalidPath:
 
- 		icon = style->standardIcon(QStyle::SP_MessageBoxWarning);
 
- 		break;
 
- 	default:
 
- 		break;
 
- 	}
 
- 	return icon;
 
- }
 
- void RemuxQueueModel::checkInputPath(int row)
 
- {
 
- 	RemuxQueueEntry &entry = queue[row];
 
- 	if (entry.sourcePath.isEmpty()) {
 
- 		entry.state = RemuxEntryState::Empty;
 
- 	} else {
 
- 		entry.sourcePath = QDir::toNativeSeparators(entry.sourcePath);
 
- 		QFileInfo fileInfo(entry.sourcePath);
 
- 		if (fileInfo.exists())
 
- 			entry.state = RemuxEntryState::Ready;
 
- 		else
 
- 			entry.state = RemuxEntryState::InvalidPath;
 
- 		if (entry.state == RemuxEntryState::Ready)
 
- 			entry.targetPath = QDir::toNativeSeparators(
 
- 				fileInfo.path() + QDir::separator() +
 
- 				fileInfo.completeBaseName() + ".mp4");
 
- 	}
 
- 	if (entry.state == RemuxEntryState::Ready && isProcessing)
 
- 		entry.state = RemuxEntryState::Pending;
 
- 	emit dataChanged(index(row, 0), index(row, RemuxEntryColumn::Count));
 
- }
 
- QFileInfoList RemuxQueueModel::checkForOverwrites() const
 
- {
 
- 	QFileInfoList list;
 
- 	for (const RemuxQueueEntry &entry : queue) {
 
- 		if (entry.state == RemuxEntryState::Ready) {
 
- 			QFileInfo fileInfo(entry.targetPath);
 
- 			if (fileInfo.exists()) {
 
- 				list.append(fileInfo);
 
- 			}
 
- 		}
 
- 	}
 
- 	return list;
 
- }
 
- bool RemuxQueueModel::checkForErrors() const
 
- {
 
- 	bool hasErrors = false;
 
- 	for (const RemuxQueueEntry &entry : queue) {
 
- 		if (entry.state == RemuxEntryState::Error) {
 
- 			hasErrors = true;
 
- 			break;
 
- 		}
 
- 	}
 
- 	return hasErrors;
 
- }
 
- void RemuxQueueModel::clearAll()
 
- {
 
- 	beginRemoveRows(QModelIndex(), 0, queue.size() - 1);
 
- 	queue.clear();
 
- 	endRemoveRows();
 
- }
 
- void RemuxQueueModel::clearFinished()
 
- {
 
- 	int index = 0;
 
- 	for (index = 0; index < queue.size(); index++) {
 
- 		const RemuxQueueEntry &entry = queue[index];
 
- 		if (entry.state == RemuxEntryState::Complete) {
 
- 			beginRemoveRows(QModelIndex(), index, index);
 
- 			queue.removeAt(index);
 
- 			endRemoveRows();
 
- 			index--;
 
- 		}
 
- 	}
 
- }
 
- bool RemuxQueueModel::canClearFinished() const
 
- {
 
- 	bool canClearFinished = false;
 
- 	for (const RemuxQueueEntry &entry : queue)
 
- 		if (entry.state == RemuxEntryState::Complete) {
 
- 			canClearFinished = true;
 
- 			break;
 
- 		}
 
- 	return canClearFinished;
 
- }
 
- void RemuxQueueModel::beginProcessing()
 
- {
 
- 	for (RemuxQueueEntry &entry : queue)
 
- 		if (entry.state == RemuxEntryState::Ready)
 
- 			entry.state = RemuxEntryState::Pending;
 
- 	// Signal that the insertion point no longer exists.
 
- 	beginRemoveRows(QModelIndex(), queue.length(), queue.length());
 
- 	endRemoveRows();
 
- 	isProcessing = true;
 
- 	emit dataChanged(index(0, RemuxEntryColumn::State),
 
- 			 index(queue.length(), RemuxEntryColumn::State));
 
- }
 
- void RemuxQueueModel::endProcessing()
 
- {
 
- 	for (RemuxQueueEntry &entry : queue) {
 
- 		if (entry.state == RemuxEntryState::Pending) {
 
- 			entry.state = RemuxEntryState::Ready;
 
- 		}
 
- 	}
 
- 	// Signal that the insertion point exists again.
 
- 	if (!autoRemux) {
 
- 		beginInsertRows(QModelIndex(), queue.length(), queue.length());
 
- 		endInsertRows();
 
- 	}
 
- 	isProcessing = false;
 
- 	emit dataChanged(index(0, RemuxEntryColumn::State),
 
- 			 index(queue.length(), RemuxEntryColumn::State));
 
- }
 
- bool RemuxQueueModel::beginNextEntry(QString &inputPath, QString &outputPath)
 
- {
 
- 	bool anyStarted = false;
 
- 	for (int row = 0; row < queue.length(); row++) {
 
- 		RemuxQueueEntry &entry = queue[row];
 
- 		if (entry.state == RemuxEntryState::Pending) {
 
- 			entry.state = RemuxEntryState::InProgress;
 
- 			inputPath = entry.sourcePath;
 
- 			outputPath = entry.targetPath;
 
- 			QModelIndex index =
 
- 				this->index(row, RemuxEntryColumn::State);
 
- 			emit dataChanged(index, index);
 
- 			anyStarted = true;
 
- 			break;
 
- 		}
 
- 	}
 
- 	return anyStarted;
 
- }
 
- void RemuxQueueModel::finishEntry(bool success)
 
- {
 
- 	for (int row = 0; row < queue.length(); row++) {
 
- 		RemuxQueueEntry &entry = queue[row];
 
- 		if (entry.state == RemuxEntryState::InProgress) {
 
- 			if (success)
 
- 				entry.state = RemuxEntryState::Complete;
 
- 			else
 
- 				entry.state = RemuxEntryState::Error;
 
- 			QModelIndex index =
 
- 				this->index(row, RemuxEntryColumn::State);
 
- 			emit dataChanged(index, index);
 
- 			break;
 
- 		}
 
- 	}
 
- }
 
- /**********************************************************
 
-   The actual remux window implementation
 
- **********************************************************/
 
- OBSRemux::OBSRemux(const char *path, QWidget *parent, bool autoRemux_)
 
- 	: QDialog(parent),
 
- 	  queueModel(new RemuxQueueModel),
 
- 	  worker(new RemuxWorker()),
 
- 	  ui(new Ui::OBSRemux),
 
- 	  recPath(path),
 
- 	  autoRemux(autoRemux_)
 
- {
 
- 	setAcceptDrops(true);
 
- 	setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
 
- 	ui->setupUi(this);
 
- 	ui->progressBar->setVisible(false);
 
- 	ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
 
- 	ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)
 
- 		->setEnabled(false);
 
- 	if (autoRemux) {
 
- 		resize(280, 40);
 
- 		ui->tableView->hide();
 
- 		ui->buttonBox->hide();
 
- 		ui->label->hide();
 
- 	}
 
- 	ui->progressBar->setMinimum(0);
 
- 	ui->progressBar->setMaximum(1000);
 
- 	ui->progressBar->setValue(0);
 
- 	ui->tableView->setModel(queueModel);
 
- 	ui->tableView->setItemDelegateForColumn(
 
- 		RemuxEntryColumn::InputPath,
 
- 		new RemuxEntryPathItemDelegate(false, recPath));
 
- 	ui->tableView->setItemDelegateForColumn(
 
- 		RemuxEntryColumn::OutputPath,
 
- 		new RemuxEntryPathItemDelegate(true, recPath));
 
- 	ui->tableView->horizontalHeader()->setSectionResizeMode(
 
- 		QHeaderView::ResizeMode::Stretch);
 
- 	ui->tableView->horizontalHeader()->setSectionResizeMode(
 
- 		RemuxEntryColumn::State, QHeaderView::ResizeMode::Fixed);
 
- 	ui->tableView->setEditTriggers(
 
- 		QAbstractItemView::EditTrigger::CurrentChanged);
 
- 	installEventFilter(CreateShortcutFilter());
 
- 	ui->buttonBox->button(QDialogButtonBox::Ok)
 
- 		->setText(QTStr("Remux.Remux"));
 
- 	ui->buttonBox->button(QDialogButtonBox::Reset)
 
- 		->setText(QTStr("Remux.ClearFinished"));
 
- 	ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)
 
- 		->setText(QTStr("Remux.ClearAll"));
 
- 	ui->buttonBox->button(QDialogButtonBox::Reset)->setDisabled(true);
 
- 	connect(ui->buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
 
- 		this, SLOT(beginRemux()));
 
- 	connect(ui->buttonBox->button(QDialogButtonBox::Reset),
 
- 		SIGNAL(clicked()), this, SLOT(clearFinished()));
 
- 	connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults),
 
- 		SIGNAL(clicked()), this, SLOT(clearAll()));
 
- 	connect(ui->buttonBox->button(QDialogButtonBox::Close),
 
- 		SIGNAL(clicked()), this, SLOT(close()));
 
- 	worker->moveToThread(&remuxer);
 
- 	remuxer.start();
 
- 	//gcc-4.8 can't use QPointer<RemuxWorker> below
 
- 	RemuxWorker *worker_ = worker;
 
- 	connect(worker_, &RemuxWorker::updateProgress, this,
 
- 		&OBSRemux::updateProgress);
 
- 	connect(&remuxer, &QThread::finished, worker_, &QObject::deleteLater);
 
- 	connect(worker_, &RemuxWorker::remuxFinished, this,
 
- 		&OBSRemux::remuxFinished);
 
- 	connect(this, &OBSRemux::remux, worker_, &RemuxWorker::remux);
 
- 	// Guessing the GCC bug mentioned above would also affect
 
- 	// QPointer<RemuxQueueModel>? Unsure.
 
- 	RemuxQueueModel *queueModel_ = queueModel;
 
- 	connect(queueModel_,
 
- 		SIGNAL(rowsInserted(const QModelIndex &, int, int)), this,
 
- 		SLOT(rowCountChanged(const QModelIndex &, int, int)));
 
- 	connect(queueModel_, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
 
- 		this, SLOT(rowCountChanged(const QModelIndex &, int, int)));
 
- 	QModelIndex index = queueModel->createIndex(0, 1);
 
- 	QMetaObject::invokeMethod(ui->tableView, "setCurrentIndex",
 
- 				  Qt::QueuedConnection,
 
- 				  Q_ARG(const QModelIndex &, index));
 
- }
 
- bool OBSRemux::stopRemux()
 
- {
 
- 	if (!worker->isWorking)
 
- 		return true;
 
- 	// By locking the worker thread's mutex, we ensure that its
 
- 	// update poll will be blocked as long as we're in here with
 
- 	// the popup open.
 
- 	QMutexLocker lock(&worker->updateMutex);
 
- 	bool exit = false;
 
- 	if (QMessageBox::critical(nullptr, QTStr("Remux.ExitUnfinishedTitle"),
 
- 				  QTStr("Remux.ExitUnfinished"),
 
- 				  QMessageBox::Yes | QMessageBox::No,
 
- 				  QMessageBox::No) == QMessageBox::Yes) {
 
- 		exit = true;
 
- 	}
 
- 	if (exit) {
 
- 		// Inform the worker it should no longer be
 
- 		// working. It will interrupt accordingly in
 
- 		// its next update callback.
 
- 		worker->isWorking = false;
 
- 	}
 
- 	return exit;
 
- }
 
- OBSRemux::~OBSRemux()
 
- {
 
- 	stopRemux();
 
- 	remuxer.quit();
 
- 	remuxer.wait();
 
- }
 
- void OBSRemux::rowCountChanged(const QModelIndex &, int, int)
 
- {
 
- 	// See if there are still any rows ready to remux. Change
 
- 	// the state of the "go" button accordingly.
 
- 	// There must be more than one row, since there will always be
 
- 	// at least one row for the empty insertion point.
 
- 	if (queueModel->rowCount() > 1) {
 
- 		ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
 
- 		ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)
 
- 			->setEnabled(true);
 
- 		ui->buttonBox->button(QDialogButtonBox::Reset)
 
- 			->setEnabled(queueModel->canClearFinished());
 
- 	} else {
 
- 		ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
 
- 		ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)
 
- 			->setEnabled(false);
 
- 		ui->buttonBox->button(QDialogButtonBox::Reset)
 
- 			->setEnabled(false);
 
- 	}
 
- }
 
- void OBSRemux::dropEvent(QDropEvent *ev)
 
- {
 
- 	QStringList urlList;
 
- 	for (QUrl url : ev->mimeData()->urls()) {
 
- 		QFileInfo fileInfo(url.toLocalFile());
 
- 		if (fileInfo.isDir()) {
 
- 			QStringList directoryFilter;
 
- 			directoryFilter << "*.flv"
 
- 					<< "*.mp4"
 
- 					<< "*.mov"
 
- 					<< "*.mkv"
 
- 					<< "*.ts"
 
- 					<< "*.m3u8";
 
- 			QDirIterator dirIter(fileInfo.absoluteFilePath(),
 
- 					     directoryFilter, QDir::Files,
 
- 					     QDirIterator::Subdirectories);
 
- 			while (dirIter.hasNext()) {
 
- 				urlList.append(dirIter.next());
 
- 			}
 
- 		} else {
 
- 			urlList.append(fileInfo.canonicalFilePath());
 
- 		}
 
- 	}
 
- 	if (urlList.empty()) {
 
- 		QMessageBox::information(nullptr,
 
- 					 QTStr("Remux.NoFilesAddedTitle"),
 
- 					 QTStr("Remux.NoFilesAdded"),
 
- 					 QMessageBox::Ok);
 
- 	} else if (!autoRemux) {
 
- 		QModelIndex insertIndex =
 
- 			queueModel->index(queueModel->rowCount() - 1,
 
- 					  RemuxEntryColumn::InputPath);
 
- 		queueModel->setData(insertIndex, urlList,
 
- 				    RemuxEntryRole::NewPathsToProcessRole);
 
- 	}
 
- }
 
- void OBSRemux::dragEnterEvent(QDragEnterEvent *ev)
 
- {
 
- 	if (ev->mimeData()->hasUrls() && !worker->isWorking)
 
- 		ev->accept();
 
- }
 
- void OBSRemux::beginRemux()
 
- {
 
- 	if (worker->isWorking) {
 
- 		stopRemux();
 
- 		return;
 
- 	}
 
- 	bool proceedWithRemux = true;
 
- 	QFileInfoList overwriteFiles = queueModel->checkForOverwrites();
 
- 	if (!overwriteFiles.empty()) {
 
- 		QString message = QTStr("Remux.FileExists");
 
- 		message += "\n\n";
 
- 		for (QFileInfo fileInfo : overwriteFiles)
 
- 			message += fileInfo.canonicalFilePath() + "\n";
 
- 		if (OBSMessageBox::question(this,
 
- 					    QTStr("Remux.FileExistsTitle"),
 
- 					    message) != QMessageBox::Yes)
 
- 			proceedWithRemux = false;
 
- 	}
 
- 	if (!proceedWithRemux)
 
- 		return;
 
- 	// Set all jobs to "pending" first.
 
- 	queueModel->beginProcessing();
 
- 	ui->progressBar->setVisible(true);
 
- 	ui->buttonBox->button(QDialogButtonBox::Ok)
 
- 		->setText(QTStr("Remux.Stop"));
 
- 	setAcceptDrops(false);
 
- 	remuxNextEntry();
 
- }
 
- void OBSRemux::AutoRemux(QString inFile, QString outFile)
 
- {
 
- 	if (inFile != "" && outFile != "" && autoRemux) {
 
- 		emit remux(inFile, outFile);
 
- 		autoRemuxFile = inFile;
 
- 	}
 
- }
 
- void OBSRemux::remuxNextEntry()
 
- {
 
- 	worker->lastProgress = 0.f;
 
- 	QString inputPath, outputPath;
 
- 	if (queueModel->beginNextEntry(inputPath, outputPath)) {
 
- 		emit remux(inputPath, outputPath);
 
- 	} else {
 
- 		queueModel->autoRemux = autoRemux;
 
- 		queueModel->endProcessing();
 
- 		if (!autoRemux) {
 
- 			OBSMessageBox::information(
 
- 				this, QTStr("Remux.FinishedTitle"),
 
- 				queueModel->checkForErrors()
 
- 					? QTStr("Remux.FinishedError")
 
- 					: QTStr("Remux.Finished"));
 
- 		}
 
- 		ui->progressBar->setVisible(autoRemux);
 
- 		ui->buttonBox->button(QDialogButtonBox::Ok)
 
- 			->setText(QTStr("Remux.Remux"));
 
- 		ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)
 
- 			->setEnabled(true);
 
- 		ui->buttonBox->button(QDialogButtonBox::Reset)
 
- 			->setEnabled(queueModel->canClearFinished());
 
- 		setAcceptDrops(true);
 
- 	}
 
- }
 
- void OBSRemux::closeEvent(QCloseEvent *event)
 
- {
 
- 	if (!stopRemux())
 
- 		event->ignore();
 
- 	else
 
- 		QDialog::closeEvent(event);
 
- }
 
- void OBSRemux::reject()
 
- {
 
- 	if (!stopRemux())
 
- 		return;
 
- 	QDialog::reject();
 
- }
 
- void OBSRemux::updateProgress(float percent)
 
- {
 
- 	ui->progressBar->setValue(percent * 10);
 
- }
 
- void OBSRemux::remuxFinished(bool success)
 
- {
 
- 	ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
 
- 	queueModel->finishEntry(success);
 
- 	if (autoRemux && autoRemuxFile != "") {
 
- 		QTimer::singleShot(3000, this, SLOT(close()));
 
- 	}
 
- 	remuxNextEntry();
 
- }
 
- void OBSRemux::clearFinished()
 
- {
 
- 	queueModel->clearFinished();
 
- }
 
- void OBSRemux::clearAll()
 
- {
 
- 	queueModel->clearAll();
 
- }
 
- /**********************************************************
 
-   Worker thread - Executes the libobs remux operation as a
 
-                   background process.
 
- **********************************************************/
 
- void RemuxWorker::UpdateProgress(float percent)
 
- {
 
- 	if (abs(lastProgress - percent) < 0.1f)
 
- 		return;
 
- 	emit updateProgress(percent);
 
- 	lastProgress = percent;
 
- }
 
- void RemuxWorker::remux(const QString &source, const QString &target)
 
- {
 
- 	isWorking = true;
 
- 	auto callback = [](void *data, float percent) {
 
- 		RemuxWorker *rw = static_cast<RemuxWorker *>(data);
 
- 		QMutexLocker lock(&rw->updateMutex);
 
- 		rw->UpdateProgress(percent);
 
- 		return rw->isWorking;
 
- 	};
 
- 	bool stopped = false;
 
- 	bool success = false;
 
- 	media_remux_job_t mr_job = nullptr;
 
- 	if (media_remux_job_create(&mr_job, QT_TO_UTF8(source),
 
- 				   QT_TO_UTF8(target))) {
 
- 		success = media_remux_job_process(mr_job, callback, this);
 
- 		media_remux_job_destroy(mr_job);
 
- 		stopped = !isWorking;
 
- 	}
 
- 	isWorking = false;
 
- 	emit remuxFinished(!stopped && success);
 
- }
 
 
  |