This commit is contained in:
Victor Arvidsson 2024-10-24 15:54:53 +13:00 committed by GitHub
commit 1a8b831ebf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 535 additions and 15 deletions

View file

@ -1,3 +1,4 @@
desktop: update "Save dive data as subtitles" feature to make it more configurable.
bluetooth: fix crash on MacOS when doing first download from a new BLE device
statistics: show proper dates in January
desktop: add country to the fields indexed for full text search

View file

@ -113,6 +113,7 @@ struct preferences {
bool extract_video_thumbnails;
int extract_video_thumbnails_position; // position in stream: 0=first 100=last second
std::string ffmpeg_executable; // path of ffmpeg binary
std::string subtitles_format_string; // Format string for subtitles generated from the dive data
int defaultsetpoint; // default setpoint in mbar
std::string default_filename;
enum def_file_behavior default_file_behavior;

View file

@ -6,7 +6,9 @@
#include "core/errorhelper.h"
#include "core/file.h"
#include "core/format.h"
#include "core/gettext.h"
#include "core/membuffer.h"
#include "core/pref.h"
#include "core/subsurface-string.h"
#include "core/version.h"
#include <errno.h>
@ -45,6 +47,16 @@ static std::string video_time(int secs)
return format_string_std("%d:%02d:%02d.000,", hours, mins, secs);
}
void replace_all(std::string& str, const std::string& old_value, const std::string& new_value) {
if (old_value.empty())
return;
size_t start_pos = std::string::npos;
while ((start_pos = str.find(old_value, start_pos)) != std::string::npos) {
str.replace(start_pos, old_value.length(), new_value);
start_pos += new_value.length(); // In case 'new_value' contains 'old_value', like replacing 'x' with 'yx'
}
}
static void put_pd(struct membuffer *b, const struct plot_info &pi, int idx)
{
const struct plot_data &entry = pi.entry[idx];
@ -175,32 +187,214 @@ static std::string format_st_event(const plot_data &entry, const plot_data &next
double value;
int decimals;
const char *unit;
if (entry.sec < offset || entry.sec > offset + length)
if (next_entry.sec < offset || entry.sec > offset + length)
return {};
std::string res = "Dialogue: 0,";
res += video_time(entry.sec - offset);
res += video_time(next_entry.sec - offset < length ? next_entry.sec - offset : length);
res += video_time(std::max(entry.sec - offset, 0));
res += video_time(std::min(next_entry.sec - offset, length));
res += "Default,,0,0,0,,";
res += format_string_std("%d:%02d ", FRACTION_TUPLE(entry.sec, 60));
std::string format_string = prefs.subtitles_format_string;
replace_all(format_string, "[time]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.sec, 60)));
value = get_depth_units(entry.depth, &decimals, &unit);
res += format_string_std("D=%02.2f %s ", value, unit);
replace_all(format_string, "[depth]", format_string_std("%02.2f %s", value, unit));
if (entry.temperature) {
value = get_temp_units(entry.temperature, &unit);
res += format_string_std("T=%.1f%s ", value, unit);
replace_all(format_string,"[temperature]", format_string_std("%.1f%s", value, unit));
} else {
replace_all(format_string, "[temperature]", "");
}
// Only show NDL if it is not essentially infinite, show TTS for mandatory stops.
if (entry.ndl_calc < 3600) {
if (entry.ndl_calc > 0)
res += format_string_std("NDL=%d:%02d ", FRACTION_TUPLE(entry.ndl_calc, 60));
else
if (entry.tts_calc > 0)
res += format_string_std("TTS=%d:%02d ", FRACTION_TUPLE(entry.tts_calc, 60));
if (entry.ceiling) {
value = get_depth_units(entry.ceiling, &decimals, &unit);
replace_all(format_string,"[ceiling]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[ceiling]", "");
}
if (entry.ndl > 0) {
if (entry.ndl < 7200) {
replace_all(format_string,"[ndl]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.ndl, 60)));
} else {
replace_all(format_string,"[ndl]", ">2h");
}
} else {
replace_all(format_string, "[ndl]", "");
}
if (entry.tts > 0) {
replace_all(format_string,"[tts]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.tts, 60)));
} else {
replace_all(format_string, "[tts]", "");
}
if (entry.rbt > 0) {
replace_all(format_string,"[rbt]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.rbt, 60)));
} else {
replace_all(format_string, "[rbt]", "");
}
if (entry.stoptime > 0) {
replace_all(format_string,"[stoptime]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.stoptime, 60)));
} else {
replace_all(format_string, "[stoptime]", "");
}
if (entry.stopdepth > 0) {
value = get_depth_units(entry.stopdepth, &decimals, &unit);
replace_all(format_string, "[stopdepth]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[stopdepth]", "");
}
replace_all(format_string, "[cns]", format_string_std("%u%%", entry.cns));
if (entry.sac > 0) {
value = get_volume_units(entry.sac, &decimals, &unit);
replace_all(format_string, "[sac]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[sac]", "");
}
if (entry.pressures.o2 > 0) {
replace_all(format_string, "[p_o2]", format_string_std("%.2fbar", entry.pressures.o2));
} else {
replace_all(format_string, "[p_o2]", "");
}
if (entry.pressures.n2 > 0) {
replace_all(format_string, "[p_n2]", format_string_std("%.2fbar", entry.pressures.n2));
} else {
replace_all(format_string, "[p_n2]", "");
}
if (entry.pressures.he > 0) {
replace_all(format_string, "[p_he]", format_string_std("%.2fbar", entry.pressures.he));
} else {
replace_all(format_string, "[p_he]", "");
}
if (entry.o2pressure.mbar > 0) {
replace_all(format_string, "[o2_pressure]", format_string_std("%.2fbar", entry.o2pressure.mbar/1000.0));
} else {
replace_all(format_string, "[o2_pressure]", "");
}
if (entry.o2setpoint.mbar > 0) {
replace_all(format_string, "[o2_setpoint]", format_string_std("%.2fbar", entry.o2setpoint.mbar/1000.0));
} else {
replace_all(format_string, "[o2_setpoint]", "");
}
if (entry.scr_OC_pO2.mbar > 0 && entry.pressures.o2 > 0) {
replace_all(format_string, "[scr_oc_po2]", format_string_std("%.2fbar", entry.scr_OC_pO2.mbar/1000.0 - entry.pressures.o2));
} else {
replace_all(format_string, "[scr_oc_po2]", "");
}
if (entry.mod > 0) {
value = get_depth_units(entry.mod, &decimals, &unit);
replace_all(format_string, "[mod]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[mod]", "");
}
if (entry.ead > 0) {
value = get_depth_units(entry.ead, &decimals, &unit);
replace_all(format_string, "[ead]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[ead]", "");
}
if (entry.end > 0) {
value = get_depth_units(entry.end, &decimals, &unit);
replace_all(format_string, "[end]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[end]", "");
}
if (entry.eadd > 0) {
value = get_depth_units(entry.eadd, &decimals, &unit);
replace_all(format_string, "[eadd]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[eadd]", "");
}
value = get_vertical_speed_units(entry.speed, &decimals, &unit);
if (entry.speed > 0)
/* Ascending speeds are positive, descending are negative */
value *= -1;
replace_all(format_string, "[speed]", format_string_std("%02.2f %s", value, unit));
if (entry.in_deco) {
replace_all(format_string, "[in_deco]", translate("gettextFromC", "In deco"));
} else {
replace_all(format_string, "[in_deco]", "");
}
if (entry.ndl_calc > 0) {
if (entry.ndl_calc < 7200) {
replace_all(format_string,"[ndl_calc]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.ndl_calc, 60)));
} else {
replace_all(format_string,"[ndl_calc]", ">2h");
}
} else {
replace_all(format_string, "[ndl_calc]", "");
}
if (entry.tts_calc > 0) {
replace_all(format_string,"[tts_calc]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.tts_calc, 60)));
} else {
replace_all(format_string, "[tts_calc]", "");
}
if (entry.stoptime_calc > 0) {
replace_all(format_string,"[stoptime_calc]", format_string_std("%d:%02d", FRACTION_TUPLE(entry.stoptime_calc, 60)));
} else {
replace_all(format_string, "[stoptime_calc]", "");
}
if (entry.stopdepth_calc > 0) {
value = get_depth_units(entry.stopdepth_calc, &decimals, &unit);
replace_all(format_string, "[stopdepth_calc]", format_string_std("%02.2f %s", value, unit));
} else {
replace_all(format_string, "[stopdepth_calc]", "");
}
if (entry.heartbeat > 0) {
replace_all(format_string, "[heartrate]", format_string_std("%d", entry.heartbeat));
} else {
replace_all(format_string, "[heartrate]", "");
}
if (entry.surface_gf > 0.0) {
res += format_string_std("sGF=%.1f%% ", entry.surface_gf);
replace_all(format_string, "[surface_gf]", format_string_std("%.1f%%", entry.surface_gf));
} else {
replace_all(format_string, "[surface_gf]", "");
}
if (entry.current_gf > 0.0) {
replace_all(format_string, "[current_gf]", format_string_std("%.1f%%", entry.current_gf));
} else {
replace_all(format_string, "[current_gf]", "");
}
if (entry.density > 0) {
replace_all(format_string, "[density]", format_string_std("%.1fg/", entry.density));
} else {
replace_all(format_string, "[density]", "");
}
if (entry.icd_warning) {
replace_all(format_string, "[icd_warning]", translate("gettextFromC", "ICD in leading tissue"));
} else {
replace_all(format_string, "[icd_warning]", "");
}
res += format_string;
res += "\n";
return res;
}

View file

@ -15,6 +15,7 @@ void qPrefMedia::loadSync(bool doSync)
disk_extract_video_thumbnails(doSync);
disk_extract_video_thumbnails_position(doSync);
disk_ffmpeg_executable(doSync);
disk_subtitles_format_string(doSync);
disk_auto_recalculate_thumbnails(doSync);
disk_auto_recalculate_thumbnails(doSync);
}
@ -23,3 +24,4 @@ HANDLE_PREFERENCE_BOOL(Media, "auto_recalculate_thumbnails", auto_recalculate_th
HANDLE_PREFERENCE_BOOL(Media, "extract_video_thumbnails", extract_video_thumbnails);
HANDLE_PREFERENCE_INT(Media, "extract_video_thumbnails_position", extract_video_thumbnails_position);
HANDLE_PREFERENCE_TXT(Media, "ffmpeg_executable", ffmpeg_executable);
HANDLE_PREFERENCE_TXT(Media, "subtitles_format_string", subtitles_format_string);

View file

@ -11,6 +11,7 @@ class qPrefMedia : public QObject {
Q_PROPERTY(bool extract_video_thumbnails READ extract_video_thumbnails WRITE set_extract_video_thumbnails NOTIFY extract_video_thumbnailsChanged)
Q_PROPERTY(int extract_video_thumbnails_position READ extract_video_thumbnails_position WRITE set_extract_video_thumbnails_position NOTIFY extract_video_thumbnails_positionChanged)
Q_PROPERTY(QString ffmpeg_executable READ ffmpeg_executable WRITE set_ffmpeg_executable NOTIFY ffmpeg_executableChanged)
Q_PROPERTY(QString subtitles_format_string READ subtitles_format_string WRITE set_subtitles_format_string NOTIFY subtitles_format_stringChanged)
public:
static qPrefMedia *instance();
@ -25,18 +26,21 @@ public:
static bool extract_video_thumbnails() { return prefs.extract_video_thumbnails; }
static int extract_video_thumbnails_position() { return prefs.extract_video_thumbnails_position; }
static QString ffmpeg_executable() { return QString::fromStdString(prefs.ffmpeg_executable); }
static QString subtitles_format_string() { return QString::fromStdString(prefs.subtitles_format_string); }
public slots:
static void set_auto_recalculate_thumbnails(bool value);
static void set_extract_video_thumbnails(bool value);
static void set_extract_video_thumbnails_position(int value);
static void set_ffmpeg_executable(const QString& value);
static void set_subtitles_format_string(const QString& value);
signals:
void auto_recalculate_thumbnailsChanged(bool value);
void extract_video_thumbnailsChanged(bool value);
void extract_video_thumbnails_positionChanged(int value);
void ffmpeg_executableChanged(const QString& value);
void subtitles_format_stringChanged(const QString& value);
private:
qPrefMedia() {}
@ -45,6 +49,7 @@ private:
static void disk_extract_video_thumbnails(bool doSync);
static void disk_extract_video_thumbnails_position(bool doSync);
static void disk_ffmpeg_executable(bool doSync);
static void disk_subtitles_format_string(bool doSync);
};

View file

@ -197,6 +197,7 @@ void setup_system_prefs()
default_prefs.divelist_font = system_divelist_default_font;
default_prefs.font_size = system_divelist_default_font_size;
default_prefs.ffmpeg_executable = "ffmpeg";
default_prefs.subtitles_format_string = "[time] D=[depth] T=[temperature] sGF=[surface_gf]";
#if !defined(SUBSURFACE_MOBILE)
default_prefs.default_filename = system_default_filename();

View file

@ -71,6 +71,7 @@ void PreferencesMedia::refreshSettings()
ui->extractVideoThumbnails->setChecked(qPrefMedia::extract_video_thumbnails());
ui->videoThumbnailPosition->setValue(qPrefMedia::extract_video_thumbnails_position());
ui->ffmpegExecutable->setText(qPrefMedia::ffmpeg_executable());
ui->subtitlesFormatString->setText(qPrefMedia::subtitles_format_string());
ui->auto_recalculate_thumbnails->setChecked(prefs.auto_recalculate_thumbnails);
}
@ -81,5 +82,6 @@ void PreferencesMedia::syncSettings()
media->set_extract_video_thumbnails(ui->extractVideoThumbnails->isChecked());
media->set_extract_video_thumbnails_position(ui->videoThumbnailPosition->value());
media->set_ffmpeg_executable(ui->ffmpegExecutable->text());
media->set_subtitles_format_string(ui->subtitlesFormatString->text());
qPrefMedia::set_auto_recalculate_thumbnails(ui->auto_recalculate_thumbnails->isChecked());
}

View file

@ -170,6 +170,319 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_11">
<property name="title">
<string>Dive data as subtitles</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>5</number>
</property>
<property name="margin">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="subtitlesFormatStringLabel">
<property name="text">
<string>Subtitle format string:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="subtitlesFormatString"/>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_help_video_4">
<property name="toolTip">
<string extracomment="Help info 4"/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="text">
<string>Available tags:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="subtitlesTimeLabel">
<property name="text">
<string>Dive time: [time]</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="subtitlesDepthLabel">
<property name="text">
<string>Depth: [depth]</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="subtitlesTemperatureLabel">
<property name="text">
<string>Temperature: [temperature]</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="subtitlesCeilingLabel">
<property name="text">
<string>Ceiling: [ceiling]</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="subtitlesNdlLabel">
<property name="text">
<string>NDL: [ndl]</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="subtitlesTtsLabel">
<property name="text">
<string>TTS: [tts]</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="subtitlesRbtLabel">
<property name="text">
<string>RBT: [rbt]</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="subtitlesStoptimeLabel">
<property name="text">
<string>Stop time: [stoptime]</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="subtitlesStopdepthLabel">
<property name="text">
<string>Stop depth: [stopdepth]</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="subtitlesCnsLabel">
<property name="text">
<string>CNS: [cns]</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="subtitlesSacLabel">
<property name="text">
<string>SAC: [sac]</string>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="subtitlesPO2Label">
<property name="text">
<string>pO₂: [p_o2]</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="subtitlesPN2Label">
<property name="text">
<string>pN₂: [p_n2]</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="subtitlesPHeLabel">
<property name="text">
<string>pHe: [p_he]</string>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="subtitlesO2pressureLabel">
<property name="text">
<string>O₂ pressure (rebreather): [o2_pressure]</string>
</property>
</widget>
</item>
<item row="17" column="0">
<widget class="QLabel" name="subtitlesO2setpointLabel">
<property name="text">
<string>O₂ setpoint: [o2_setpoint]</string>
</property>
</widget>
</item>
<item row="18" column="0">
<widget class="QLabel" name="subtitlesScr_oc_p02Label">
<property name="text">
<string>SCR ΔpO₂: [scr_oc_po2]</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="subtitlesModLabel">
<property name="text">
<string>MOD: [mod]</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="subtitlesEadLabel">
<property name="text">
<string>EAD: [ead]</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="subtitlesEndLabel">
<property name="text">
<string>END: [end]</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="subtitlesEaddLabel">
<property name="text">
<string>EADD: [eadd]</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="subtitlesSpeedLabel">
<property name="text">
<string>Vertical speed: [speed]</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="subtitlesIn_deco_calcLabel">
<property name="text">
<string>In deco (calculated): [in_deco]</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="subtitlesNdlCalcLabel">
<property name="text">
<string>NDL (calculated): [ndl_calc]</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="subtitlesTts_calcLabel">
<property name="text">
<string>TTS (calculated): [tts_calc]</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="subtitlesStoptime_calcLabel">
<property name="text">
<string>Stop time (calculated): [stoptime_calc]</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLabel" name="subtitlesStopdepth_calcLabel">
<property name="text">
<string>Stop depth (calculated): [stopdepth_calc]</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QLabel" name="subtitlesHeartrateLabel">
<property name="text">
<string>Heartrate: [heartrate]</string>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QLabel" name="subtitlesBearingLabel">
<property name="text">
<string>Bearing: [bearing]</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QLabel" name="subtitlesSurface_gfLabel">
<property name="text">
<string>Surface GF: [surface_gf]</string>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QLabel" name="subtitlesCurrent_gfLabel">
<property name="text">
<string>Current GF: [current_gf]</string>
</property>
</widget>
</item>
<item row="16" column="1">
<widget class="QLabel" name="subtitlesDensityLabel">
<property name="text">
<string>Density: [density]</string>
</property>
</widget>
</item>
<item row="17" column="1">
<widget class="QLabel" name="subtitlesIcd_warningLabel">
<property name="text">
<string>ICD Warning: [icd_warning]</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
@ -186,6 +499,7 @@
</layout>
</widget>
<resources/>
<connections>
</connections>