Поток данных¶
На этой странице прослеживается движение данных от пользовательского ввода до аудиовыхода с указанием слоёв, преобразующих данные, и мест хранения состояния.
Поиск → entities¶
Пользователь вводит запрос
│
▼ stores/search.ts: runSearch(query)
│
▼ search/engine.ts: engine.query(query, enabledProviders)
│ ├── resolver.ts: invoke("resolve_query") → ResolveResult
│ │ (Brave Search → парсинг заголовков Genius → canonical (artist, title))
│ └── new SearchSession(providerQuery, providers)
│
▼ SearchSession запускает provider-ы параллельно:
│
├── RutrackerProvider.search(query)
│ ├── invoke("rutracker_search") → строки topic-ов
│ ├── для каждого topic: invoke("rutracker_get_torrent_details") → файлы
│ ├── detectAlbums(files) → группы альбомов
│ └── отправка snapshot-ов PipelineEntity[]
│
└── SoulseekProvider.search(query)
├── invoke("soulseek_search") → запускает поиск на бэкенде
├── listen("soulseek-search-batch") → batch-ы от peer-ов
└── groupSlskRowsToEntities(raw) → отправка snapshot-ов PipelineEntity[]
│
▼ SearchSession._flushImmediate() (на каждом rAF)
│ объединение snapshot-ов → runPipeline([normalize, dedup, score, filter])
│ → session.results.value обновляется
│
▼ stores/search.ts: watch(session.results) → _applyFromSession()
│ registerEntities(entities) → реестр stores/entities.ts
│ searchEntities.value = зарегистрированные entities
│
▼ Results.vue: читает searchEntities → рендерит карточки альбомов/треков
Клик для воспроизведения¶
Пользователь кликает трек
│
▼ stores/queue.ts: enqueue / set position
│
▼ Player.vue: watch(nowPlaying)
│
▼ torrent/api.ts: streamUrl(magnet, fileIdx, opts)
│
├── [путь RuTracker]
│ _cachedTorrentFileB64(topicId) → base64 .torrent (из cache или dl.php)
│ invoke("torrent_prepare_stream", { magnet, fileIdx, torrentFileB64 })
│ → Rust: VozduxanStreamState.prepare()
│ проверка версии → отмена старого token → вызов vozduxan C++ → предбуферизация
│ → "http://127.0.0.1:<port>/stream/<token>"
│
└── [путь SoulSeek]
soulseekPrepareStream(username, filepath, filesize)
→ invoke("soulseek_prepare_stream")
→ Rust: download_and_stream()
P connection → handshake → F connection → download_loop
http_server_loop на 127.0.0.1:0
→ "http://127.0.0.1:<port>/slsk/<token>"
│
▼ <audio src={url} />
│
├── timeupdate → invoke("vozduxan_notify_position", { token, byteOffset })
│ → C++: сдвигает окно приоритетов кусков
│
├── прогресс ~22% / 30с → prefetchNextInQueue(current, next)
│ → invoke("torrent_prefetch_next_track", { ..., warmOnly: false })
│ сохраняется в bucket prefetch_token
│
└── трек закончился / пропуск
invoke("torrent_release_stream", { token })
→ C++: ожидает завершения потока приоритетов (~100мс), помечает stream как idle
Сохранение лайков¶
Пользователь нажимает кнопку лайка
│
▼ stores/library.ts: likes.toggleTrack(trackId)
│ likes.trackIds.value.add/delete(trackId)
│ likes.likedAt.value.set(trackId, Date.now())
│ saveLikesSnapshot() → localStorage "neegde.likes.v2"
│
▼ Перезапуск приложения
loadLikesSnapshot() → likes.trackIds восстановлены
hydrateTrack(id) для каждого понравившегося id ← trackCache (localStorage)
или появляется снова при следующем поиске → registerEntity
Сохранение очереди¶
Любое изменение очереди (добавление/удаление/переупорядочение)
│
saveQueueSnapshot() → localStorage "neegde.queue.v2": { ids, pos }
│
Перезапуск приложения
│
▼ stores/queue.ts: seedFromSnapshot()
queue.ids = snapshot.ids
queue.pos = snapshot.pos
suppressAutoplay = true ← без HTML-автовоспроизведения при восстановлении
hydrateTrack(id) для каждого id ← trackCache
Поток данных реестра entities¶
Provider отправляет PipelineEntity[]
│
registerEntities(entities) в stores/entities.ts
│
для каждого entity:
withMergedPersisted(entity) ← объединение coverUrl/albumTitle из trackCache
normalize(entity) ← buildTrack() или buildAlbum()
_byId.value.set(id, instance)
если track: putTrack() → trackCache (localStorage)
если track: recordGeneralList() → general-list.json (debug-реестр)
│
bump(): triggerRef(_byId) + entitiesVersion.value++
│
Computed-сигналы инвалидируются:
- queue.nowPlaying, .next, .secondNext
- queue.tracks
- library.likedTracks, .likedAlbums
- любой компонент, читающий entitiesVersion.value
Сводка по владению состоянием¶
| Данные | Местонахождение | Сохранение |
|---|---|---|
| Реестр entities | stores/entities._byId |
trackCache / albumCache в localStorage |
| Очередь | stores/queue.playbackQueue |
neegde.queue.v2 в localStorage |
| Понравившиеся треки/альбомы | stores/library.likes |
neegde.likes.v2 в localStorage |
| Плейлисты | stores/library.library |
neegde.playlists.v2 в localStorage |
| Результаты поиска | stores/search.searchEntities |
Не сохраняются (только в сессии) |
| Активные stream token-ы | vozduxan_stream.rs VozduxanSessionInner |
Не сохраняются (в памяти процесса) |
| SoulSeek stream-ы | soulseek/mod.rs SoulSeekState.streams |
Не сохраняются (временные файлы в OS tmp) |
| Сессия RuTracker | rutracker/mod.rs + session.json + meta.json |
На диске (относительно исполняемого файла) |
| Сессия SoulSeek | soulseek/mod.rs Session |
creds.json (только учётные данные, без сессии) |
| Cache файлов .torrent | torrent/api.ts _torrentFileCache |
localStorage torrent_file_b64_v1_* |
| Лог отладки | torrent_stream/debug_log.rs AppDebugLog |
app_debug.json (только флаг включения) |