UI: Add file formatting options for recording

Allows the user to specify custom formatting for their recording file
names with many formatting options, viewed via tooltip.  The options
have been added to the advanced settings section.

Closes jp9000/obs-studio#507
master
bl 2016-03-25 02:43:38 -07:00 committed by jp9000
parent 086e3f4a09
commit 50961861c7
6 changed files with 125 additions and 5 deletions

View File

@ -404,6 +404,8 @@ Basic.Settings.Output.Adv.Recording.Type="Type"
Basic.Settings.Output.Adv.Recording.Type.Standard="Standard"
Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Custom Output (FFmpeg)"
Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Use stream encoder)"
Basic.Settings.Output.Adv.Recording.Filename="Filename Formatting"
Basic.Settings.Output.Adv.Recording.OverwriteIfExists="Overwrite if file exists"
Basic.Settings.Output.Adv.FFmpeg.Type="FFmpeg Output Type"
Basic.Settings.Output.Adv.FFmpeg.Type.URL="Output to URL"
Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile="Output to File"
@ -424,6 +426,12 @@ Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audio Encoder"
Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Audio Encoder Settings (if any)"
Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxer Settings (if any)"
# basic mode 'output' settings - advanced section - recording subsection - completer
FilenameFormatting.completer="%yyyy-%mm-%dd %hh-%mm-%ss\n%yy-%mm-%dd %hh-%mm-%ss\n%Y-%m-%d %H-%M-%S\n%y-%m-%d %H-%M-%S\n%a %Y-%m-%d %H-%M-%S\n%A %Y-%m-%d %H-%M-%S\n%Y-%b-%d %H-%M-%S\n%Y-%B-%d %H-%M-%S\n%Y-%m-%d %I-%M-%S-%p\n%Y-%m-%d %H-%M-%S-%z\n%Y-%m-%d %H-%M-%S-%Z"
# basic mode 'output' settings - advanced section - recording subsection - TT
FilenameFormatting.TT="%yyyy Year, four digits\n%yy Year, last two digits (00-99)\n%mm Month as a decimal number (01-12)\n%dd Day of the month, zero-padded (01-31)\n%hh Hour in 24h format (00-23)\n%mm Minute (00-59)\n%ss Second (00-61)\n%% A % sign\n%a Abbreviated weekday name\n%A Full weekday name\n%b Abbreviated month name\n%B Full month name\n%d Day of the month, zero-padded (01-31)\n%H Hour in 24h format (00-23)\n%I Hour in 12h format (01-12)\n%m Month as a decimal number (01-12)\n%M Minute (00-59)\n%p AM or PM designation\n%S Second (00-61)\n%y Year, last two digits (00-99)\n%Y Year\n%z ISO 8601 offset from UTC or timezone\n name or abbreviation\n%Z Timezone name or abbreviation\n"
# basic mode 'video' settings
Basic.Settings.Video="Video"
Basic.Settings.Video.Adapter="Video Adapter:"

View File

@ -2953,6 +2953,32 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Basic.Settings.Output.Adv.Recording</string>
</property>
<layout class="QFormLayout" name="formLayout_17">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Basic.Settings.Output.Adv.Recording.Filename</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="filenameFormatting"/>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="overwriteIfExists">
<property name="text">
<string>Basic.Settings.Output.Adv.Recording.OverwriteIfExists</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">

View File

@ -75,6 +75,36 @@ static void OBSStopRecording(void *data, calldata_t *params)
UNUSED_PARAMETER(params);
}
static void FindBestFilename(string &strPath, bool noSpace)
{
int num = 2;
if (!os_file_exists(strPath.c_str()))
return;
const char *ext = strrchr(strPath.c_str(), '.');
if (!ext)
return;
int extStart = int(ext - strPath.c_str());
for (;;) {
string testPath = strPath;
string numStr;
numStr = noSpace ? "_" : " (";
numStr += to_string(num++);
if (!noSpace)
numStr += ")";
testPath.insert(extStart, numStr);
if (!os_file_exists(testPath.c_str())) {
strPath = testPath;
break;
}
}
}
/* ------------------------------------------------------------------------ */
static bool CreateAACEncoder(OBSEncoder &res, string &id, int bitrate,
@ -431,6 +461,10 @@ bool SimpleOutput::StartRecording()
"MuxerCustom");
bool noSpace = config_get_bool(main->Config(), "SimpleOutput",
"FileNameWithoutSpace");
const char *filenameFormat = config_get_string(main->Config(), "Output",
"FilenameFormatting");
bool overwriteIfExists = config_get_bool(main->Config(), "Output",
"OverwriteIfExists");
os_dir_t *dir = path ? os_opendir(path) : nullptr;
@ -450,8 +484,10 @@ bool SimpleOutput::StartRecording()
if (lastChar != '/' && lastChar != '\\')
strPath += "/";
strPath += GenerateTimeDateFilename(ffmpegOutput ? "avi" : format,
noSpace);
strPath += GenerateSpecifiedFilename(ffmpegOutput ? "avi" : format,
noSpace, filenameFormat);
if (!overwriteIfExists)
FindBestFilename(strPath, noSpace);
SetupOutputs();
@ -932,8 +968,10 @@ bool AdvancedOutput::StartStreaming(obs_service_t *service)
bool AdvancedOutput::StartRecording()
{
const char *path;
const char *format;
const char *recFormat;
const char *filenameFormat;
bool noSpace = false;
bool overwriteIfExists = false;
if (!useStreamEncoder) {
if (!ffmpegOutput) {
@ -951,8 +989,12 @@ bool AdvancedOutput::StartRecording()
if (!ffmpegOutput || ffmpegRecording) {
path = config_get_string(main->Config(), "AdvOut",
ffmpegRecording ? "FFFilePath" : "RecFilePath");
format = config_get_string(main->Config(), "AdvOut",
recFormat = config_get_string(main->Config(), "AdvOut",
ffmpegRecording ? "FFExtension" : "RecFormat");
filenameFormat = config_get_string(main->Config(), "Output",
"FilenameFormatting");
overwriteIfExists = config_get_bool(main->Config(), "Output",
"OverwriteIfExists");
noSpace = config_get_bool(main->Config(), "AdvOut",
ffmpegRecording ?
"FFFileNameWithoutSpace" :
@ -976,7 +1018,10 @@ bool AdvancedOutput::StartRecording()
if (lastChar != '/' && lastChar != '\\')
strPath += "/";
strPath += GenerateTimeDateFilename(format, noSpace);
strPath += GenerateSpecifiedFilename(recFormat, noSpace,
filenameFormat);
if (!overwriteIfExists)
FindBestFilename(strPath, noSpace);
obs_data_t *settings = obs_data_create();
obs_data_set_string(settings,

View File

@ -749,6 +749,9 @@ bool OBSBasic::InitBasicConfigDefaults()
config_set_default_uint (basicConfig, "Video", "BaseCX", cx);
config_set_default_uint (basicConfig, "Video", "BaseCY", cy);
config_set_default_string(basicConfig, "Output", "FilenameFormatting",
"%yyyy-%mm-%dd %hh-%mm-%ss");
config_set_default_bool (basicConfig, "Output", "DelayEnable", false);
config_set_default_uint (basicConfig, "Output", "DelaySec", 20);
config_set_default_bool (basicConfig, "Output", "DelayPreserve", true);

View File

@ -22,6 +22,7 @@
#include <graphics/math-defs.h>
#include <initializer_list>
#include <sstream>
#include <QCompleter>
#include <QLineEdit>
#include <QMessageBox>
#include <QCloseEvent>
@ -347,6 +348,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->colorRange, COMBO_CHANGED, ADV_CHANGED);
HookWidget(ui->disableOSXVSync, CHECK_CHANGED, ADV_CHANGED);
HookWidget(ui->resetOSXVSync, CHECK_CHANGED, ADV_CHANGED);
HookWidget(ui->filenameFormatting, EDIT_CHANGED, ADV_CHANGED);
HookWidget(ui->overwriteIfExists, CHECK_CHANGED, ADV_CHANGED);
HookWidget(ui->streamDelayEnable, CHECK_CHANGED, ADV_CHANGED);
HookWidget(ui->streamDelaySec, SCROLL_CHANGED, ADV_CHANGED);
HookWidget(ui->streamDelayPreserve, CHECK_CHANGED, ADV_CHANGED);
@ -1128,6 +1131,14 @@ void OBSBasicSettings::LoadAdvOutputStreamingSettings()
ui->advOutRescale->setEnabled(rescale);
ui->advOutRescale->setCurrentText(rescaleRes);
QStringList specList = QTStr("FilenameFormatting.completer").split(
QRegularExpression("\n"));
QCompleter *specCompleter = new QCompleter(specList);
specCompleter->setCaseSensitivity(Qt::CaseSensitive);
specCompleter->setFilterMode(Qt::MatchContains);
ui->filenameFormatting->setCompleter(specCompleter);
ui->filenameFormatting->setToolTip(QTStr("FilenameFormatting.TT"));
switch (trackIndex) {
case 1: ui->advOutTrack1->setChecked(true); break;
case 2: ui->advOutTrack2->setChecked(true); break;
@ -1664,11 +1675,18 @@ void OBSBasicSettings::LoadAdvancedSettings()
"RetryDelay");
int maxRetries = config_get_int(main->Config(), "Output",
"MaxRetries");
const char *filename = config_get_string(main->Config(), "Output",
"FilenameFormatting");
bool overwriteIfExists = config_get_bool(main->Config(), "Output",
"OverwriteIfExists");
loading = true;
LoadRendererList();
ui->filenameFormatting->setText(filename);
ui->overwriteIfExists->setChecked(overwriteIfExists);
ui->reconnectEnable->setChecked(reconnect);
ui->reconnectRetryDelay->setValue(retryDelay);
ui->reconnectMaxRetries->setValue(maxRetries);
@ -2110,6 +2128,8 @@ void OBSBasicSettings::SaveAdvancedSettings()
SaveCombo(ui->colorFormat, "Video", "ColorFormat");
SaveCombo(ui->colorSpace, "Video", "ColorSpace");
SaveComboData(ui->colorRange, "Video", "ColorRange");
SaveEdit(ui->filenameFormatting, "Output", "FilenameFormatting");
SaveCheckBox(ui->overwriteIfExists, "Output", "OverwriteIfExists");
SaveCheckBox(ui->streamDelayEnable, "Output", "DelayEnable");
SaveSpinBox(ui->streamDelaySec, "Output", "DelaySec");
SaveCheckBox(ui->streamDelayPreserve, "Output", "DelayPreserve");
@ -2690,6 +2710,23 @@ void OBSBasicSettings::RecalcOutputResPixels(const char *resText)
}
}
void OBSBasicSettings::on_filenameFormatting_textEdited(const QString &text)
{
#ifdef __APPLE__
size_t invalidLocation =
text.toStdString().find_first_of(":/\\");
#elif _WIN32
size_t invalidLocation =
text.toStdString().find_first_of("<>:\"/\\|?*");
#else
size_t invalidLocation = text.toStdString().find_first_of("/");
#endif
if (invalidLocation != string::npos)
ui->filenameFormatting->backspace();
}
void OBSBasicSettings::on_outputResolution_editTextChanged(const QString &text)
{
if (!loading)

View File

@ -253,6 +253,7 @@ private slots:
void on_colorFormat_currentIndexChanged(const QString &text);
void on_filenameFormatting_textEdited(const QString &text);
void on_outputResolution_editTextChanged(const QString &text);
void on_baseResolution_editTextChanged(const QString &text);