Skip to content

Conversation

danzkigg
Copy link
Contributor

@danzkigg danzkigg commented Aug 19, 2025

At-a-glance view your total download speed, and the number of torrents currently seeding and leeching.

There are 2 options for this widget:

  1. detailed: with a detailed list for each active torrent currently downloading that shows under the "Show more" button (hidden when there is no active torrents)
  2. basic: just stats from qBittorrent (Current download speed, number of torrents seeding and leeching)

Detailed view

preview3 preview1 preview2

Basic view

preview4

@Panonim
Copy link
Contributor

Panonim commented Aug 21, 2025

Already exists.
The same functionality in the newest update.
image

@danzkigg
Copy link
Contributor Author

Already exists.
The same functionality in the newest update.
image

Not the same, yours is using a middleware/wrapper. This is acts as a native widget without any 3rd party.

@5at0ri
Copy link
Contributor

5at0ri commented Sep 6, 2025

this widget is great! I made some small changes if you would like to make it an option within the widget: swapped out leeching for seeding. Show more when seeding shows upload speed, size and ratio. Also changed to MB/s

    - type: custom-api
      title: qBittorrent
      cache: 10s
      options:
        type: "detailed" # "basic" for basic view | "detailed" for detailed view
      subrequests:
        transfer:
          url: ${QBITTORRENT_URL}/api/v2/transfer/info
        seeding:
          url: ${QBITTORRENT_URL}/api/v2/torrents/info
          parameters:
            filter: seeding
        leeching:
          url: ${QBITTORRENT_URL}/api/v2/torrents/info
          parameters:
            filter: downloading
      template: |
        {{ $transfer := .Subrequest "transfer" }}
        {{ $seeding := .Subrequest "seeding" }}
        {{ $leeching := .Subrequest "leeching" }}

        {{ if and (eq $transfer.Response.StatusCode 200) (eq $seeding.Response.StatusCode 200) (eq $leeching.Response.StatusCode 200) }}

          {{ $isDetailed := eq (.Options.StringOr "type" "detailed") "detailed" }}

          {{ if $isDetailed }}
          <!-- Detailed View -->
            <div class="list" style="--list-gap: 15px;">
            
              <div class="flex justify-between text-center">
                <div>
                  {{ $dlSpeed := $transfer.JSON.Float "dl_info_speed" }}
                  <div class="color-highlight size-h3">{{ printf "%.1f MB/s" (div $dlSpeed 1000000.0) }}</div>
                  <div class="size-h6">DOWNLOADING</div>
                </div>
                <div>
                  {{ $ulSpeed := $transfer.JSON.Float "up_info_speed" }}
                  <div class="color-highlight size-h3">{{ printf "%.1f MB/s" (div $ulSpeed 1000000.0) }}</div>
                  <div class="size-h6">UPLOADING</div>
                </div>
                <div>
                  <div class="color-highlight size-h3">{{ len ($seeding.JSON.Array "") }}</div>
                  <div class="size-h6">SEEDING</div>
                </div>
              </div>

              {{ $downloadingTorrents := $leeching.JSON.Array "" }}
              {{ if gt (len $downloadingTorrents) 0 }}
                <div>
                  <div class="size-h5 color-primary margin-bottom-10">Downloading</div>
                  <ul class="list collapsible-container" data-collapse-after="0" style="--list-gap: 15px;">
                    {{ range $t := $downloadingTorrents }}
                      <li class="flex items-center" style="gap: 10px;">
                        {{ $state := $t.String "state" }}
                        {{ $icon := "❔" }}
                        {{ if ge ($t.Int "completed") ($t.Int "size") }}{{ $icon = "✔" }}
                        {{ else if eq $state "downloading" "forcedDL" }}{{ $icon = "↓" }}
                        {{ else if eq $state "pausedDL" "stoppedDL" "pausedUP" "stalledDL" "stalledUP" "queuedDL" "queuedUP" }}{{ $icon = "❚❚" }}
                        {{ else if eq $state "error" "missingFiles" }}{{ $icon = "!" }}
                        {{ else if eq $state "checkingDL" "checkingUP" "allocating" }}{{ $icon = "…" }}
                        {{ else if eq $state "checkingResumeData" }}{{ $icon = "⟳" }}
                        {{ end }}
                        <div class="size-h4" style="flex-shrink: 0;">{{ $icon }}</div>
                                  
                        <div style="flex-grow: 1; min-width: 0;">
                          <div class="text-truncate color-highlight">{{ $t.String "name" }}</div>
                          <div title="{{ $t.Float "progress" | mul 100 | printf "%.1f" }}%" style="background: rgba(128, 128, 128, 0.2); border-radius: 5px; height: 6px; margin-top: 5px; overflow: hidden;">
                            <div style="width: {{ $t.Float "progress" | mul 100 }}%; background-color: var(--color-positive); height: 100%; border-radius: 5px;"></div>
                          </div>
                        </div>

                        <div style="flex-shrink: 0; text-align: right; width: 80px;">
                          {{ $dlSpeed := $t.Float "dlspeed" }}
                          <div class="size-sm color-paragraph">
                            {{ if lt $dlSpeed 1000.0 }}--
                            {{ else }}{{ printf "%.1f MB/s" (div $dlSpeed 1000000.0) }}
                            {{ end }}
                          </div>
                          {{ $eta := .Int "eta" }}
                          <div class="size-sm color-paragraph">
                          {{ if eq $eta 8640000 }}∞
                          {{ else if gt $eta 3600 }}{{ printf "%dh %dm" (div $eta 3600) (mod (div $eta 60) 60) }}
                          {{ else if gt $eta 0 }}{{ printf "%dm" (div $eta 60) }}
                          {{ else }}--
                          {{ end }}
                          </div>
                        </div>
                      </li>
                    {{ end }}
                  </ul>
                </div>
              {{ end }}

              {{ $seedingTorrents := $seeding.JSON.Array "" }}
              {{ if gt (len $seedingTorrents) 0 }}
                <div style="margin-top: 20px;">
                  <div class="size-h5 color-primary margin-bottom-10">Seeding</div>
                  <ul class="list collapsible-container" data-collapse-after="0" style="--list-gap: 15px;">
                    {{ range $t := $seedingTorrents }}
                      <li class="flex items-center" style="gap: 10px;">
                        {{ $state := $t.String "state" }}
                        {{ $icon := "↑" }}
                        {{ if eq $state "pausedUP" "stoppedUP" "stalledUP" "queuedUP" }}{{ $icon = "❚❚" }}
                        {{ else if eq $state "error" "missingFiles" }}{{ $icon = "!" }}
                        {{ else if eq $state "checkingUP" }}{{ $icon = "…" }}
                        {{ else if eq $state "checkingResumeData" }}{{ $icon = "⟳" }}
                        {{ end }}
                        <div class="size-h4" style="flex-shrink: 0;">{{ $icon }}</div>
                                  
                        <div style="flex-grow: 1; min-width: 0;">
                          <div class="text-truncate color-highlight">{{ $t.String "name" }}</div>
                          <div class="size-sm color-paragraph">
                            Ratio: {{ printf "%.2f" ($t.Float "ratio") }} | 
                            Size: {{ printf "%.1f GB" (div ($t.Float "size") 1073741824.0) }}
                          </div>
                        </div>

                        <div style="flex-shrink: 0; text-align: right; width: 80px;">
                          {{ $ulSpeed := $t.Float "upspeed" }}
                          <div class="size-sm color-paragraph">
                            {{ if lt $ulSpeed 1000.0 }}--
                            {{ else }}{{ printf "%.1f MB/s" (div $ulSpeed 1000000.0) }}
                            {{ end }}
                          </div>
                          <div class="size-sm color-paragraph">Upload</div>
                        </div>
                      </li>
                    {{ end }}
                  </ul>
                </div>
              {{ end }}
            </div>

          {{ else }}

          <!-- Basic View -->
            <div class="flex justify-between text-center">
              <div>
                {{ $dlSpeed := $transfer.JSON.Float "dl_info_speed" }}
                <div class="color-highlight size-h3">{{ printf "%.1f MB/s" (div $dlSpeed 1000000.0) }}</div>
                <div class="size-h6">DOWNLOADING</div>
              </div>
              <div>
                {{ $ulSpeed := $transfer.JSON.Float "up_info_speed" }}
                <div class="color-highlight size-h3">{{ printf "%.1f MB/s" (div $ulSpeed 1000000.0) }}</div>
                <div class="size-h6">UPLOADING</div>
              </div>
              <div>
                <div class="color-highlight size-h3">{{ len ($seeding.JSON.Array "") }}</div>
                <div class="size-h6">SEEDING</div>
              </div>
            </div>
          {{ end }}

        {{ else }}
          <div class="color-negative text-center">
            <p>Error fetching qBittorrent data.</p>
            <p class="size-sm">Check URL and authentication bypass settings.</p>
          </div>
        {{ end }}
image

@danzkigg
Copy link
Contributor Author

danzkigg commented Sep 6, 2025

this widget is great! I made some small changes if you would like to make it an option within the widget: swapped out leeching for seeding. Show more when seeding shows upload speed, size and ratio. Also changed to MB/s

Great suggestion, I've added that option to the widget. User can now use mode: "upload" to get the seeding list (when combined with the detailed view) and the upload speed.

Copy link
Member

@svilenmarkov svilenmarkov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing!

@svilenmarkov svilenmarkov merged commit 854314e into glanceapp:main Sep 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants