Data Flow¶
This page traces how data moves from user input to audio output, showing which layers transform it and where state lives.
Search → entities¶
User types query
│
▼ stores/search.ts: runSearch(query)
│
▼ search/engine.ts: engine.query(query, enabledProviders)
│ ├── resolver.ts: invoke("resolve_query") → ResolveResult
│ │ (Brave Search → Genius title parse → canonical (artist, title))
│ └── new SearchSession(providerQuery, providers)
│
▼ SearchSession runs providers concurrently:
│
├── RutrackerProvider.search(query)
│ ├── invoke("rutracker_search") → topic rows
│ ├── for each topic: invoke("rutracker_get_torrent_details") → files
│ ├── detectAlbums(files) → album groups
│ └── yield PipelineEntity[] snapshots
│
└── SoulseekProvider.search(query)
├── invoke("soulseek_search") → starts backend search
├── listen("soulseek-search-batch") → batches from peers
└── groupSlskRowsToEntities(raw) → yield PipelineEntity[] snapshots
│
▼ SearchSession._flushImmediate() (on every rAF)
│ flatten snapshots → runPipeline([normalize, dedup, score, filter])
│ → session.results.value updated
│
▼ stores/search.ts: watch(session.results) → _applyFromSession()
│ registerEntities(entities) → stores/entities.ts registry
│ searchEntities.value = registered entities
│
▼ Results.vue: reads searchEntities → renders album/track cards
Click to play¶
User clicks a track
│
▼ stores/queue.ts: enqueue / set position
│
▼ Player.vue: watch(nowPlaying)
│
▼ torrent/api.ts: streamUrl(magnet, fileIdx, opts)
│
├── [RuTracker path]
│ _cachedTorrentFileB64(topicId) → base64 .torrent (from cache or dl.php)
│ invoke("torrent_prepare_stream", { magnet, fileIdx, torrentFileB64 })
│ → Rust: VozduxanStreamState.prepare()
│ version guard → cancel old token → call vozduxan C++ → prebuffer
│ → "http://127.0.0.1:<port>/stream/<token>"
│
└── [SoulSeek path]
soulseekPrepareStream(username, filepath, filesize)
→ invoke("soulseek_prepare_stream")
→ Rust: download_and_stream()
P connection → handshake → F connection → download_loop
http_server_loop on 127.0.0.1:0
→ "http://127.0.0.1:<port>/slsk/<token>"
│
▼ <audio src={url} />
│
├── timeupdate → invoke("vozduxan_notify_position", { token, byteOffset })
│ → C++: slides piece priority window
│
├── progress ~22% / 30s → prefetchNextInQueue(current, next)
│ → invoke("torrent_prefetch_next_track", { ..., warmOnly: false })
│ stores in prefetch_token bucket
│
└── track ends / skip
invoke("torrent_release_stream", { token })
→ C++: joins priority thread (~100ms), marks stream idle
Likes persistence¶
User clicks like button
│
▼ stores/library.ts: likes.toggleTrack(trackId)
│ likes.trackIds.value.add/delete(trackId)
│ likes.likedAt.value.set(trackId, Date.now())
│ saveLikesSnapshot() → localStorage "neegde.likes.v2"
│
▼ App restart
loadLikesSnapshot() → likes.trackIds restored
hydrateTrack(id) for each liked id ← trackCache (localStorage)
or re-appears when next search is run → registerEntity
Queue persistence¶
Any queue mutation (add/remove/reorder)
│
saveQueueSnapshot() → localStorage "neegde.queue.v2": { ids, pos }
│
App restart
│
▼ stores/queue.ts: seedFromSnapshot()
queue.ids = snapshot.ids
queue.pos = snapshot.pos
suppressAutoplay = true ← no HTML autoplay on restore
hydrateTrack(id) for each id ← trackCache
Entities registry data flow¶
Provider emits PipelineEntity[]
│
registerEntities(entities) in stores/entities.ts
│
for each entity:
withMergedPersisted(entity) ← merge coverUrl/albumTitle from trackCache
normalize(entity) ← buildTrack() or buildAlbum()
_byId.value.set(id, instance)
if track: putTrack() → trackCache (localStorage)
if track: recordGeneralList() → general-list.json (debug registry)
│
bump(): triggerRef(_byId) + entitiesVersion.value++
│
Computed signals invalidated:
- queue.nowPlaying, .next, .secondNext
- queue.tracks
- library.likedTracks, .likedAlbums
- any component that reads entitiesVersion.value
State ownership summary¶
| Data | Location | Persistence |
|---|---|---|
| Entity registry | stores/entities._byId |
trackCache / albumCache in localStorage |
| Queue | stores/queue.playbackQueue |
neegde.queue.v2 in localStorage |
| Liked tracks/albums | stores/library.likes |
neegde.likes.v2 in localStorage |
| Playlists | stores/library.library |
neegde.playlists.v2 in localStorage |
| Search results | stores/search.searchEntities |
Not persisted (session only) |
| Active stream tokens | vozduxan_stream.rs VozduxanSessionInner |
Not persisted (in-process) |
| SoulSeek streams | soulseek/mod.rs SoulSeekState.streams |
Not persisted (temp files in OS tmp) |
| RuTracker session | rutracker/mod.rs + session.json + meta.json |
Disk (exe-relative) |
| SoulSeek session | soulseek/mod.rs Session |
creds.json (credentials only, no session) |
| .torrent file cache | torrent/api.ts _torrentFileCache |
localStorage torrent_file_b64_v1_* |
| Debug log | torrent_stream/debug_log.rs AppDebugLog |
app_debug.json (enabled flag only) |