node build fixed

This commit is contained in:
ra_ma
2025-09-20 14:08:38 +01:00
parent c6ebbe069d
commit 3d298fa434
1516 changed files with 535727 additions and 2 deletions

View File

@@ -0,0 +1,218 @@
// This code was generated by codegen/main.go. DO NOT EDIT.
package events
const (
AddUnknownMediaEndpoint = "ANIME-COLLECTION-add-unknown-media"
AnilistListAnimeEndpoint = "ANILIST-anilist-list-anime"
AnilistListMangaEndpoint = "MANGA-anilist-list-manga"
AnilistListMissedSequelsEndpoint = "ANILIST-anilist-list-missed-sequels"
AnilistListRecentAiringAnimeEndpoint = "ANILIST-anilist-list-recent-airing-anime"
AnimeEntryBulkActionEndpoint = "ANIME-ENTRIES-anime-entry-bulk-action"
AnimeEntryManualMatchEndpoint = "ANIME-ENTRIES-anime-entry-manual-match"
CancelDiscordActivityEndpoint = "DISCORD-cancel-discord-activity"
ClearAllChapterDownloadQueueEndpoint = "MANGA-DOWNLOAD-clear-all-chapter-download-queue"
ClearFileCacheMediastreamVideoFilesEndpoint = "FILECACHE-clear-file-cache-mediastream-video-files"
CreateAutoDownloaderRuleEndpoint = "AUTO-DOWNLOADER-create-auto-downloader-rule"
CreatePlaylistEndpoint = "PLAYLIST-create-playlist"
DebridAddTorrentsEndpoint = "DEBRID-debrid-add-torrents"
DebridCancelDownloadEndpoint = "DEBRID-debrid-cancel-download"
DebridCancelStreamEndpoint = "DEBRID-debrid-cancel-stream"
DebridDeleteTorrentEndpoint = "DEBRID-debrid-delete-torrent"
DebridDownloadTorrentEndpoint = "DEBRID-debrid-download-torrent"
DebridGetTorrentFilePreviewsEndpoint = "DEBRID-debrid-get-torrent-file-previews"
DebridGetTorrentInfoEndpoint = "DEBRID-debrid-get-torrent-info"
DebridGetTorrentsEndpoint = "DEBRID-debrid-get-torrents"
DebridStartStreamEndpoint = "DEBRID-debrid-start-stream"
DeleteAnilistListEntryEndpoint = "ANILIST-delete-anilist-list-entry"
DeleteAutoDownloaderItemEndpoint = "AUTO-DOWNLOADER-delete-auto-downloader-item"
DeleteAutoDownloaderRuleEndpoint = "AUTO-DOWNLOADER-delete-auto-downloader-rule"
DeleteLocalFilesEndpoint = "LOCALFILES-delete-local-files"
DeleteLogsEndpoint = "STATUS-delete-logs"
DeleteMangaDownloadedChaptersEndpoint = "MANGA-DOWNLOAD-delete-manga-downloaded-chapters"
DeletePlaylistEndpoint = "PLAYLIST-delete-playlist"
DirectorySelectorEndpoint = "DIRECTORY-SELECTOR-directory-selector"
DirectstreamPlayLocalFileEndpoint = "DIRECTSTREAM-directstream-play-local-file"
DownloadIssueReportEndpoint = "REPORT-download-issue-report"
DownloadMangaChaptersEndpoint = "MANGA-DOWNLOAD-download-manga-chapters"
DownloadReleaseEndpoint = "DOWNLOAD-download-release"
DownloadTorrentFileEndpoint = "DOWNLOAD-download-torrent-file"
EditAnilistListEntryEndpoint = "ANILIST-edit-anilist-list-entry"
EditMALListEntryProgressEndpoint = "MAL-edit-mal-list-entry-progress"
EmptyMangaEntryCacheEndpoint = "MANGA-empty-manga-entry-cache"
FetchAnimeEntrySuggestionsEndpoint = "ANIME-ENTRIES-fetch-anime-entry-suggestions"
FetchExternalExtensionDataEndpoint = "EXTENSIONS-fetch-external-extension-data"
ForceGCEndpoint = "STATUS-force-g-c"
GetActiveTorrentListEndpoint = "TORRENT-CLIENT-get-active-torrent-list"
GetAllExtensionsEndpoint = "EXTENSIONS-get-all-extensions"
GetAniListStatsEndpoint = "ANILIST-get-ani-list-stats"
GetAnilistAnimeDetailsEndpoint = "ANILIST-get-anilist-anime-details"
GetAnilistMangaCollectionEndpoint = "MANGA-get-anilist-manga-collection"
GetAnilistStudioDetailsEndpoint = "ANILIST-get-anilist-studio-details"
GetAnimeCollectionEndpoint = "ANILIST-get-anime-collection"
GetAnimeCollectionScheduleEndpoint = "ANIME-COLLECTION-get-anime-collection-schedule"
GetAnimeEntryEndpoint = "ANIME-ENTRIES-get-anime-entry"
GetAnimeEntrySilenceStatusEndpoint = "ANIME-ENTRIES-get-anime-entry-silence-status"
GetAnimeEpisodeCollectionEndpoint = "ANIME-get-anime-episode-collection"
GetAnnouncementsEndpoint = "STATUS-get-announcements"
GetAutoDownloaderItemsEndpoint = "AUTO-DOWNLOADER-get-auto-downloader-items"
GetAutoDownloaderRuleEndpoint = "AUTO-DOWNLOADER-get-auto-downloader-rule"
GetAutoDownloaderRulesEndpoint = "AUTO-DOWNLOADER-get-auto-downloader-rules"
GetAutoDownloaderRulesByAnimeEndpoint = "AUTO-DOWNLOADER-get-auto-downloader-rules-by-anime"
GetCPUProfileEndpoint = "STATUS-get-c-p-u-profile"
GetChangelogEndpoint = "RELEASES-get-changelog"
GetContinuityWatchHistoryEndpoint = "CONTINUITY-get-continuity-watch-history"
GetContinuityWatchHistoryItemEndpoint = "CONTINUITY-get-continuity-watch-history-item"
GetDebridSettingsEndpoint = "DEBRID-get-debrid-settings"
GetDocsEndpoint = "DOCS-get-docs"
GetExtensionPayloadEndpoint = "EXTENSIONS-get-extension-payload"
GetExtensionUpdateDataEndpoint = "EXTENSIONS-get-extension-update-data"
GetExtensionUserConfigEndpoint = "EXTENSIONS-get-extension-user-config"
GetFileCacheMediastreamVideoFilesTotalSizeEndpoint = "FILECACHE-get-file-cache-mediastream-video-files-total-size"
GetFileCacheTotalSizeEndpoint = "FILECACHE-get-file-cache-total-size"
GetGoRoutineProfileEndpoint = "STATUS-get-go-routine-profile"
GetLatestLogContentEndpoint = "STATUS-get-latest-log-content"
GetLatestUpdateEndpoint = "RELEASES-get-latest-update"
GetLibraryCollectionEndpoint = "ANIME-COLLECTION-get-library-collection"
GetLocalFilesEndpoint = "LOCALFILES-get-local-files"
GetLocalMangaPageEndpoint = "MANGA-get-local-manga-page"
GetLogFilenamesEndpoint = "STATUS-get-log-filenames"
GetMangaCollectionEndpoint = "MANGA-get-manga-collection"
GetMangaDownloadDataEndpoint = "MANGA-DOWNLOAD-get-manga-download-data"
GetMangaDownloadQueueEndpoint = "MANGA-DOWNLOAD-get-manga-download-queue"
GetMangaDownloadsListEndpoint = "MANGA-DOWNLOAD-get-manga-downloads-list"
GetMangaEntryEndpoint = "MANGA-get-manga-entry"
GetMangaEntryChaptersEndpoint = "MANGA-get-manga-entry-chapters"
GetMangaEntryDetailsEndpoint = "MANGA-get-manga-entry-details"
GetMangaEntryDownloadedChaptersEndpoint = "MANGA-get-manga-entry-downloaded-chapters"
GetMangaEntryPagesEndpoint = "MANGA-get-manga-entry-pages"
GetMangaLatestChapterNumbersMapEndpoint = "MANGA-get-manga-latest-chapter-numbers-map"
GetMangaMappingEndpoint = "MANGA-get-manga-mapping"
GetMarketplaceExtensionsEndpoint = "EXTENSIONS-get-marketplace-extensions"
GetMediastreamSettingsEndpoint = "MEDIASTREAM-get-mediastream-settings"
GetMemoryProfileEndpoint = "STATUS-get-memory-profile"
GetMemoryStatsEndpoint = "STATUS-get-memory-stats"
GetMissingEpisodesEndpoint = "ANIME-ENTRIES-get-missing-episodes"
GetNakamaAnimeAllLibraryFilesEndpoint = "NAKAMA-get-nakama-anime-all-library-files"
GetNakamaAnimeLibraryEndpoint = "NAKAMA-get-nakama-anime-library"
GetNakamaAnimeLibraryCollectionEndpoint = "NAKAMA-get-nakama-anime-library-collection"
GetNakamaAnimeLibraryFilesEndpoint = "NAKAMA-get-nakama-anime-library-files"
GetOnlineStreamEpisodeListEndpoint = "ONLINESTREAM-get-online-stream-episode-list"
GetOnlineStreamEpisodeSourceEndpoint = "ONLINESTREAM-get-online-stream-episode-source"
GetOnlinestreamMappingEndpoint = "ONLINESTREAM-get-onlinestream-mapping"
GetPlaylistEpisodesEndpoint = "PLAYLIST-get-playlist-episodes"
GetPlaylistsEndpoint = "PLAYLIST-get-playlists"
GetPluginSettingsEndpoint = "EXTENSIONS-get-plugin-settings"
GetRawAnilistMangaCollectionEndpoint = "MANGA-get-raw-anilist-manga-collection"
GetRawAnimeCollectionEndpoint = "ANILIST-get-raw-anime-collection"
GetScanSummariesEndpoint = "SCAN-SUMMARY-get-scan-summaries"
GetSettingsEndpoint = "SETTINGS-get-settings"
GetStatusEndpoint = "STATUS-get-status"
GetThemeEndpoint = "THEME-get-theme"
GetTorrentstreamBatchHistoryEndpoint = "TORRENTSTREAM-get-torrentstream-batch-history"
GetTorrentstreamSettingsEndpoint = "TORRENTSTREAM-get-torrentstream-settings"
GetTorrentstreamTorrentFilePreviewsEndpoint = "TORRENTSTREAM-get-torrentstream-torrent-file-previews"
GettingStartedEndpoint = "SETTINGS-getting-started"
GrantPluginPermissionsEndpoint = "EXTENSIONS-grant-plugin-permissions"
ImportLocalFilesEndpoint = "LOCALFILES-import-local-files"
InstallExternalExtensionEndpoint = "EXTENSIONS-install-external-extension"
InstallLatestUpdateEndpoint = "RELEASES-install-latest-update"
ListAnimeTorrentProviderExtensionsEndpoint = "EXTENSIONS-list-anime-torrent-provider-extensions"
ListDevelopmentModeExtensionsEndpoint = "EXTENSIONS-list-development-mode-extensions"
ListExtensionDataEndpoint = "EXTENSIONS-list-extension-data"
ListMangaProviderExtensionsEndpoint = "EXTENSIONS-list-manga-provider-extensions"
ListOnlinestreamProviderExtensionsEndpoint = "EXTENSIONS-list-onlinestream-provider-extensions"
LocalAddTrackedMediaEndpoint = "LOCAL-local-add-tracked-media"
LocalFileBulkActionEndpoint = "LOCALFILES-local-file-bulk-action"
LocalGetHasLocalChangesEndpoint = "LOCAL-local-get-has-local-changes"
LocalGetIsMediaTrackedEndpoint = "LOCAL-local-get-is-media-tracked"
LocalGetLocalStorageSizeEndpoint = "LOCAL-local-get-local-storage-size"
LocalGetSyncQueueStateEndpoint = "LOCAL-local-get-sync-queue-state"
LocalGetTrackedMediaItemsEndpoint = "LOCAL-local-get-tracked-media-items"
LocalRemoveTrackedMediaEndpoint = "LOCAL-local-remove-tracked-media"
LocalSetHasLocalChangesEndpoint = "LOCAL-local-set-has-local-changes"
LocalSyncAnilistDataEndpoint = "LOCAL-local-sync-anilist-data"
LocalSyncDataEndpoint = "LOCAL-local-sync-data"
LocalSyncSimulatedDataToAnilistEndpoint = "LOCAL-local-sync-simulated-data-to-anilist"
LoginEndpoint = "AUTH-login"
LogoutEndpoint = "AUTH-logout"
MALAuthEndpoint = "MAL-mal-auth"
MALLogoutEndpoint = "MAL-mal-logout"
MangaManualMappingEndpoint = "MANGA-manga-manual-mapping"
MangaManualSearchEndpoint = "MANGA-manga-manual-search"
MediastreamShutdownTranscodeStreamEndpoint = "MEDIASTREAM-mediastream-shutdown-transcode-stream"
NakamaCreateWatchPartyEndpoint = "NAKAMA-nakama-create-watch-party"
NakamaJoinWatchPartyEndpoint = "NAKAMA-nakama-join-watch-party"
NakamaLeaveWatchPartyEndpoint = "NAKAMA-nakama-leave-watch-party"
NakamaPlayVideoEndpoint = "NAKAMA-nakama-play-video"
NakamaReconnectToHostEndpoint = "NAKAMA-nakama-reconnect-to-host"
NakamaRemoveStaleConnectionsEndpoint = "NAKAMA-nakama-remove-stale-connections"
NakamaWebSocketEndpoint = "NAKAMA-nakama-web-socket"
OnlineStreamEmptyCacheEndpoint = "ONLINESTREAM-online-stream-empty-cache"
OnlinestreamManualMappingEndpoint = "ONLINESTREAM-onlinestream-manual-mapping"
OnlinestreamManualSearchEndpoint = "ONLINESTREAM-onlinestream-manual-search"
OpenAnimeEntryInExplorerEndpoint = "ANIME-ENTRIES-open-anime-entry-in-explorer"
OpenInExplorerEndpoint = "EXPLORER-open-in-explorer"
PlaybackAutoPlayNextEpisodeEndpoint = "PLAYBACK-MANAGER-playback-auto-play-next-episode"
PlaybackCancelCurrentPlaylistEndpoint = "PLAYBACK-MANAGER-playback-cancel-current-playlist"
PlaybackCancelManualTrackingEndpoint = "PLAYBACK-MANAGER-playback-cancel-manual-tracking"
PlaybackGetNextEpisodeEndpoint = "PLAYBACK-MANAGER-playback-get-next-episode"
PlaybackPlayNextEpisodeEndpoint = "PLAYBACK-MANAGER-playback-play-next-episode"
PlaybackPlayRandomVideoEndpoint = "PLAYBACK-MANAGER-playback-play-random-video"
PlaybackPlayVideoEndpoint = "PLAYBACK-MANAGER-playback-play-video"
PlaybackPlaylistNextEndpoint = "PLAYBACK-MANAGER-playback-playlist-next"
PlaybackStartManualTrackingEndpoint = "PLAYBACK-MANAGER-playback-start-manual-tracking"
PlaybackStartPlaylistEndpoint = "PLAYBACK-MANAGER-playback-start-playlist"
PlaybackSyncCurrentProgressEndpoint = "PLAYBACK-MANAGER-playback-sync-current-progress"
PopulateFillerDataEndpoint = "METADATA-populate-filler-data"
PreloadMediastreamMediaContainerEndpoint = "MEDIASTREAM-preload-mediastream-media-container"
RefetchMangaChapterContainersEndpoint = "MANGA-refetch-manga-chapter-containers"
ReloadExternalExtensionEndpoint = "EXTENSIONS-reload-external-extension"
ReloadExternalExtensionsEndpoint = "EXTENSIONS-reload-external-extensions"
RemoveEmptyDirectoriesEndpoint = "LOCALFILES-remove-empty-directories"
RemoveFileCacheBucketEndpoint = "FILECACHE-remove-file-cache-bucket"
RemoveFillerDataEndpoint = "METADATA-remove-filler-data"
RemoveMangaMappingEndpoint = "MANGA-remove-manga-mapping"
RemoveOnlinestreamMappingEndpoint = "ONLINESTREAM-remove-onlinestream-mapping"
RequestMediastreamMediaContainerEndpoint = "MEDIASTREAM-request-mediastream-media-container"
ResetErroredChapterDownloadQueueEndpoint = "MANGA-DOWNLOAD-reset-errored-chapter-download-queue"
RunAutoDownloaderEndpoint = "AUTO-DOWNLOADER-run-auto-downloader"
RunExtensionPlaygroundCodeEndpoint = "EXTENSIONS-run-extension-playground-code"
SaveAutoDownloaderSettingsEndpoint = "SETTINGS-save-auto-downloader-settings"
SaveDebridSettingsEndpoint = "DEBRID-save-debrid-settings"
SaveExtensionUserConfigEndpoint = "EXTENSIONS-save-extension-user-config"
SaveIssueReportEndpoint = "REPORT-save-issue-report"
SaveMediastreamSettingsEndpoint = "MEDIASTREAM-save-mediastream-settings"
SaveSettingsEndpoint = "SETTINGS-save-settings"
SaveTorrentstreamSettingsEndpoint = "TORRENTSTREAM-save-torrentstream-settings"
ScanLocalFilesEndpoint = "SCAN-scan-local-files"
SearchTorrentEndpoint = "TORRENT-SEARCH-search-torrent"
SendNakamaMessageEndpoint = "NAKAMA-send-nakama-message"
SetDiscordAnimeActivityWithProgressEndpoint = "DISCORD-set-discord-anime-activity-with-progress"
SetDiscordLegacyAnimeActivityEndpoint = "DISCORD-set-discord-legacy-anime-activity"
SetDiscordMangaActivityEndpoint = "DISCORD-set-discord-manga-activity"
SetOfflineModeEndpoint = "LOCAL-set-offline-mode"
SetPluginSettingsPinnedTraysEndpoint = "EXTENSIONS-set-plugin-settings-pinned-trays"
StartDefaultMediaPlayerEndpoint = "MEDIAPLAYER-start-default-media-player"
StartMangaDownloadQueueEndpoint = "MANGA-DOWNLOAD-start-manga-download-queue"
StopMangaDownloadQueueEndpoint = "MANGA-DOWNLOAD-stop-manga-download-queue"
TestDumpEndpoint = "MANUAL-DUMP-test-dump"
ToggleAnimeEntrySilenceStatusEndpoint = "ANIME-ENTRIES-toggle-anime-entry-silence-status"
TorrentClientActionEndpoint = "TORRENT-CLIENT-torrent-client-action"
TorrentClientAddMagnetFromRuleEndpoint = "TORRENT-CLIENT-torrent-client-add-magnet-from-rule"
TorrentClientDownloadEndpoint = "TORRENT-CLIENT-torrent-client-download"
TorrentstreamDropTorrentEndpoint = "TORRENTSTREAM-torrentstream-drop-torrent"
TorrentstreamStartStreamEndpoint = "TORRENTSTREAM-torrentstream-start-stream"
TorrentstreamStopStreamEndpoint = "TORRENTSTREAM-torrentstream-stop-stream"
UninstallExternalExtensionEndpoint = "EXTENSIONS-uninstall-external-extension"
UpdateAnimeEntryProgressEndpoint = "ANIME-ENTRIES-update-anime-entry-progress"
UpdateAnimeEntryRepeatEndpoint = "ANIME-ENTRIES-update-anime-entry-repeat"
UpdateAutoDownloaderRuleEndpoint = "AUTO-DOWNLOADER-update-auto-downloader-rule"
UpdateContinuityWatchHistoryItemEndpoint = "CONTINUITY-update-continuity-watch-history-item"
UpdateDiscordAnimeActivityWithProgressEndpoint = "DISCORD-update-discord-anime-activity-with-progress"
UpdateExtensionCodeEndpoint = "EXTENSIONS-update-extension-code"
UpdateLocalFileDataEndpoint = "LOCALFILES-update-local-file-data"
UpdateLocalFilesEndpoint = "LOCALFILES-update-local-files"
UpdateMangaProgressEndpoint = "MANGA-update-manga-progress"
UpdatePlaylistEndpoint = "PLAYLIST-update-playlist"
UpdateThemeEndpoint = "THEME-update-theme"
)

View File

@@ -0,0 +1,98 @@
package events
type WebsocketClientEventType string
const (
NativePlayerEventType WebsocketClientEventType = "native-player"
NakamaEventType WebsocketClientEventType = "nakama"
PluginEvent WebsocketClientEventType = "plugin"
)
type WebsocketClientEvent struct {
ClientID string `json:"clientId"`
Type WebsocketClientEventType `json:"type"`
Payload interface{} `json:"payload"`
}
const (
ServerReady = "server-ready" // The anilist data has been loaded
EventScanProgress = "scan-progress" // Progress of the scan
EventScanStatus = "scan-status" // Status text of the scan
RefreshedAnilistAnimeCollection = "refreshed-anilist-anime-collection" // The anilist collection has been refreshed
RefreshedAnilistMangaCollection = "refreshed-anilist-manga-collection" // The manga collection has been refreshed
LibraryWatcherFileAdded = "library-watcher-file-added" // A new file has been added to the library
LibraryWatcherFileRemoved = "library-watcher-file-removed" // A file has been removed from the library
AutoDownloaderItemAdded = "auto-downloader-item-added" // An item has been added to the auto downloader queue
AutoScanStarted = "auto-scan-started" // The auto scan has started
AutoScanCompleted = "auto-scan-completed" // The auto scan has stopped
PlaybackManagerProgressTrackingStarted = "playback-manager-progress-tracking-started" // The video progress tracking has started
PlaybackManagerProgressTrackingStopped = "playback-manager-progress-tracking-stopped" // The video progress tracking has stopped
PlaybackManagerProgressVideoCompleted = "playback-manager-progress-video-completed" // The video progress has been completed
PlaybackManagerProgressPlaybackState = "playback-manager-progress-playback-state" // Dispatches the current playback state
PlaybackManagerProgressUpdated = "playback-manager-progress-updated" // Signals that the progress has been updated
PlaybackManagerPlaylistState = "playback-manager-playlist-state" // Dispatches the current playlist state
PlaybackManagerManualTrackingPlaybackState = "playback-manager-manual-tracking-playback-state" // Dispatches the current playback state
PlaybackManagerManualTrackingStopped = "playback-manager-manual-tracking-stopped" // The manual tracking has been stopped
ExternalPlayerOpenURL = "external-player-open-url" // Open a URL to send media to an external media player
InfoToast = "info-toast"
ErrorToast = "error-toast"
WarningToast = "warning-toast"
SuccessToast = "success-toast"
CheckForUpdates = "check-for-updates"
CheckForAnnouncements = "check-for-announcements"
RefreshedMangaDownloadData = "refreshed-manga-download-data"
ChapterDownloadQueueUpdated = "chapter-download-queue-updated"
OfflineSnapshotCreated = "offline-snapshot-created"
MediastreamShutdownStream = "mediastream-shutdown-stream"
ExtensionsReloaded = "extensions-reloaded"
ExtensionUpdatesFound = "extension-updates-found"
PluginUnloaded = "plugin-unloaded"
PluginLoaded = "plugin-loaded"
ActiveTorrentCountUpdated = "active-torrent-count-updated"
SyncLocalQueueState = "sync-local-queue-state"
SyncLocalFinished = "sync-local-finished"
SyncAnilistFinished = "sync-anilist-finished"
TorrentStreamState = "torrentstream-state"
DebridDownloadProgress = "debrid-download-progress"
DebridStreamState = "debrid-stream-state"
InvalidateQueries = "invalidate-queries"
ConsoleLog = "console-log"
ConsoleWarn = "console-warn"
ShowIndefiniteLoader = "show-indefinite-loader"
HideIndefiniteLoader = "hide-indefinite-loader"
// Nakama events
NakamaHostStarted = "nakama-host-started"
NakamaHostStopped = "nakama-host-stopped"
NakamaPeerConnected = "nakama-peer-connected"
NakamaPeerDisconnected = "nakama-peer-disconnected"
NakamaHostConnected = "nakama-host-connected"
NakamaHostDisconnected = "nakama-host-disconnected"
NakamaError = "nakama-error"
NakamaAnimeLibraryReceived = "nakama-anime-library-received"
NakamaCustomMessage = "nakama-custom-message"
NakamaStatusRequested = "nakama-status-requested"
NakamaStatus = "nakama-status"
NakamaOnlineStreamEvent = "nakama-online-stream-event"
// Nakama Watch Party events
NakamaWatchPartyState = "nakama-watch-party-state"
NakamaWatchPartyEnableRelayMode = "nakama-watch-party-enable-relay-mode"
NakamaWatchPartyRelayModeToggleShareLibraryWithOrigin = "nakama-watch-party-relay-mode-toggle-share-library-with-origin"
)

View File

@@ -0,0 +1,289 @@
package events
import (
"os"
"seanime/internal/util"
"seanime/internal/util/result"
"sync"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/gorilla/websocket"
"github.com/rs/zerolog"
)
type WSEventManagerInterface interface {
SendEvent(t string, payload interface{})
SendEventTo(clientId string, t string, payload interface{}, noLog ...bool)
SubscribeToClientEvents(id string) *ClientEventSubscriber
SubscribeToClientNativePlayerEvents(id string) *ClientEventSubscriber
SubscribeToClientNakamaEvents(id string) *ClientEventSubscriber
UnsubscribeFromClientEvents(id string)
}
type GlobalWSEventManagerWrapper struct {
WSEventManager WSEventManagerInterface
}
var GlobalWSEventManager *GlobalWSEventManagerWrapper
func (w *GlobalWSEventManagerWrapper) SendEvent(t string, payload interface{}) {
if w.WSEventManager == nil {
return
}
w.WSEventManager.SendEvent(t, payload)
}
func (w *GlobalWSEventManagerWrapper) SendEventTo(clientId string, t string, payload interface{}, noLog ...bool) {
if w.WSEventManager == nil {
return
}
w.WSEventManager.SendEventTo(clientId, t, payload, noLog...)
}
type (
// WSEventManager holds the websocket connection instance.
// It is attached to the App instance, so it is available to other handlers.
WSEventManager struct {
Conns []*WSConn
Logger *zerolog.Logger
hasHadConnection bool
mu sync.Mutex
eventMu sync.RWMutex
clientEventSubscribers *result.Map[string, *ClientEventSubscriber]
clientNativePlayerEventSubscribers *result.Map[string, *ClientEventSubscriber]
nakamaEventSubscribers *result.Map[string, *ClientEventSubscriber]
}
ClientEventSubscriber struct {
Channel chan *WebsocketClientEvent
mu sync.RWMutex
closed bool
}
WSConn struct {
ID string
Conn *websocket.Conn
}
WSEvent struct {
Type string `json:"type"`
Payload interface{} `json:"payload"`
}
)
// NewWSEventManager creates a new WSEventManager instance for App.
func NewWSEventManager(logger *zerolog.Logger) *WSEventManager {
ret := &WSEventManager{
Logger: logger,
Conns: make([]*WSConn, 0),
clientEventSubscribers: result.NewResultMap[string, *ClientEventSubscriber](),
clientNativePlayerEventSubscribers: result.NewResultMap[string, *ClientEventSubscriber](),
nakamaEventSubscribers: result.NewResultMap[string, *ClientEventSubscriber](),
}
GlobalWSEventManager = &GlobalWSEventManagerWrapper{
WSEventManager: ret,
}
return ret
}
// ExitIfNoConnsAsDesktopSidecar monitors the websocket connection as a desktop sidecar.
// It checks for a connection every 5 seconds. If a connection is lost, it starts a countdown a waits for 15 seconds.
// If a connection is not established within 15 seconds, it will exit the app.
func (m *WSEventManager) ExitIfNoConnsAsDesktopSidecar() {
go func() {
defer util.HandlePanicInModuleThen("events/ExitIfNoConnsAsDesktopSidecar", func() {})
m.Logger.Info().Msg("ws: Monitoring connection as desktop sidecar")
// Create a ticker to check connection every 5 seconds
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
// Track connection loss time
var connectionLostTime time.Time
exitTimeout := 10 * time.Second
for range ticker.C {
// Check WebSocket connection status
if len(m.Conns) == 0 && m.hasHadConnection {
// If not connected and first detection of connection loss
if connectionLostTime.IsZero() {
m.Logger.Warn().Msg("ws: No connection detected. Starting countdown...")
connectionLostTime = time.Now()
}
// Check if connection has been lost for more than 15 seconds
if time.Since(connectionLostTime) > exitTimeout {
m.Logger.Warn().Msg("ws: No connection detected for 10 seconds. Exiting...")
os.Exit(1)
}
} else {
// Connection is active, reset connection lost time
connectionLostTime = time.Time{}
}
}
}()
}
func (m *WSEventManager) AddConn(id string, conn *websocket.Conn) {
m.hasHadConnection = true
m.Conns = append(m.Conns, &WSConn{
ID: id,
Conn: conn,
})
}
func (m *WSEventManager) RemoveConn(id string) {
for i, conn := range m.Conns {
if conn.ID == id {
m.Conns = append(m.Conns[:i], m.Conns[i+1:]...)
break
}
}
}
// SendEvent sends a websocket event to the client.
func (m *WSEventManager) SendEvent(t string, payload interface{}) {
m.mu.Lock()
defer m.mu.Unlock()
// If there's no connection, do nothing
//if m.Conn == nil {
// return
//}
if t != PlaybackManagerProgressPlaybackState && payload == nil {
m.Logger.Trace().Str("type", t).Msg("ws: Sending message")
}
for _, conn := range m.Conns {
err := conn.Conn.WriteJSON(WSEvent{
Type: t,
Payload: payload,
})
if err != nil {
// Note: NaN error coming from [progress_tracking.go]
//m.Logger.Err(err).Msg("ws: Failed to send message")
}
//m.Logger.Trace().Str("type", t).Msg("ws: Sent message")
}
//err := m.Conn.WriteJSON(WSEvent{
// Type: t,
// Payload: payload,
//})
//if err != nil {
// m.Logger.Err(err).Msg("ws: Failed to send message")
//}
//m.Logger.Trace().Str("type", t).Msg("ws: Sent message")
}
// SendEventTo sends a websocket event to the specified client.
func (m *WSEventManager) SendEventTo(clientId string, t string, payload interface{}, noLog ...bool) {
m.mu.Lock()
defer m.mu.Unlock()
for _, conn := range m.Conns {
if conn.ID == clientId {
if t != "pong" {
if len(noLog) == 0 || !noLog[0] {
truncated := spew.Sprint(payload)
if len(truncated) > 500 {
truncated = truncated[:500] + "..."
}
m.Logger.Trace().Str("to", clientId).Str("type", t).Str("payload", truncated).Msg("ws: Sending message")
}
}
_ = conn.Conn.WriteJSON(WSEvent{
Type: t,
Payload: payload,
})
}
}
}
func (m *WSEventManager) SendStringTo(clientId string, s string) {
m.mu.Lock()
defer m.mu.Unlock()
for _, conn := range m.Conns {
if conn.ID == clientId {
_ = conn.Conn.WriteMessage(websocket.TextMessage, []byte(s))
}
}
}
func (m *WSEventManager) OnClientEvent(event *WebsocketClientEvent) {
m.eventMu.RLock()
defer m.eventMu.RUnlock()
onEvent := func(key string, subscriber *ClientEventSubscriber) bool {
go func() {
defer util.HandlePanicInModuleThen("events/OnClientEvent/clientNativePlayerEventSubscribers", func() {})
subscriber.mu.RLock()
defer subscriber.mu.RUnlock()
if !subscriber.closed {
select {
case subscriber.Channel <- event:
default:
// Channel is blocked, skip sending
m.Logger.Warn().Msg("ws: Client event channel is blocked, event dropped")
}
}
}()
return true
}
switch event.Type {
case NativePlayerEventType:
m.clientNativePlayerEventSubscribers.Range(onEvent)
case NakamaEventType:
m.nakamaEventSubscribers.Range(onEvent)
default:
m.clientEventSubscribers.Range(onEvent)
}
}
func (m *WSEventManager) SubscribeToClientEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent, 900),
}
m.clientEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *WSEventManager) SubscribeToClientNativePlayerEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent, 100),
}
m.clientNativePlayerEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *WSEventManager) SubscribeToClientNakamaEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent, 100),
}
m.nakamaEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *WSEventManager) UnsubscribeFromClientEvents(id string) {
m.eventMu.Lock()
defer m.eventMu.Unlock()
defer func() {
if r := recover(); r != nil {
m.Logger.Warn().Msg("ws: Failed to unsubscribe from client events")
}
}()
subscriber, ok := m.clientEventSubscribers.Get(id)
if !ok {
subscriber, ok = m.clientNativePlayerEventSubscribers.Get(id)
}
if ok {
subscriber.mu.Lock()
defer subscriber.mu.Unlock()
subscriber.closed = true
m.clientEventSubscribers.Delete(id)
close(subscriber.Channel)
}
}

View File

@@ -0,0 +1,75 @@
package events
import (
"seanime/internal/util/result"
"github.com/rs/zerolog"
)
type (
MockWSEventManager struct {
Conn interface{}
Logger *zerolog.Logger
ClientEventSubscribers *result.Map[string, *ClientEventSubscriber]
}
MockWSEvent struct {
Type string `json:"type"`
Payload interface{} `json:"payload"`
}
)
func NewMockWSEventManager(logger *zerolog.Logger) *MockWSEventManager {
return &MockWSEventManager{
Logger: logger,
ClientEventSubscribers: result.NewResultMap[string, *ClientEventSubscriber](),
}
}
// SendEvent sends a websocket event to the client.
func (m *MockWSEventManager) SendEvent(t string, payload interface{}) {
m.Logger.Trace().Any("payload", payload).Str("type", t).Msg("ws: Sent message")
}
func (m *MockWSEventManager) SendEventTo(clientId string, t string, payload interface{}, noLog ...bool) {
if len(noLog) == 0 || !noLog[0] {
m.Logger.Trace().Any("payload", payload).Str("type", t).Str("clientId", clientId).Msg("ws: Sent message to client")
}
}
func (m *MockWSEventManager) SubscribeToClientEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent),
}
m.ClientEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *MockWSEventManager) SubscribeToClientNativePlayerEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent),
}
m.ClientEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *MockWSEventManager) SubscribeToClientNakamaEvents(id string) *ClientEventSubscriber {
subscriber := &ClientEventSubscriber{
Channel: make(chan *WebsocketClientEvent),
}
m.ClientEventSubscribers.Set(id, subscriber)
return subscriber
}
func (m *MockWSEventManager) UnsubscribeFromClientEvents(id string) {
m.ClientEventSubscribers.Delete(id)
}
////
func (m *MockWSEventManager) MockSendClientEvent(event *WebsocketClientEvent) {
m.ClientEventSubscribers.Range(func(key string, subscriber *ClientEventSubscriber) bool {
subscriber.Channel <- event
return true
})
}