From 25b3ce850b850a936b79f7ad2f99e5ada8212607 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 12 Apr 2026 01:34:26 -0500 Subject: [PATCH 1/3] feat: add seeding time column to torrent list and details tab --- src/rpc/torrent.cpp | 6 ++++++ src/rpc/torrent.h | 1 + src/ui/screens/mainwindow/torrentsmodel.cpp | 15 +++++++++++++++ src/ui/screens/mainwindow/torrentsmodel.h | 1 + .../torrentproperties/torrentpropertieswidget.cpp | 10 ++++++++++ 5 files changed, 33 insertions(+) diff --git a/src/rpc/torrent.cpp b/src/rpc/torrent.cpp index cb7ad781..a941a348 100644 --- a/src/rpc/torrent.cpp +++ b/src/rpc/torrent.cpp @@ -62,6 +62,7 @@ namespace tremotesf { Error, ErrorString, ActivityDate, + SecondsSeeding, DoneDate, PeersLimit, HonorSessionLimits, @@ -151,6 +152,8 @@ namespace tremotesf { return "errorString"_L1; case TorrentData::UpdateKey::ActivityDate: return "activityDate"_L1; + case TorrentData::UpdateKey::SecondsSeeding: + return "secondsSeeding"_L1; case TorrentData::UpdateKey::DoneDate: return "doneDate"_L1; case TorrentData::UpdateKey::PeersLimit: @@ -357,6 +360,9 @@ namespace tremotesf { case TorrentData::UpdateKey::TotalUploaded: setChanged(totalUploaded, value.toInteger(), changed); return; + case TorrentData::UpdateKey::SecondsSeeding: + setChanged(secondsSeeding, value.toInteger(), changed); + return; case TorrentData::UpdateKey::Ratio: setChanged(ratio, value.toDouble(), changed); return; diff --git a/src/rpc/torrent.h b/src/rpc/torrent.h index 29895055..21af3d87 100644 --- a/src/rpc/torrent.h +++ b/src/rpc/torrent.h @@ -92,6 +92,7 @@ namespace tremotesf { qint64 totalDownloaded{}; qint64 totalUploaded{}; + qint64 secondsSeeding{}; double ratio{}; double ratioLimit{}; RatioLimitMode ratioLimitMode{}; diff --git a/src/ui/screens/mainwindow/torrentsmodel.cpp b/src/ui/screens/mainwindow/torrentsmodel.cpp index 89026b87..17e3dcb3 100644 --- a/src/ui/screens/mainwindow/torrentsmodel.cpp +++ b/src/ui/screens/mainwindow/torrentsmodel.cpp @@ -223,6 +223,15 @@ namespace tremotesf { QLocale::ShortFormat, mUseRelativeTime ); + case Column::SecondsSeeding: { + const auto secs = torrent->data().secondsSeeding; + const auto d = secs / 86400; + const auto h = (secs % 86400) / 3600; + const auto m = (secs % 3600) / 60; + const auto s = secs % 60; + //: Torrents list column data, seeding time + return qApp->translate("tremotesf", "%1d, %2h, %3m, %4s").arg(d).arg(h).arg(m).arg(s); + } default: break; } @@ -234,6 +243,7 @@ namespace tremotesf { case Column::AddedDate: case Column::DoneDate: case Column::ActivityDate: + case Column::SecondsSeeding: return data(index, Qt::DisplayRole); case Column::DownloadDirectory: return torrent->data().downloadDirectory; @@ -294,6 +304,8 @@ namespace tremotesf { return torrent->data().completedSize; case Column::ActivityDate: return torrent->data().activityDate; + case Column::SecondsSeeding: + return torrent->data().secondsSeeding; default: return data(index, Qt::DisplayRole); } @@ -386,6 +398,9 @@ namespace tremotesf { case Column::ActivityDate: //: Torrents list column name return qApp->translate("tremotesf", "Last Activity"); + case Column::SecondsSeeding: + //: Torrents list column name + return qApp->translate("tremotesf", "Seeding Time"); default: return {}; } diff --git a/src/ui/screens/mainwindow/torrentsmodel.h b/src/ui/screens/mainwindow/torrentsmodel.h index 96212e78..29d69464 100644 --- a/src/ui/screens/mainwindow/torrentsmodel.h +++ b/src/ui/screens/mainwindow/torrentsmodel.h @@ -47,6 +47,7 @@ namespace tremotesf { LeftUntilDone, DownloadDirectory, CompletedSize, + SecondsSeeding, ActivityDate }; Q_ENUM(Column) diff --git a/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp b/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp index 11b6cdbe..6b1a349e 100644 --- a/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp +++ b/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp @@ -163,6 +163,8 @@ namespace tremotesf { ); auto lastActivityLabel = new QLabel(this); activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Last activity:"), lastActivityLabel); + auto seedingTimeLabel = new QLabel(this); + activityGroupBoxLayout->addRow(qApp->translate("tremotesf", "Seeding time:"), seedingTimeLabel); detailsTabLayout->addWidget(activityGroupBox); //: Torrent's details tab section @@ -238,6 +240,13 @@ namespace tremotesf { lastActivityLabel->setText( formatutils::formatDateTime(mTorrent->data().activityDate.toLocalTime(), QLocale::LongFormat) ); + + const auto secs = mTorrent->data().secondsSeeding; + const auto d = secs / 86400; + const auto h = (secs % 86400) / 3600; + const auto m = (secs % 3600) / 60; + const auto s = secs % 60; + seedingTimeLabel->setText(qApp->translate("tremotesf", "%1d, %2h, %3m, %4s").arg(d).arg(h).arg(m).arg(s)); totalSizeLabel->setText(formatutils::formatByteSize(mTorrent->data().totalSize)); locationLabel->setText( @@ -267,6 +276,7 @@ namespace tremotesf { webSeedersSendingToUsLabel->clear(); peersGettingFromUsLabel->clear(); lastActivityLabel->clear(); + seedingTimeLabel->clear(); totalSizeLabel->clear(); locationLabel->clear(); hashLabel->clear(); From 6b22e2d79ab5e428979c5022d7458eaa95275545 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 1 Jul 2026 02:00:19 -0500 Subject: [PATCH 2/3] Add Seeding Time column --- src/formatutils.cpp | 15 +++++++++++++++ src/formatutils.h | 2 +- src/ui/screens/mainwindow/torrentsmodel.cpp | 10 +++------- .../torrentproperties/torrentpropertieswidget.cpp | 11 ++++------- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/formatutils.cpp b/src/formatutils.cpp index 7d9637e3..aa3dcdc8 100644 --- a/src/formatutils.cpp +++ b/src/formatutils.cpp @@ -166,6 +166,21 @@ namespace tremotesf::formatutils { return formatRatio(static_cast(uploaded) / static_cast(downloaded)); } + std::optional formatElapsedTime(int seconds) { + if (seconds < 0) { + return std::nullopt; + } + + const auto d = seconds / 86400; + const auto h = (seconds % 86400) / 3600; + const auto m = (seconds % 3600) / 60; + const auto s = seconds % 60; + + // Use your existing translation string or the one from the PR + return qApp->translate("tremotesf", "%1d, %2h, %3m, %4s") + .arg(d).arg(h).arg(m).arg(s); + } + QString formatEta(int seconds) { if (seconds < 0) { return "\u221E"; diff --git a/src/formatutils.h b/src/formatutils.h index de82bd8e..08c1216d 100644 --- a/src/formatutils.h +++ b/src/formatutils.h @@ -18,7 +18,7 @@ namespace tremotesf::formatutils { QString formatProgress(double progress); QString formatRatio(double ratio); QString formatRatio(long long downloaded, long long uploaded); - + std::optional formatElapsedTime(int seconds); QString formatEta(int seconds); QString formatDateTime(const QDateTime& dateTime, QLocale::FormatType format, bool displayRelativeTime); diff --git a/src/ui/screens/mainwindow/torrentsmodel.cpp b/src/ui/screens/mainwindow/torrentsmodel.cpp index 17e3dcb3..82bf9777 100644 --- a/src/ui/screens/mainwindow/torrentsmodel.cpp +++ b/src/ui/screens/mainwindow/torrentsmodel.cpp @@ -224,13 +224,9 @@ namespace tremotesf { mUseRelativeTime ); case Column::SecondsSeeding: { - const auto secs = torrent->data().secondsSeeding; - const auto d = secs / 86400; - const auto h = (secs % 86400) / 3600; - const auto m = (secs % 3600) / 60; - const auto s = secs % 60; - //: Torrents list column data, seeding time - return qApp->translate("tremotesf", "%1d, %2h, %3m, %4s").arg(d).arg(h).arg(m).arg(s); + if (const auto formattedTime = formatutils::formatElapsedTime(torrent->data().secondsSeeding)) { + return *formattedTime; + } } default: break; diff --git a/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp b/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp index 6b1a349e..3556280a 100644 --- a/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp +++ b/src/ui/screens/torrentproperties/torrentpropertieswidget.cpp @@ -241,12 +241,9 @@ namespace tremotesf { formatutils::formatDateTime(mTorrent->data().activityDate.toLocalTime(), QLocale::LongFormat) ); - const auto secs = mTorrent->data().secondsSeeding; - const auto d = secs / 86400; - const auto h = (secs % 86400) / 3600; - const auto m = (secs % 3600) / 60; - const auto s = secs % 60; - seedingTimeLabel->setText(qApp->translate("tremotesf", "%1d, %2h, %3m, %4s").arg(d).arg(h).arg(m).arg(s)); + seedingTimeLabel->setText( + formatutils::formatElapsedTime(mTorrent->data().secondsSeeding).value_or(QString{}) + ); totalSizeLabel->setText(formatutils::formatByteSize(mTorrent->data().totalSize)); locationLabel->setText( @@ -276,7 +273,7 @@ namespace tremotesf { webSeedersSendingToUsLabel->clear(); peersGettingFromUsLabel->clear(); lastActivityLabel->clear(); - seedingTimeLabel->clear(); + seedingTimeLabel->clear(); totalSizeLabel->clear(); locationLabel->clear(); hashLabel->clear(); From 00962c204c3f45624fe1572d5956e9d631268b6d Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 2 Jul 2026 22:56:07 -0500 Subject: [PATCH 3/3] Standardize same formatting for seedingTime --- src/formatutils.cpp | 29 +-------------------- src/ui/screens/mainwindow/torrentsmodel.cpp | 1 + 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/src/formatutils.cpp b/src/formatutils.cpp index aa3dcdc8..adfeab0b 100644 --- a/src/formatutils.cpp +++ b/src/formatutils.cpp @@ -182,34 +182,7 @@ namespace tremotesf::formatutils { } QString formatEta(int seconds) { - if (seconds < 0) { - return "\u221E"; - } - - const int days = seconds / 86400; - seconds %= 86400; - const int hours = seconds / 3600; - seconds %= 3600; - const int minutes = seconds / 60; - seconds %= 60; - - if (days > 0) { - //: Remaining time string. %L1 is days, %L2 is hours, e.g. "2 d 5 h" - return qApp->translate("tremotesf", "%L1 d %L2 h").arg(days).arg(hours); - } - - if (hours > 0) { - //: Remaining time string. %L1 is hours, %L2 is minutes, e.g. "2 h 5 m" - return qApp->translate("tremotesf", "%L1 h %L2 m").arg(hours).arg(minutes); - } - - if (minutes > 0) { - //: Remaining time string. %L1 is minutes, %L2 is seconds, e.g. "2 m 5 s" - return qApp->translate("tremotesf", "%L1 m %L2 s").arg(minutes).arg(seconds); - } - - //: Remaining time string. %L1 is seconds, "10 s" - return qApp->translate("tremotesf", "%L1 s").arg(seconds); + return formatElapsedTime(seconds).value_or(QStringLiteral("\u221E")); } namespace { diff --git a/src/ui/screens/mainwindow/torrentsmodel.cpp b/src/ui/screens/mainwindow/torrentsmodel.cpp index 82bf9777..0b286da2 100644 --- a/src/ui/screens/mainwindow/torrentsmodel.cpp +++ b/src/ui/screens/mainwindow/torrentsmodel.cpp @@ -227,6 +227,7 @@ namespace tremotesf { if (const auto formattedTime = formatutils::formatElapsedTime(torrent->data().secondsSeeding)) { return *formattedTime; } + break; } default: break;