///////////////////////////////////////////////////////////////////////////////
// Simple x264 Launcher
-// Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
//
// 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
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
-#include "model_jobList.h"
+//Internal
#include "global.h"
+#include "model_jobList.h"
#include "thread_encode.h"
+#include "model_options.h"
+#include "model_preferences.h"
+#include "resource.h"
+//MUtils
+#include <MUtils/Sound.h>
+
+//Qt
#include <QIcon>
#include <QFileInfo>
+#include <QSettings>
+
+static const char *KEY_ENTRY_COUNT = "entry_count";
+static const char *KEY_SOURCE_FILE = "source_file";
+static const char *KEY_OUTPUT_FILE = "output_file";
+static const char *KEY_ENC_OPTIONS = "enc_options";
-JobListModel::JobListModel(void)
+static const char *JOB_TEMPLATE = "job_%08x";
+
+#define VALID_INDEX(INDEX) ((INDEX).isValid() && ((INDEX).row() >= 0) && ((INDEX).row() < m_jobs.count()))
+
+JobListModel::JobListModel(PreferencesModel *preferences)
{
+ m_preferences = preferences;
}
JobListModel::~JobListModel(void)
QUuid id = m_jobs.takeFirst();
EncodeThread *thread = m_threads.value(id, NULL);
LogFileModel *logFile = m_logFile.value(id, NULL);
- X264_DELETE(thread);
- X264_DELETE(logFile);
+ MUTILS_DELETE(thread);
+ MUTILS_DELETE(logFile);
}
}
case 1:
switch(m_status.value(m_jobs.at(index.row())))
{
- case EncodeThread::JobStatus_Enqueued:
+ case JobStatus_Enqueued:
return QVariant::fromValue<QString>(tr("Enqueued."));
break;
- case EncodeThread::JobStatus_Starting:
+ case JobStatus_Starting:
return QVariant::fromValue<QString>(tr("Starting..."));
break;
- case EncodeThread::JobStatus_Indexing:
+ case JobStatus_Indexing:
return QVariant::fromValue<QString>(tr("Indexing..."));
break;
- case EncodeThread::JobStatus_Running:
+ case JobStatus_Running:
return QVariant::fromValue<QString>(tr("Running..."));
break;
- case EncodeThread::JobStatus_Running_Pass1:
+ case JobStatus_Running_Pass1:
return QVariant::fromValue<QString>(tr("Running... (Pass 1)"));
break;
- case EncodeThread::JobStatus_Running_Pass2:
+ case JobStatus_Running_Pass2:
return QVariant::fromValue<QString>(tr("Running... (Pass 2)"));
break;
- case EncodeThread::JobStatus_Completed:
+ case JobStatus_Completed:
return QVariant::fromValue<QString>(tr("Completed."));
break;
- case EncodeThread::JobStatus_Failed:
+ case JobStatus_Failed:
return QVariant::fromValue<QString>(tr("Failed!"));
break;
- case EncodeThread::JobStatus_Pausing:
+ case JobStatus_Pausing:
return QVariant::fromValue<QString>(tr("Pausing..."));
break;
- case EncodeThread::JobStatus_Paused:
+ case JobStatus_Paused:
return QVariant::fromValue<QString>(tr("Paused."));
break;
- case EncodeThread::JobStatus_Resuming:
+ case JobStatus_Resuming:
return QVariant::fromValue<QString>(tr("Resuming..."));
break;
- case EncodeThread::JobStatus_Aborting:
+ case JobStatus_Aborting:
return QVariant::fromValue<QString>(tr("Aborting..."));
break;
- case EncodeThread::JobStatus_Aborted:
+ case JobStatus_Aborted:
return QVariant::fromValue<QString>(tr("Aborted!"));
break;
default:
{
switch(m_status.value(m_jobs.at(index.row())))
{
- case EncodeThread::JobStatus_Enqueued:
+ case JobStatus_Enqueued:
return QIcon(":/buttons/hourglass.png");
break;
- case EncodeThread::JobStatus_Starting:
+ case JobStatus_Starting:
return QIcon(":/buttons/lightning.png");
break;
- case EncodeThread::JobStatus_Indexing:
+ case JobStatus_Indexing:
return QIcon(":/buttons/find.png");
break;
- case EncodeThread::JobStatus_Running:
- case EncodeThread::JobStatus_Running_Pass1:
- case EncodeThread::JobStatus_Running_Pass2:
+ case JobStatus_Running:
+ case JobStatus_Running_Pass1:
+ case JobStatus_Running_Pass2:
return QIcon(":/buttons/play.png");
break;
- case EncodeThread::JobStatus_Completed:
+ case JobStatus_Completed:
return QIcon(":/buttons/accept.png");
break;
- case EncodeThread::JobStatus_Failed:
+ case JobStatus_Failed:
return QIcon(":/buttons/exclamation.png");
break;
- case EncodeThread::JobStatus_Pausing:
+ case JobStatus_Pausing:
return QIcon(":/buttons/clock_pause.png");
break;
- case EncodeThread::JobStatus_Paused:
+ case JobStatus_Paused:
return QIcon(":/buttons/suspended.png");
break;
- case EncodeThread::JobStatus_Resuming:
+ case JobStatus_Resuming:
return QIcon(":/buttons/clock_play.png");
break;
- case EncodeThread::JobStatus_Aborting:
+ case JobStatus_Aborting:
return QIcon(":/buttons/clock_stop.png");
break;
- case EncodeThread::JobStatus_Aborted:
+ case JobStatus_Aborted:
return QIcon(":/buttons/error.png");
break;
default:
QModelIndex JobListModel::insertJob(EncodeThread *thread)
{
QUuid id = thread->getId();
- LogFileModel *logFile = NULL;
if(m_jobs.contains(id))
{
return QModelIndex();
}
-
+
+ QString config = "N/A";
+
+ switch(thread->options()->rcMode())
+ {
+ case OptionsModel::EncType_X264:
+ config = QString("x264");
+ break;
+ case OptionsModel::EncType_X265:
+ config = QString("x265");
+ break;
+ }
+
+ switch(thread->options()->rcMode())
+ {
+ case OptionsModel::RCMode_CRF:
+ config = QString("%1 CRF@%2") .arg(config, QString::number(thread->options()->quantizer()));
+ break;
+ case OptionsModel::RCMode_CQ:
+ config = QString("%1 CQ@%2") .arg(config, QString::number(qRound(thread->options()->quantizer())));
+ break;
+ case OptionsModel::RCMode_2Pass:
+ config = QString("%1 2Pass@%2").arg(config, QString::number(thread->options()->bitrate()));
+ break;
+ case OptionsModel::RCMode_ABR:
+ config = QString("%1 ABR@%2") .arg(config, QString::number(thread->options()->bitrate()));
+ break;
+ }
+
int n = 2;
- QString jobName = QFileInfo(thread->sourceFileName()).completeBaseName();
+ QString jobName = QString("%1 (%2)").arg(QFileInfo(thread->sourceFileName()).completeBaseName().simplified(), config);
forever
{
}
if(!unique)
{
- jobName = QString("%1 (%2)").arg(QFileInfo(thread->sourceFileName()).completeBaseName(), QString::number(n++));
+ jobName = QString("%1 %2 (%3)").arg(QFileInfo(thread->sourceFileName()).completeBaseName().simplified(), QString::number(n++), config);
continue;
}
break;
}
-
+
+ LogFileModel *logFile = new LogFileModel(thread->sourceFileName(), thread->outputFileName(), config);
+
beginInsertRows(QModelIndex(), m_jobs.count(), m_jobs.count());
m_jobs.append(id);
m_name.insert(id, jobName);
- m_status.insert(id, EncodeThread::JobStatus_Enqueued);
+ m_status.insert(id, JobStatus_Enqueued);
m_progress.insert(id, 0);
m_threads.insert(id, thread);
- m_logFile.insert(id, (logFile = new LogFileModel));
+ m_logFile.insert(id, logFile);
m_details.insert(id, tr("Not started yet."));
endInsertRows();
- connect(thread, SIGNAL(statusChanged(QUuid, EncodeThread::JobStatus)), this, SLOT(updateStatus(QUuid, EncodeThread::JobStatus)), Qt::QueuedConnection);
+ connect(thread, SIGNAL(statusChanged(QUuid, JobStatus)), this, SLOT(updateStatus(QUuid, JobStatus)), Qt::QueuedConnection);
connect(thread, SIGNAL(progressChanged(QUuid, unsigned int)), this, SLOT(updateProgress(QUuid, unsigned int)), Qt::QueuedConnection);
connect(thread, SIGNAL(messageLogged(QUuid, QString)), logFile, SLOT(addLogMessage(QUuid, QString)), Qt::QueuedConnection);
connect(thread, SIGNAL(detailsChanged(QUuid, QString)), this, SLOT(updateDetails(QUuid, QString)), Qt::QueuedConnection);
bool JobListModel::startJob(const QModelIndex &index)
{
- if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ if(VALID_INDEX(index))
{
QUuid id = m_jobs.at(index.row());
- if(m_status.value(id) == EncodeThread::JobStatus_Enqueued)
+ if(m_status.value(id) == JobStatus_Enqueued)
{
- updateStatus(id, EncodeThread::JobStatus_Starting);
+ updateStatus(id, JobStatus_Starting);
updateDetails(id, tr("Starting up, please wait..."));
m_threads.value(id)->start();
return true;
bool JobListModel::pauseJob(const QModelIndex &index)
{
- if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ if(VALID_INDEX(index))
{
QUuid id = m_jobs.at(index.row());
- EncodeThread::JobStatus status = m_status.value(id);
- if((status == EncodeThread::JobStatus_Indexing) || (status == EncodeThread::JobStatus_Running) ||
- (status == EncodeThread::JobStatus_Running_Pass1) || (status == EncodeThread::JobStatus_Running_Pass2))
+ JobStatus status = m_status.value(id);
+ if((status == JobStatus_Indexing) || (status == JobStatus_Running) ||
+ (status == JobStatus_Running_Pass1) || (status == JobStatus_Running_Pass2))
{
- updateStatus(id, EncodeThread::JobStatus_Pausing);
+ updateStatus(id, JobStatus_Pausing);
m_threads.value(id)->pauseJob();
return true;
}
bool JobListModel::resumeJob(const QModelIndex &index)
{
- if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ if(VALID_INDEX(index))
{
QUuid id = m_jobs.at(index.row());
- EncodeThread::JobStatus status = m_status.value(id);
- if(status == EncodeThread::JobStatus_Paused)
+ JobStatus status = m_status.value(id);
+ if(status == JobStatus_Paused)
{
- updateStatus(id, EncodeThread::JobStatus_Resuming);
+ updateStatus(id, JobStatus_Resuming);
m_threads.value(id)->resumeJob();
return true;
}
bool JobListModel::abortJob(const QModelIndex &index)
{
- if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ if(VALID_INDEX(index))
{
QUuid id = m_jobs.at(index.row());
- if(m_status.value(id) == EncodeThread::JobStatus_Indexing || m_status.value(id) == EncodeThread::JobStatus_Running ||
- m_status.value(id) == EncodeThread::JobStatus_Running_Pass1 || EncodeThread::JobStatus_Running_Pass2)
+ if(m_status.value(id) == JobStatus_Indexing || m_status.value(id) == JobStatus_Running ||
+ m_status.value(id) == JobStatus_Running_Pass1 || JobStatus_Running_Pass2)
{
- updateStatus(id, EncodeThread::JobStatus_Aborting);
+ updateStatus(id, JobStatus_Aborting);
m_threads.value(id)->abortJob();
return true;
}
return false;
}
+bool JobListModel::deleteJob(const QModelIndex &index)
+{
+ if(VALID_INDEX(index))
+ {
+ QUuid id = m_jobs.at(index.row());
+ if(m_status.value(id) == JobStatus_Completed || m_status.value(id) == JobStatus_Failed ||
+ m_status.value(id) == JobStatus_Aborted || m_status.value(id) == JobStatus_Enqueued)
+ {
+ int idx = index.row();
+ QUuid id = m_jobs.at(idx);
+ EncodeThread *thread = m_threads.value(id, NULL);
+ LogFileModel *logFile = m_logFile.value(id, NULL);
+ if((thread == NULL) || (!thread->isRunning()))
+ {
+
+ beginRemoveRows(QModelIndex(), idx, idx);
+ m_jobs.removeAt(index.row());
+ m_name.remove(id);
+ m_threads.remove(id);
+ m_status.remove(id);
+ m_progress.remove(id);
+ m_logFile.remove(id);
+ m_details.remove(id);
+ endRemoveRows();
+ MUTILS_DELETE(thread);
+ MUTILS_DELETE(logFile);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool JobListModel::moveJob(const QModelIndex &index, const int &direction)
+{
+ if(VALID_INDEX(index))
+ {
+ if((direction == MOVE_UP) && (index.row() > 0))
+ {
+ beginMoveRows(QModelIndex(), index.row(), index.row(), QModelIndex(), index.row() - 1);
+ m_jobs.swap(index.row(), index.row() - 1);
+ endMoveRows();
+ return true;
+ }
+ if((direction == MOVE_DOWN) && (index.row() < m_jobs.size() - 1))
+ {
+ beginMoveRows(QModelIndex(), index.row(), index.row(), QModelIndex(), index.row() + 2);
+ m_jobs.swap(index.row(), index.row() + 1);
+ endMoveRows();
+ return true;
+ }
+ }
+
+ return false;
+}
+
LogFileModel *JobListModel::getLogFile(const QModelIndex &index)
{
if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
return NULL;
}
-EncodeThread::JobStatus JobListModel::getJobStatus(const QModelIndex &index)
+const QString &JobListModel::getJobSourceFile(const QModelIndex &index)
+{
+ static QString nullStr;
+
+ if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ {
+ EncodeThread *thread = m_threads.value(m_jobs.at(index.row()));
+ return (thread != NULL) ? thread->sourceFileName() : nullStr;
+ }
+
+ return nullStr;
+}
+
+const QString &JobListModel::getJobOutputFile(const QModelIndex &index)
+{
+ static QString nullStr;
+
+ if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ {
+ EncodeThread *thread = m_threads.value(m_jobs.at(index.row()));
+ return (thread != NULL) ? thread->outputFileName() : nullStr;
+ }
+
+ return nullStr;
+}
+
+JobStatus JobListModel::getJobStatus(const QModelIndex &index)
{
if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
{
return m_status.value(m_jobs.at(index.row()));
}
- return static_cast<EncodeThread::JobStatus>(-1);
+ return static_cast<JobStatus>(-1);
}
unsigned int JobListModel::getJobProgress(const QModelIndex &index)
return 0;
}
+const OptionsModel *JobListModel::getJobOptions(const QModelIndex &index)
+{
+ static QString nullStr;
+
+ if(index.isValid() && index.row() >= 0 && index.row() < m_jobs.count())
+ {
+ EncodeThread *thread = m_threads.value(m_jobs.at(index.row()));
+ return (thread != NULL) ? thread->options() : NULL;
+ }
+
+ return NULL;
+}
+
QModelIndex JobListModel::getJobIndexById(const QUuid &id)
{
if(m_jobs.contains(id))
// Slots
///////////////////////////////////////////////////////////////////////////////
-void JobListModel::updateStatus(const QUuid &jobId, EncodeThread::JobStatus newStatus)
+void JobListModel::updateStatus(const QUuid &jobId, JobStatus newStatus)
{
int index = -1;
{
m_status.insert(jobId, newStatus);
emit dataChanged(createIndex(index, 0), createIndex(index, 1));
+
+ if(m_preferences->getEnableSounds())
+ {
+ switch(newStatus)
+ {
+ case JobStatus_Completed:
+ MUtils::Sound::play_sound("tada", true);
+ break;
+ case JobStatus_Aborted:
+ MUtils::Sound::play_sound("shattering", true);
+ break;
+ case JobStatus_Failed:
+ MUtils::Sound::play_sound("failure", true);
+ break;
+ }
+ }
}
}
emit dataChanged(createIndex(index, 3), createIndex(index, 3));
}
}
+
+size_t JobListModel::saveQueuedJobs(void)
+{
+ const QString appDir = x264_data_path();
+ QSettings settings(QString("%1/queue.ini").arg(appDir), QSettings::IniFormat);
+
+ settings.clear();
+ settings.setValue(KEY_ENTRY_COUNT, 0);
+ size_t jobCounter = 0;
+
+ for(QList<QUuid>::ConstIterator iter = m_jobs.constBegin(); iter != m_jobs.constEnd(); iter++)
+ {
+ if(m_status.value(*iter) == JobStatus_Enqueued)
+ {
+ if(const EncodeThread *thread = m_threads.value(*iter))
+ {
+ settings.beginGroup(QString().sprintf(JOB_TEMPLATE, jobCounter++));
+ settings.setValue(KEY_SOURCE_FILE, thread->sourceFileName());
+ settings.setValue(KEY_OUTPUT_FILE, thread->outputFileName());
+
+ settings.beginGroup(KEY_ENC_OPTIONS);
+ OptionsModel::saveOptions(thread->options(), settings);
+
+ settings.endGroup();
+ settings.endGroup();
+
+ settings.setValue(KEY_ENTRY_COUNT, jobCounter);
+ }
+ }
+ }
+
+ settings.sync();
+ return jobCounter;
+}
+
+size_t JobListModel::loadQueuedJobs(const SysinfoModel *sysinfo)
+{
+ const QString appDir = x264_data_path();
+ QSettings settings(QString("%1/queue.ini").arg(appDir), QSettings::IniFormat);
+
+ bool ok = false;
+ const size_t jobCounter = settings.value(KEY_ENTRY_COUNT, 0).toUInt(&ok);
+
+ if((!ok) || (jobCounter < 1))
+ {
+ return 0;
+ }
+
+ const QStringList groups = settings.childGroups();
+ for(size_t i = 0; i < jobCounter; i++)
+ {
+ if(!groups.contains(QString().sprintf(JOB_TEMPLATE, i)))
+ {
+ return 0;
+ }
+ }
+
+ size_t jobsCreated = 0;
+ for(size_t i = 0; i < jobCounter; i++)
+ {
+ settings.beginGroup(QString().sprintf(JOB_TEMPLATE, i));
+ const QString sourceFileName = settings.value(KEY_SOURCE_FILE, QString()).toString().trimmed();
+ const QString outputFileName = settings.value(KEY_OUTPUT_FILE, QString()).toString().trimmed();
+
+ if(sourceFileName.isEmpty() || outputFileName.isEmpty())
+ {
+ settings.endGroup();
+ continue;
+ }
+
+ settings.beginGroup(KEY_ENC_OPTIONS);
+ OptionsModel options(sysinfo);
+ const bool okay = OptionsModel::loadOptions(&options, settings);
+
+ settings.endGroup();
+ settings.endGroup();
+
+ if(okay)
+ {
+ EncodeThread *thread = new EncodeThread(sourceFileName, outputFileName, &options, sysinfo, m_preferences);
+ insertJob(thread);
+ jobsCreated++;
+ }
+ }
+
+ return jobsCreated;
+}
+
+void JobListModel::clearQueuedJobs(void)
+{
+ const QString appDir = x264_data_path();
+ QSettings settings(QString("%1/queue.ini").arg(appDir), QSettings::IniFormat);
+ settings.clear();
+ settings.setValue(KEY_ENTRY_COUNT, 0);
+ settings.sync();
+}