Standardize Folia online-song data access around the Omni facade, including search, playback, lyrics, catalogs, libraries, recommendations, account actions, mutations, provider identity, and explicit cross-provider flows. Use when adding, refactoring, reviewing, or testing online music features, when deciding whether a caller may use a provider adapter directly, or when implementing provider aggregation, fallback, comparison, or migration.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Standardize Folia online-song data access around the Omni facade, including search, playback, lyrics, catalogs, libraries, recommendations, account actions, mutations, provider identity, and explicit cross-provider flows. Use when adding, refactoring, reviewing, or testing online music features, when deciding whether a caller may use a provider adapter directly, or when implementing provider aggregation, fallback, comparison, or migration.
Online Song Omni Routing
Use this skill for every feature that exchanges data with an online music provider. For fast navigation, read skills/codebase-navigation/SKILL.md, then the relevant sections of README.md, src/README.md, and src/services/onlineMusic/README.md; treat src/services/onlineMusic/omni.ts and src/types/onlineMusic.ts as the public contract.
Non-negotiable rule
Unless the feature is explicitly cross-provider, route all online-song data interaction through omni.
This rule applies to search, song detail, audio URLs, availability/replacement, lyrics, chorus ranges, playlists, albums, artists, recommendations, account status, likes, subscriptions, playlist mutations, and page URLs. Components, hooks, stores, and ordinary app services must not import a concrete provider, provider registry, or provider transport to perform these operations.
omni is a facade, not a raw-response escape hatch. Callers consume UnifiedSong, OmniCollection, OmniPage, OmniLyricsResult, OmniAudioSource, OmniUser, and OmniError; never make caller code depend on provider-specific field names or envelopes.
Current provider implementation map: providerRegistry.ts selects capabilities, providerAccountCache.ts and providerStorage.ts retain account/session state, neteaseProvider.ts and kugouProvider.ts normalize provider data, and kugouTransport.ts owns KuGou transport details. navidromeService.ts is a separate Subsonic service, not an Omni provider.
Decide the boundary before coding
Classify the requested behavior as one of these:
Single-provider flow: the user is browsing, playing, editing, or reading data belonging to the active provider or to the provider that owns a song/collection. Use an omni method that routes by active provider, song source, or collection provider.
Explicit provider selection: the UI lets the user choose one provider. Still use omni, normally searchProviderSongs, getProviderUserPlaylists, getProviderCapabilities, or another provider-explicit Omni method. Selecting a provider is not by itself a reason to bypass Omni.
Cross-provider flow: the product behavior intentionally combines two or more providers, such as comparison, federated search, fallback across providers, duplicate review, or migration. Implement an app/service-level orchestrator that calls Omni's provider-explicit methods and preserves each result's source identity.
Provider implementation: the change adds or repairs one adapter's transport, request mapping, raw-response unwrapping, or normalization. Only the provider adapter and transport layers may use that provider's raw API. Keep the result behind the Omni contract.
If the request does not explicitly say cross-provider, default to single-provider routing through Omni. Do not infer cross-provider behavior merely because multiple providers are registered.
Do not replace these calls with kugouProvider.search.searchSongs(...), requestKugou(...), neteaseApi(...), or a direct fetch from a component, hook, store, or app-level feature service.
Explicit cross-provider examples
Cross-provider code must say what makes it cross-provider and must keep provider identity attached to every result. Prefer Omni's provider-explicit methods even here:
This is valid because the feature is intentionally federated search. A fallback implementation is also cross-provider only when the product explicitly requires it:
for (const providerId of providers) {
const page = await omni.searchProviderSongs(providerId, query, { limit: 20, offset: 0 });
if (page.items.length > 0) return page.items;
}
return [];
When merging semantically duplicate songs, use an explicit matching policy (for example normalized artist/title/duration) but never discard the original provider ID. A duplicate is not the same playback identity: online:netease:123 and online:kugou:123 are different songs until the user or a documented policy chooses one.
Direct provider calls are still not the default for cross-provider features. Use them only inside the adapter/transport implementation when the cross-provider orchestration needs a capability that Omni does not expose; first extend the Omni contract when the capability is a normal online-song operation.
Layer rules
Layer
Allowed online data access
Forbidden
Component / hook / store
omni and shared source-aware utilities
provider registry, provider adapter, transport, raw API
Ordinary app-level service
omni; source-aware utilities
concrete provider calls
Cross-provider orchestrator
omni provider-explicit methods; provider summaries/capabilities
dropping providerId, comparing numeric IDs alone
src/services/onlineMusic/*Provider.ts
its own transport and adapter helpers; normalize into shared types
leaking raw response shapes to callers
src/services/onlineMusic/*Transport.ts
HTTP/IPC/provider protocol
UI or product orchestration
Tests
test the layer being changed; Omni tests for routing, adapter tests for normalization
fixtures that pretend raw provider data is the public contract
If Omni lacks a normal capability, add the capability to src/types/onlineMusic.ts and src/services/onlineMusic/omni.ts, then implement it in each applicable adapter. Do not create a second public bypass around Omni.
Identity and normalization rules
Treat online identity as (sourceRef.kind: 'online', sourceRef.providerId, sourceRef.mediaId), not song.id alone.
Use getPlaybackSourceRef, getPlaybackSongKey, isSamePlaybackSong, and existing source-aware helpers before comparing, deduplicating, replacing, or queuing songs.
Keep providerId on UnifiedSong, SongResult, collections, catalog references, cache keys, and mutation checks.
Route a song mutation through the provider that owns the song. Do not add a Netease song to a KuGou playlist unless the feature explicitly resolves and confirms a cross-provider mapping.
Normalize raw provider data once at the adapter boundary. Callers should receive stable shared types and OnlineProviderError/OmniError codes.
Respect capability and availability checks. An unsupported operation is not an empty successful result; preserve the existing Omni error/empty-page convention.
Preserve active-provider request cancellation behavior. Do not cache or apply a late response after the active provider changes.
Implementation workflow
Read the relevant README sections and locate the actual Omni method/type, provider adapter, and transport.
Classify the feature using the boundary decision above. Write the cross-provider reason in the service/function name or a short comment when it is not obvious.
Implement ordinary calls through Omni. If a normal capability is missing, extend the Omni contract instead of bypassing it.
For provider changes, verify the raw contract with the provider-specific alignment skill and keep normalization inside the adapter.
Add focused tests for routing, source identity, capability failures, and cross-provider deduplication/fallback when applicable.
Run the repository's testing guidance for the touched path; prefer focused unit tests over an unrelated full build.
Review checklist
Is every online-song call in a component, hook, store, or ordinary app service made through omni?
If a concrete provider was used, is the file a provider adapter/transport or a focused adapter test?
If the code loops over providers, is the cross-provider product behavior explicit and documented?
Does every song/collection retain providerId and source-aware identity?
Are search, playback, lyrics, catalog, account, recommendation, and mutation results shared Omni types rather than raw provider data?
Are capability, availability, auth, network, and unsupported states preserved?
Are numeric IDs ever compared without provider identity? If yes, fix them.
Is there a focused test for the new routing or adapter behavior?