Status: idea / discussion, not a hard implementation request. This document
compares how ratings and comments work in aMule vs eMule, pinpoints what aMule is
missing, and sketches a full design (core + EC + REST + Web UI) as a basis for
discussion. All claims are backed by file:line references — aMule paths are
relative to the repo; eMule paths refer to the eMule source tree.
1. Summary / motivation
eMule can show comments and ratings for a file the user has not downloaded —
you can right-click a search result, hit "Search Kad", and see comments/ratings
that other users published for that file. aMule cannot: its search results carry
only a weak self-rating, and comments are available only for files that are
already downloading (from connected sources).
The data eMule shows travels over Kademlia via a dedicated "Notes" lookup keyed
on the file hash (distinct from the keyword search that returns the file).
aMule already contains half of this machinery — it publishes notes and can
parse a notes response — but it never starts a notes lookup, never routes
results to search results, and never surfaces them in any UI.
This RFC proposes closing that gap and, since aMule's remote/Web interface is the
long-term consumer, exposing the result over EC → REST → Web UI.
2. Background: two different rating/comment channels
There are two independent channels, and it is important not to conflate them:
- Self-rating on the offered file (rides with the search hit). The client that
offers a file may attach FT_FILERATING to its offered-file packet; an ed2k
server relays it back with the search hit. This is a single publisher's own
rating, not an aggregate. (Kad keyword search carries no rating at all in
either client.)
- Kad Notes (comments + ratings, keyed on hash). Any client that has the file
can publish a note (comment + rating) to the Kad nodes responsible for the file
hash. Anyone can later retrieve all notes for that hash — without downloading
the file or connecting to its sources. This is the channel eMule uses to show
comments for non-downloaded files, and the subject of this RFC.
A third, unrelated channel is per-source comments for a file you are already
downloading (read from connected CUpDownClients) — aMule already has this for
downloads (CPartFile::GetRatingAndComments, src/PartFile.cpp:4202-4214).
3. How it works today in aMule
- Self-rating publish:
FT_FILERATING is written only in
CKnownFile::CreateOfferedFilePacket (src/KnownFile.cpp:1226-1229), from the
publisher's own GetFileRating(). A search hit parses it in
src/SearchFile.cpp:82: m_iUserRating = (tag.GetInt() & 0xF) / 3 — quantized to
0-3, and it is a single self-rating, never an aggregate.
- Per-source comments (downloads only):
CPartFile::GetRatingAndComments
(src/PartFile.cpp:4202-4214) reads only m_SrcList — comments from connected
sources. Requires the file to be a CPartFile with live sources.
- Kad Notes — the half that exists:
- Publish (STORENOTES):
src/SharedFileList.cpp:1477-1499 →
PrepareLookup(CSearch::STORENOTES, …); packet built in
src/kademlia/kademlia/Search.cpp:817-843 (attaches TAG_FILENAME,
TAG_FILERATING, TAG_DESCRIPTION, TAG_FILESIZE).
- Parse response:
CSearch::ProcessResultNotes
(src/kademlia/kademlia/Search.cpp:1061-1110), keeping TAG_FILENAME,
TAG_DESCRIPTION, TAG_FILERATING (:1078-1088); dispatched via
KademliaUDPListener.cpp:313-314 → ProcessSearchNotesResponse:1474 →
CSearchManager::ProcessResult (src/kademlia/kademlia/SearchManager.cpp:537).
- Kad Notes — the half that is missing:
- Retrieval is never triggered. There is no
PrepareLookup(CSearch::NOTES, …)
caller anywhere; no FindNoteFile; the CSearch::NOTES lifecycle branches
(SearchManager.cpp:321,468) are dead code.
- Results are not routed to search hits.
ProcessResultNotes looks the hash
up only in sharedfiles then downloadqueue and calls file->AddNote(entry);
theApp->searchlist is never consulted and CSearchList has no AddNotes.
- Nothing surfaces
m_kadNotes. AddNote stores into
CAbstractFile::m_kadNotes (src/KnownFile.cpp:295), but no code reads it back
for display — not even the download comment view, which reads only m_SrcList.
- Request builder is shared-file-bound. The NOTES request build (equivalent to
eMule Search.cpp:604) resolves the file's size from the shared-file registry,
which does not hold search-result files — a blocker for search-result use.
Net: the comment/rating data for a non-downloaded file exists on the Kad
network and aMule can already parse it, but aMule never asks for it, never attaches
it to a search result, and never displays it.
4. How eMule does it (the parts aMule needs)
Structural key: in eMule the entire notes API lives on the base class
CAbstractFile — from which CKnownFile, CPartFile, and CSearchFile all
derive (AbstractFile.h:111-136: AddNote, getNotes,
IsKadCommentSearchRunning, m_kadNotes). So the same mechanism works uniformly
for shared files, downloads, and search results.
Full lifecycle:
- Trigger (user-initiated "Search Kad"):
CommentDialog.cpp:313-336 and
CommentDialogLst.cpp:176-199 call
CSearchManager::PrepareLookup(CSearch::NOTES, true, CUInt128(hash)), then flag
SetKadCommentSearchRunning(true). Guarded by
CSearchManager::AlreadySearchingFor (SearchManager.cpp:259). Reachable from a
search result: the context menu (SearchListCtrl.cpp:1019, handler
:917-922) opens a detail sheet whose comment page is a CCommentDialogLst bound
to the selected CSearchFile — so "Search Kad" runs a NOTES lookup on the search
hit's hash.
- Request:
KADEMLIA2_SEARCH_NOTES_REQ (Search.cpp:660-704).
- Response → route to all three collections:
ProcessResultNotes
(Search.cpp:1118-1233) builds a Kademlia::CEntry, then attaches it to:
- search results —
searchlist->AddNotes(pEntry, id) (Search.cpp:1202 →
SearchList.cpp:769-788, iterates CSearchFiles and matches by hash);
- shared files and downloads (
Search.cpp:1205-1212).
- Surface in UI:
CCommentDialogLst::RefreshData (CommentDialogLst.cpp:130-161) merges the
per-source ed2k comments and file->getNotes(); CCommentListCtrl shows an
Origin column ("eD2K" vs "Kad", CommentListCtrl.cpp:216,230-238).
- The search list draws a rating smiley plus a "has comment" overlay plus a
"Kad search running" spinner (SearchListCtrl.cpp:1421-1425);
CSearchFile::UpdateFileRatingCommentAvail (SearchFile.cpp:332-361) computes
HasComment and an averaged rating from m_kadNotes.
Secondary detail — aggregate rating fidelity: eMule also decodes the ed2k
server's packed rating (HIBYTE = % of clients that rated, LOBYTE = average) into a
0-5 aggregate (SearchFile.cpp:202-215), whereas aMule keeps only the quantized
0-3 self-rating.
5. Gap analysis
| Piece |
aMule |
eMule |
Notes |
| Publish notes (STORENOTES) |
✅ SharedFileList.cpp:1477, Search.cpp:817 |
✅ |
parity |
| Parse NOTES response |
✅ Search.cpp:1061 |
✅ Search.cpp:1118 |
parity |
| Trigger NOTES lookup |
❌ dead (SearchManager.cpp:321,468) |
✅ CommentDialog(Lst).cpp |
missing |
Route notes to CSearchFile |
❌ no CSearchList::AddNotes |
✅ Search.cpp:1202→SearchList.cpp:769 |
missing |
| Notes API on base class |
⚠️ m_kadNotes inherited but unused for search |
✅ AbstractFile.h:111-136 |
needs wiring |
| Surface notes in UI (even downloads) |
❌ m_kadNotes never read |
✅ CommentDialogLst.cpp:149 |
missing |
| NOTES request needs file shared |
⚠️ blocker (size from shared registry) |
handled via search context |
needs relaxing |
| Aggregate rating from server/Kad |
⚠️ self-rating 0-3 only (SearchFile.cpp:82) |
✅ 0-5 avg (SearchFile.cpp:202) |
fidelity gap |
6. Proposed direction — core (aMule)
Framed as direction, not prescription:
- Add a NOTES-search trigger. Introduce the equivalent of eMule's
PrepareLookup(CSearch::NOTES, hash) (revive the dead CSearch::NOTES branches).
Relax the shared-file size lookup so it can take the size from the target
CSearchFile/CPartFile rather than requiring the file to be shared.
- Route results to search hits. Add
CSearchList::AddNotes(entry, hash) and
call it from ProcessResultNotes alongside the existing shared/download matching,
so notes attach to the CSearchFile (which already inherits m_kadNotes).
- Derive presence + aggregate. Add a
CSearchFile::UpdateFileRatingCommentAvail
equivalent that computes HasComment() and an averaged rating from m_kadNotes.
- Surface for downloads too. Merge
m_kadNotes into aMule's comment view
(extend GetRatingAndComments / the comment dialog to include Kad notes with an
origin marker), matching eMule's merged list.
No new Kad wire format is required — the KADEMLIA2_*_NOTES_* opcodes already exist
and aMule already parses the response.
7. Proposed direction — downstream EC / REST / Web UI (aMule)
Since the remote/Web interface is the intended consumer, expose the above:
EC (amuled ↔ clients)
- In
CEC_SearchFile_Tag (src/ECSpecialCoreTags.cpp:365-386), add a has_comment
flag and the aggregate rating once notes are attached.
- Add an EC op to (i) trigger a Kad-notes fetch for a given hash and (ii)
return the notes list, modelled on the existing per-source comments path
(EC_TAG_PARTFILE_COMMENTS=0x0316, EC_TAG_KNOWNFILE_COMMENT=0x040E). Each note:
filename, rating, comment (and origin = Kad).
REST (amuleapi)
POST /api/v0/search/results/{hash}/comments/fetch — kick off the Kad-notes
lookup for that hash. Async: notes arrive later via the refresher (like search
results do today).
GET /api/v0/search/results/{hash}/comments — return the collected notes:
{ "count": 2,
"comments": [ { "origin": "kad", "username": null, "filename": "movie.mkv",
"rating": 4, "comment": "great quality" } ] }
Reuse the shape defined by .claude/files/02-file-comments-ratings.md (add an
origin field to distinguish kad vs ed2k).
- Add
has_comment (bool) and the aggregate rating to the search result object in
WriteSearchObject (src/webapi/Api.cpp:3681-3702), decoded in ApplySearchFull
(src/webapi/Refresher.cpp:1552-1591).
Web UI (src/webapi/static)
- A "has comment" indicator + aggregate rating on each search row.
- An on-demand "fetch comments" action per result that calls the
fetch
endpoint, then shows the returned notes in a panel (with the eD2K/Kad origin).
8. Open questions for discussion
- On-demand vs automatic. eMule fetches notes only when the user asks (avoids
flooding Kad with lookups). Automatic prefetch for every visible result could be
abusive/slow. Recommend on-demand, mirroring eMule.
- Spam / word filtering. eMule filters note text (
GetCommentFilter,
RefilterKadNotes, AbstractFile.cpp:411). Should aMule port this before showing
arbitrary user-published text in the Web UI?
- The shared-file requirement (
Search.cpp:604) must be relaxed for
search-result use — is there a reason it was scoped to shared files?
- Aggregate rating fidelity. Worth also adopting eMule's packed-rating decode
(0-5 average with % of raters) rather than the 0-3 self-rating? Related but
separable from Kad notes.
- Privacy. Publishing your own comment/rating to Kad exposes it network-wide;
the Web UI should make writing a comment an explicit, opt-in action.
- Availability caveat. Notes only exist if Kad nodes host them; many files will
return nothing. The UI must distinguish "no comments" from "not fetched / Kad
unavailable".
9. Effort / risk
- Core: medium/high — touches Kademlia search lifecycle and search-list wiring,
but no new wire protocol (the NOTES opcodes and parser already exist). The main
work is reviving the trigger, relaxing the shared-file constraint, routing to
CSearchFile, and surfacing m_kadNotes.
- EC/REST/Web UI: additive and low-risk, following existing patterns (the search
result serializer and the per-source comments design in .claude/files/02).
- Independent of the four search issues in this folder (
01-04); can be
scoped/scheduled on its own.
1. Summary / motivation
eMule can show comments and ratings for a file the user has not downloaded —
you can right-click a search result, hit "Search Kad", and see comments/ratings
that other users published for that file. aMule cannot: its search results carry
only a weak self-rating, and comments are available only for files that are
already downloading (from connected sources).
The data eMule shows travels over Kademlia via a dedicated "Notes" lookup keyed
on the file hash (distinct from the keyword search that returns the file).
aMule already contains half of this machinery — it publishes notes and can
parse a notes response — but it never starts a notes lookup, never routes
results to search results, and never surfaces them in any UI.
This RFC proposes closing that gap and, since aMule's remote/Web interface is the
long-term consumer, exposing the result over EC → REST → Web UI.
2. Background: two different rating/comment channels
There are two independent channels, and it is important not to conflate them:
offers a file may attach
FT_FILERATINGto its offered-file packet; an ed2kserver relays it back with the search hit. This is a single publisher's own
rating, not an aggregate. (Kad keyword search carries no rating at all in
either client.)
can publish a note (comment + rating) to the Kad nodes responsible for the file
hash. Anyone can later retrieve all notes for that hash — without downloading
the file or connecting to its sources. This is the channel eMule uses to show
comments for non-downloaded files, and the subject of this RFC.
A third, unrelated channel is per-source comments for a file you are already
downloading (read from connected
CUpDownClients) — aMule already has this fordownloads (
CPartFile::GetRatingAndComments,src/PartFile.cpp:4202-4214).3. How it works today in aMule
FT_FILERATINGis written only inCKnownFile::CreateOfferedFilePacket(src/KnownFile.cpp:1226-1229), from thepublisher's own
GetFileRating(). A search hit parses it insrc/SearchFile.cpp:82:m_iUserRating = (tag.GetInt() & 0xF) / 3— quantized to0-3, and it is a single self-rating, never an aggregate.
CPartFile::GetRatingAndComments(
src/PartFile.cpp:4202-4214) reads onlym_SrcList— comments from connectedsources. Requires the file to be a
CPartFilewith live sources.src/SharedFileList.cpp:1477-1499→PrepareLookup(CSearch::STORENOTES, …); packet built insrc/kademlia/kademlia/Search.cpp:817-843(attachesTAG_FILENAME,TAG_FILERATING,TAG_DESCRIPTION,TAG_FILESIZE).CSearch::ProcessResultNotes(
src/kademlia/kademlia/Search.cpp:1061-1110), keepingTAG_FILENAME,TAG_DESCRIPTION,TAG_FILERATING(:1078-1088); dispatched viaKademliaUDPListener.cpp:313-314→ProcessSearchNotesResponse:1474→CSearchManager::ProcessResult(src/kademlia/kademlia/SearchManager.cpp:537).PrepareLookup(CSearch::NOTES, …)caller anywhere; no
FindNoteFile; theCSearch::NOTESlifecycle branches(
SearchManager.cpp:321,468) are dead code.ProcessResultNoteslooks the hashup only in
sharedfilesthendownloadqueueand callsfile->AddNote(entry);theApp->searchlistis never consulted andCSearchListhas noAddNotes.m_kadNotes.AddNotestores intoCAbstractFile::m_kadNotes(src/KnownFile.cpp:295), but no code reads it backfor display — not even the download comment view, which reads only
m_SrcList.eMule
Search.cpp:604) resolves the file's size from the shared-file registry,which does not hold search-result files — a blocker for search-result use.
Net: the comment/rating data for a non-downloaded file exists on the Kad
network and aMule can already parse it, but aMule never asks for it, never attaches
it to a search result, and never displays it.
4. How eMule does it (the parts aMule needs)
Structural key: in eMule the entire notes API lives on the base class
CAbstractFile— from whichCKnownFile,CPartFile, andCSearchFileallderive (
AbstractFile.h:111-136:AddNote,getNotes,IsKadCommentSearchRunning,m_kadNotes). So the same mechanism works uniformlyfor shared files, downloads, and search results.
Full lifecycle:
CommentDialog.cpp:313-336andCommentDialogLst.cpp:176-199callCSearchManager::PrepareLookup(CSearch::NOTES, true, CUInt128(hash)), then flagSetKadCommentSearchRunning(true). Guarded byCSearchManager::AlreadySearchingFor(SearchManager.cpp:259). Reachable from asearch result: the context menu (
SearchListCtrl.cpp:1019, handler:917-922) opens a detail sheet whose comment page is aCCommentDialogLstboundto the selected
CSearchFile— so "Search Kad" runs a NOTES lookup on the searchhit's hash.
KADEMLIA2_SEARCH_NOTES_REQ(Search.cpp:660-704).ProcessResultNotes(
Search.cpp:1118-1233) builds aKademlia::CEntry, then attaches it to:searchlist->AddNotes(pEntry, id)(Search.cpp:1202→SearchList.cpp:769-788, iteratesCSearchFiles and matches by hash);Search.cpp:1205-1212).CCommentDialogLst::RefreshData(CommentDialogLst.cpp:130-161) merges theper-source ed2k comments and
file->getNotes();CCommentListCtrlshows anOrigin column ("eD2K" vs "Kad",
CommentListCtrl.cpp:216,230-238)."Kad search running" spinner (
SearchListCtrl.cpp:1421-1425);CSearchFile::UpdateFileRatingCommentAvail(SearchFile.cpp:332-361) computesHasCommentand an averaged rating fromm_kadNotes.Secondary detail — aggregate rating fidelity: eMule also decodes the ed2k
server's packed rating (HIBYTE = % of clients that rated, LOBYTE = average) into a
0-5 aggregate (
SearchFile.cpp:202-215), whereas aMule keeps only the quantized0-3 self-rating.
5. Gap analysis
SharedFileList.cpp:1477,Search.cpp:817Search.cpp:1061Search.cpp:1118SearchManager.cpp:321,468)CommentDialog(Lst).cppCSearchFileCSearchList::AddNotesSearch.cpp:1202→SearchList.cpp:769m_kadNotesinherited but unused for searchAbstractFile.h:111-136m_kadNotesnever readCommentDialogLst.cpp:149SearchFile.cpp:82)SearchFile.cpp:202)6. Proposed direction — core (aMule)
Framed as direction, not prescription:
PrepareLookup(CSearch::NOTES, hash)(revive the deadCSearch::NOTESbranches).Relax the shared-file size lookup so it can take the size from the target
CSearchFile/CPartFilerather than requiring the file to be shared.CSearchList::AddNotes(entry, hash)andcall it from
ProcessResultNotesalongside the existing shared/download matching,so notes attach to the
CSearchFile(which already inheritsm_kadNotes).CSearchFile::UpdateFileRatingCommentAvailequivalent that computes
HasComment()and an averaged rating fromm_kadNotes.m_kadNotesinto aMule's comment view(extend
GetRatingAndComments/ the comment dialog to include Kad notes with anorigin marker), matching eMule's merged list.
No new Kad wire format is required — the
KADEMLIA2_*_NOTES_*opcodes already existand aMule already parses the response.
7. Proposed direction — downstream EC / REST / Web UI (aMule)
Since the remote/Web interface is the intended consumer, expose the above:
EC (amuled ↔ clients)
CEC_SearchFile_Tag(src/ECSpecialCoreTags.cpp:365-386), add ahas_commentflag and the aggregate rating once notes are attached.
return the notes list, modelled on the existing per-source comments path
(
EC_TAG_PARTFILE_COMMENTS=0x0316,EC_TAG_KNOWNFILE_COMMENT=0x040E). Each note:filename, rating, comment (and origin = Kad).
REST (amuleapi)
POST /api/v0/search/results/{hash}/comments/fetch— kick off the Kad-noteslookup for that hash. Async: notes arrive later via the refresher (like search
results do today).
GET /api/v0/search/results/{hash}/comments— return the collected notes:{ "count": 2, "comments": [ { "origin": "kad", "username": null, "filename": "movie.mkv", "rating": 4, "comment": "great quality" } ] }.claude/files/02-file-comments-ratings.md(add anoriginfield to distinguishkadvsed2k).has_comment(bool) and the aggregateratingto the search result object inWriteSearchObject(src/webapi/Api.cpp:3681-3702), decoded inApplySearchFull(
src/webapi/Refresher.cpp:1552-1591).Web UI (
src/webapi/static)fetchendpoint, then shows the returned notes in a panel (with the eD2K/Kad origin).
8. Open questions for discussion
flooding Kad with lookups). Automatic prefetch for every visible result could be
abusive/slow. Recommend on-demand, mirroring eMule.
GetCommentFilter,RefilterKadNotes,AbstractFile.cpp:411). Should aMule port this before showingarbitrary user-published text in the Web UI?
Search.cpp:604) must be relaxed forsearch-result use — is there a reason it was scoped to shared files?
(0-5 average with % of raters) rather than the 0-3 self-rating? Related but
separable from Kad notes.
the Web UI should make writing a comment an explicit, opt-in action.
return nothing. The UI must distinguish "no comments" from "not fetched / Kad
unavailable".
9. Effort / risk
but no new wire protocol (the NOTES opcodes and parser already exist). The main
work is reviving the trigger, relaxing the shared-file constraint, routing to
CSearchFile, and surfacingm_kadNotes.result serializer and the per-source comments design in
.claude/files/02).01-04); can bescoped/scheduled on its own.