Skip to content

Enrich the download-detail endpoint and add a shared-file detail endpoint #417

Description

@ngosang

Summary

Two gaps in the amuleapi REST API around single-file information:

  1. GET /api/v0/downloads/{hash} exposes only a subset of what the aMule desktop File
    Details
    dialog (CFileDetailDialog) shows.
  2. There is no single-file endpoint for shared files at all — /api/v0/shared/{hash}
    only accepts PATCH, and GET /api/v0/shared returns just a lean list (7 of the 12
    columns of the desktop shared-files table).

This issue (a) wires the missing download-detail fields through, and (b) adds a new
GET /api/v0/shared/{hash} detail endpoint that returns all available info for a
shared file. GET /shared (the list) is left unchanged.

Most values are already shipped by amuled over EC — the refresher just drops them; the
rest are trivially computed in the amuleapi backend.

Effort: webapi-only. No amuled or EC-protocol changes.


Part A — GET /downloads/{hash} detail fields

Extend FileSnapshot::DownloadSide (src/webapi/State.h:96), decode the tags in
src/webapi/Refresher.cpp (MergeDownloadTags), and emit the fields in
WriteDownloadObject (src/webapi/Api.cpp:1595). All download-side tags are already
emitted by CEC_PartFile_Tag (src/ECSpecialCoreTags.cpp:151-212).

New fields (inside the download object unless noted):

JSON field Meaning EC tag Backing method (ECSpecialCoreTags.cpp)
last_seen_complete unix ts; 0 = unknown EC_TAG_PARTFILE_LAST_SEEN_COMP lastseencomplete (:174)
last_changed unix ts of last change EC_TAG_PARTFILE_LAST_RECV GetLastChangeDatetime() (:175)
download_active_time seconds actively downloading EC_TAG_PARTFILE_DOWNLOAD_ACTIVE GetDlActiveTime() (:176)
available_part_count parts available across sources EC_TAG_PARTFILE_AVAILABLE_PARTS GetAvailablePartCount() (:177)
part_count total parts derive ceil(size / PARTSIZE) (already computed at Api.cpp:1564)
remaining_time ETA seconds; -1 = unknown/stalled computed in amuleapi (see below)
hashing_progress part currently being hashed EC_TAG_PARTFILE_HASHED_PART_COUNT GetHashingProgress() (:178)
lost_to_corruption bytes lost to corruption EC_TAG_PARTFILE_LOST_CORRUPTION GetLostDueToCorruption() (:180)
gained_by_compression bytes saved by compression EC_TAG_PARTFILE_GAINED_COMPRESSION GetGainDueToCompression() (:181)
saved_by_ich packets recovered by I.C.H. EC_TAG_PARTFILE_SAVED_ICH TotalPacketsSavedDueToICH() (:182)
aich_hash AICH master hash (also on GET /shared/{hash}) EC_TAG_KNOWNFILE_AICH_MASTERHASH GetAICHMasterHash() (:226)
met_file .part.met basename EC_TAG_KNOWNFILE_FILENAME GetCachedPartMetBasename() (:246)
partmet_id numeric partfile id EC_TAG_PARTFILE_PARTMETID GetPartMetNumber() (:203)
queued_count clients queued (also on GET /shared/{hash}) EC_TAG_KNOWNFILE_ON_QUEUE GetQueuedCount() (:236)

remaining_time (computed in the backend)

The desktop getTimeRemaining() (src/PartFile.cpp:4423) computes ETA as
(size - size_done) / (speed), returning -1 when the speed is ~0. There is no EC tag
for it, but amuleapi already has size, size_done and speed_bps in the snapshot, so it
computes remaining_time server-side — no amuled/EC change needed.

File Details coverage notes

  • The "Filepart-Count" line in the dialog shows PartCount (HashCount). part_count is
    covered; hash_count (GetHashCount()) has no standalone EC tag — a minor, derivable
    gap, not blocking.
  • The dialog's "Cleanup" button is a pure client-side filename-beautifier heuristic
    (src/FileDetailDialog.cpp:415-505) — no API data; a client implements it locally.
  • These fields back the "General", "Transfer" and "Intelligent Corruption Handling" boxes
    of CFileDetailDialog (src/FileDetailDialog.cpp:151-192).

Part B — new GET /shared/{hash} (shared-file detail)

GET /shared (the list) stays exactly as it is. Add a new detail endpoint that returns
all available info for a single shared file — the goal is one call that gives a client
everything about a file.

  • Routing: /api/v0/shared/{hash} currently maps only PATCHHandleSharedPatch
    (src/webapi/Api.cpp:851-855). Add a GET branch → a new HandleSharedDetail +
    WriteSharedDetailObject, mirroring the download detail path (Api.cpp:2204 /
    HandleDownloadDetail).
  • Base fields: everything WriteSharedObject (Api.cpp:1701) already emits —
    hash, name, ed2k_link, size, priority, priority_auto, complete_sources, xfer{session,total}, requests{session,total}, accepts{session,total}.
  • Plus the following detail fields (the shared-files table gaps + shared-applicable
    identity fields):
JSON field GUI column / meaning Source Kind
file_type Type computed in amuleapi backend from the filename extension (same categorization as GetFiletypeByName, src/SharedFilesCtrl.cpp:595): "Audio", "Video", "Archive", "Program", "Document" backend-computed
share_ratio Share Ratio computed in amuleapi backend as xfer.total / size (mirrors src/SharedFilesCtrl.cpp:623); 0 when size == 0 backend-computed
path Directory Path EC_TAG_KNOWNFILE_FILENAMEGetFilePath().GetPrintable() ("[PartFile]" for a shared partfile, src/SharedFilesCtrl.cpp:657-660) webapi-only decode
complete_sources_range { low, high } Complete Sources range EC_TAG_KNOWNFILE_COMPLETE_SOURCES_LOW / _HIGH (src/ECSpecialCoreTags.cpp:232-233); the < N / N - M display (src/SharedFilesCtrl.cpp:638-652) — complements scalar complete_sources webapi-only decode
aich_hash EC_TAG_KNOWNFILE_AICH_MASTERHASH (ECSpecialCoreTags.cpp:226) webapi-only decode
part_count derive ceil(size / PARTSIZE) backend-computed
queued_count EC_TAG_KNOWNFILE_ON_QUEUE (ECSpecialCoreTags.cpp:236) webapi-only decode

The "Obtained Parts" availability-bar column is intentionally excluded — it is the only
shared-table column that would need a new EC tag, and it is ~100% for complete shared files.

Acceptance criteria

  • GET /downloads/{hash} returns all Part-A fields with correct values for an active
    download; remaining_time is -1 when paused/stalled.
  • GET /shared/{hash} exists (new) and returns every GET /shared field plus the
    Part-B detail fields for a shared file; share_ratio is 0 when size == 0; path is
    "[PartFile]" for a shared partfile.
  • GET /shared (the list) is unchanged.
  • No regressions in the existing download-list / download-detail / shared-list payloads.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions