Skip to content

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)