diff --git a/codegen/generated/hooks.json b/codegen/generated/hooks.json index 98780dff..88a6ef7d 100644 --- a/codegen/generated/hooks.json +++ b/codegen/generated/hooks.json @@ -6622,6 +6622,131 @@ ] } }, + { + "package": "platform", + "goStruct": { + "filepath": "../internal/platforms/platform/hook_events.go", + "filename": "hook_events.go", + "name": "PreDeleteEntryEvent", + "formattedName": "PreDeleteEntryEvent", + "package": "platform", + "fields": [ + { + "name": "MediaID", + "jsonName": "mediaId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "EntryID", + "jsonName": "entryId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "next", + "jsonName": "next", + "goType": "", + "typescriptType": "any", + "required": true, + "public": false, + "comments": [] + }, + { + "name": "preventDefault", + "jsonName": "preventDefault", + "goType": "", + "typescriptType": "any", + "required": true, + "public": false, + "comments": [] + }, + { + "name": "DefaultPrevented", + "jsonName": "defaultPrevented", + "goType": "bool", + "typescriptType": "boolean", + "required": true, + "public": true, + "comments": [] + } + ], + "comments": [ + " PreDeleteEntryEvent is triggered when an entry is about to be deleted.", + " Prevent default to skip the default deletion and override the deletion." + ], + "embeddedStructNames": [ + "hook_resolver.Event" + ] + } + }, + { + "package": "platform", + "goStruct": { + "filepath": "../internal/platforms/platform/hook_events.go", + "filename": "hook_events.go", + "name": "PostDeleteEntryEvent", + "formattedName": "PostDeleteEntryEvent", + "package": "platform", + "fields": [ + { + "name": "MediaID", + "jsonName": "mediaId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "EntryID", + "jsonName": "entryId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "next", + "jsonName": "next", + "goType": "", + "typescriptType": "any", + "required": true, + "public": false, + "comments": [] + }, + { + "name": "preventDefault", + "jsonName": "preventDefault", + "goType": "", + "typescriptType": "any", + "required": true, + "public": false, + "comments": [] + }, + { + "name": "DefaultPrevented", + "jsonName": "defaultPrevented", + "goType": "bool", + "typescriptType": "boolean", + "required": true, + "public": true, + "comments": [] + } + ], + "comments": [], + "embeddedStructNames": [ + "hook_resolver.Event" + ] + } + }, { "package": "torrentstream", "goStruct": { diff --git a/codegen/generated/public_structs.json b/codegen/generated/public_structs.json index 277df919..148d4dba 100644 --- a/codegen/generated/public_structs.json +++ b/codegen/generated/public_structs.json @@ -42275,6 +42275,63 @@ ], "comments": [] }, + { + "filepath": "../internal/hook/helper.go", + "filename": "helper.go", + "name": "HookTriggerOptions", + "formattedName": "HookTriggerOptions", + "package": "hook", + "fields": [ + { + "name": "Event", + "jsonName": "Event", + "goType": "T", + "typescriptType": "T", + "usedTypescriptType": "T", + "usedStructName": "hook.T", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Hook", + "jsonName": "Hook", + "goType": "", + "typescriptType": "any", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "OnError", + "jsonName": "OnError", + "goType": "", + "typescriptType": "any", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "OnDefaultPrevented", + "jsonName": "OnDefaultPrevented", + "goType": "", + "typescriptType": "any", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "OnSuccess", + "jsonName": "OnSuccess", + "goType": "", + "typescriptType": "any", + "required": true, + "public": true, + "comments": [] + } + ], + "comments": [] + }, { "filepath": "../internal/hook/hook.go", "filename": "hook.go", @@ -42555,6 +42612,24 @@ "public": false, "comments": [] }, + { + "name": "onPreDeleteEntry", + "jsonName": "onPreDeleteEntry", + "goType": "", + "typescriptType": "any", + "required": false, + "public": false, + "comments": [] + }, + { + "name": "onPostDeleteEntry", + "jsonName": "onPostDeleteEntry", + "goType": "", + "typescriptType": "any", + "required": false, + "public": false, + "comments": [] + }, { "name": "onAnimeEntryRequested", "jsonName": "onAnimeEntryRequested", @@ -66322,6 +66397,391 @@ ], "comments": [] }, + { + "filepath": "../internal/pgs/pgs.go", + "filename": "pgs.go", + "name": "PgsDecoder", + "formattedName": "PgsDecoder", + "package": "pgs", + "fields": [ + { + "name": "Palette", + "jsonName": "Palette", + "goType": "color.Palette", + "typescriptType": "Palette", + "usedTypescriptType": "Palette", + "usedStructName": "color.Palette", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "currentObject", + "jsonName": "currentObject", + "goType": "PgsObject", + "typescriptType": "PgsObject", + "usedTypescriptType": "PgsObject", + "usedStructName": "pgs.PgsObject", + "required": false, + "public": false, + "comments": [] + }, + { + "name": "currentComposition", + "jsonName": "currentComposition", + "goType": "PgsComposition", + "typescriptType": "PgsComposition", + "usedTypescriptType": "PgsComposition", + "usedStructName": "pgs.PgsComposition", + "required": false, + "public": false, + "comments": [] + }, + { + "name": "objects", + "jsonName": "objects", + "goType": "map[uint16]PgsObject", + "typescriptType": "Record\u003cnumber, PgsObject\u003e", + "usedTypescriptType": "PgsObject", + "usedStructName": "pgs.PgsObject", + "required": false, + "public": false, + "comments": [ + " Store completed objects by ID" + ] + }, + { + "name": "windows", + "jsonName": "windows", + "goType": "map[uint8]WindowDefinition", + "typescriptType": "Record\u003cnumber, WindowDefinition\u003e", + "usedTypescriptType": "WindowDefinition", + "usedStructName": "pgs.WindowDefinition", + "required": false, + "public": false, + "comments": [] + } + ], + "comments": [ + " PgsDecoder decodes PGS packets into images" + ] + }, + { + "filepath": "../internal/pgs/pgs.go", + "filename": "pgs.go", + "name": "PgsObject", + "formattedName": "PgsObject", + "package": "pgs", + "fields": [ + { + "name": "ID", + "jsonName": "ID", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Version", + "jsonName": "Version", + "goType": "uint8", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Width", + "jsonName": "Width", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Height", + "jsonName": "Height", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Data", + "jsonName": "Data", + "goType": "string", + "typescriptType": "Array\u003cstring\u003e", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "Complete", + "jsonName": "Complete", + "goType": "bool", + "typescriptType": "boolean", + "required": true, + "public": true, + "comments": [] + } + ], + "comments": [] + }, + { + "filepath": "../internal/pgs/pgs.go", + "filename": "pgs.go", + "name": "PgsComposition", + "formattedName": "PgsComposition", + "package": "pgs", + "fields": [ + { + "name": "PTS", + "jsonName": "PTS", + "goType": "uint64", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [ + " Presentation timestamp" + ] + }, + { + "name": "DTS", + "jsonName": "DTS", + "goType": "uint64", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [ + " Decode timestamp" + ] + }, + { + "name": "Width", + "jsonName": "Width", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Height", + "jsonName": "Height", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "FrameRate", + "jsonName": "FrameRate", + "goType": "uint8", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "CompositionNum", + "jsonName": "CompositionNum", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "CompositionState", + "jsonName": "CompositionState", + "goType": "uint8", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "PaletteUpdate", + "jsonName": "PaletteUpdate", + "goType": "bool", + "typescriptType": "boolean", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "PaletteID", + "jsonName": "PaletteID", + "goType": "uint8", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Objects", + "jsonName": "Objects", + "goType": "[]CompositionObject", + "typescriptType": "Array\u003cCompositionObject\u003e", + "usedTypescriptType": "CompositionObject", + "usedStructName": "pgs.CompositionObject", + "required": false, + "public": true, + "comments": [] + } + ], + "comments": [] + }, + { + "filepath": "../internal/pgs/pgs.go", + "filename": "pgs.go", + "name": "CompositionObject", + "formattedName": "CompositionObject", + "package": "pgs", + "fields": [ + { + "name": "ObjectID", + "jsonName": "ObjectID", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "WindowID", + "jsonName": "WindowID", + "goType": "uint8", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "X", + "jsonName": "X", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Y", + "jsonName": "Y", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Cropped", + "jsonName": "Cropped", + "goType": "bool", + "typescriptType": "boolean", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "CropX", + "jsonName": "CropX", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "CropY", + "jsonName": "CropY", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "CropWidth", + "jsonName": "CropWidth", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "CropHeight", + "jsonName": "CropHeight", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + } + ], + "comments": [] + }, + { + "filepath": "../internal/pgs/pgs.go", + "filename": "pgs.go", + "name": "WindowDefinition", + "formattedName": "WindowDefinition", + "package": "pgs", + "fields": [ + { + "name": "WindowID", + "jsonName": "WindowID", + "goType": "uint8", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "X", + "jsonName": "X", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Y", + "jsonName": "Y", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Width", + "jsonName": "Width", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + }, + { + "name": "Height", + "jsonName": "Height", + "goType": "uint16", + "typescriptType": "number", + "required": true, + "public": true, + "comments": [] + } + ], + "comments": [] + }, { "filepath": "../internal/platforms/anilist_platform/anilist_platform.go", "filename": "anilist_platform.go", @@ -67034,6 +67494,71 @@ "hook_resolver.Event" ] }, + { + "filepath": "../internal/platforms/platform/hook_events.go", + "filename": "hook_events.go", + "name": "PreDeleteEntryEvent", + "formattedName": "PreDeleteEntryEvent", + "package": "platform", + "fields": [ + { + "name": "MediaID", + "jsonName": "mediaId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "EntryID", + "jsonName": "entryId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + } + ], + "comments": [ + " PreDeleteEntryEvent is triggered when an entry is about to be deleted.", + " Prevent default to skip the default deletion and override the deletion." + ], + "embeddedStructNames": [ + "hook_resolver.Event" + ] + }, + { + "filepath": "../internal/platforms/platform/hook_events.go", + "filename": "hook_events.go", + "name": "PostDeleteEntryEvent", + "formattedName": "PostDeleteEntryEvent", + "package": "platform", + "fields": [ + { + "name": "MediaID", + "jsonName": "mediaId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + }, + { + "name": "EntryID", + "jsonName": "entryId", + "goType": "int", + "typescriptType": "number", + "required": false, + "public": true, + "comments": [] + } + ], + "comments": [], + "embeddedStructNames": [ + "hook_resolver.Event" + ] + }, { "filepath": "../internal/platforms/shared_platform/cachelayer.go", "filename": "cachelayer.go", diff --git a/internal/api/metadata_provider/provider.go b/internal/api/metadata_provider/provider.go index 928c1cc2..972634ff 100644 --- a/internal/api/metadata_provider/provider.go +++ b/internal/api/metadata_provider/provider.go @@ -51,6 +51,7 @@ type ( GetAnimeMetadataWrapper(anime *anilist.BaseAnime, metadata *metadata.AnimeMetadata) AnimeMetadataWrapper GetCache() *result.BoundedCache[string, *metadata.AnimeMetadata] SetUseFallbackProvider(bool) + ClearCache() Close() } @@ -87,6 +88,10 @@ func (p *ProviderImpl) Close() { go p.animeMetadataCache.Clear() } +func (p *ProviderImpl) ClearCache() { + p.animeMetadataCache.Clear() +} + // GetCache returns the anime metadata cache. func (p *ProviderImpl) GetCache() *result.BoundedCache[string, *metadata.AnimeMetadata] { return p.animeMetadataCache diff --git a/internal/core/flags.go b/internal/core/flags.go index fc413ea4..a0bcc14e 100644 --- a/internal/core/flags.go +++ b/internal/core/flags.go @@ -33,15 +33,15 @@ func GetSeanimeFlags() SeanimeFlags { fmt.Printf("Usage: seanime [flags]\n\n") } fmt.Printf("Flags:\n") - fmt.Printf(" -datadir string directory that contains all Seanime data\n") - fmt.Printf(" -host string host address to bind to (default: 127.0.0.1)\n") - fmt.Printf(" -port int port to bind to (default: 43211)\n") - fmt.Printf(" -update update the application\n") - fmt.Printf(" -desktop-sidecar run as the desktop sidecar\n") - fmt.Printf(" -disable-features string comma-separated list of features to disable\n") - fmt.Printf(" -disable-all-features disable all features that can be disabled\n") - fmt.Printf(" -password string password to use for the instance\n") - fmt.Printf(" -disable-password disable password protection\n") + fmt.Printf(" --datadir string directory that contains all Seanime data\n") + fmt.Printf(" --host string host address to bind to (default: 127.0.0.1)\n") + fmt.Printf(" --port int port to bind to (default: 43211)\n") + fmt.Printf(" --update update the application\n") + fmt.Printf(" --desktop-sidecar run as the desktop sidecar\n") + fmt.Printf(" --disable-features string comma-separated list of features to disable\n") + fmt.Printf(" --disable-all-features disable all features that can be disabled\n") + fmt.Printf(" --password string password to use for the instance\n") + fmt.Printf(" --disable-password disable password protection\n") fmt.Printf(" -h show this help message\n") } diff --git a/internal/extension_repo/goja_plugin_types/app.d.ts b/internal/extension_repo/goja_plugin_types/app.d.ts index 2ac3ba2d..fcc238e1 100644 --- a/internal/extension_repo/goja_plugin_types/app.d.ts +++ b/internal/extension_repo/goja_plugin_types/app.d.ts @@ -1367,6 +1367,37 @@ declare namespace $app { mediaId?: number; } + /** + * @event PreDeleteEntryEvent + * @file internal/platforms/platform/hook_events.go + * @description + * PreDeleteEntryEvent is triggered when an entry is about to be deleted. + * Prevent default to skip the default deletion and override the deletion. + */ + function onPreDeleteEntry(cb: (event: PreDeleteEntryEvent) => void): void; + + interface PreDeleteEntryEvent { + mediaId?: number; + entryId?: number; + + next(): void; + + preventDefault(): void; + } + + /** + * @event PostDeleteEntryEvent + * @file internal/platforms/platform/hook_events.go + */ + function onPostDeleteEntry(cb: (event: PostDeleteEntryEvent) => void): void; + + interface PostDeleteEntryEvent { + mediaId?: number; + entryId?: number; + + next(): void; + } + /** * @package playbackmanager diff --git a/internal/extension_repo/goja_plugin_types/plugin.d.ts b/internal/extension_repo/goja_plugin_types/plugin.d.ts index d82f62fd..8e8f509f 100644 --- a/internal/extension_repo/goja_plugin_types/plugin.d.ts +++ b/internal/extension_repo/goja_plugin_types/plugin.d.ts @@ -1085,6 +1085,12 @@ declare namespace $ui { * @throws Error if a needed repository is not found */ getAnimeEntry(mediaId: number): Promise<$app.Anime_Entry> + + /** + * Clears episode metadata cache. + * Note: To clear the anime entry cache, use $anilist.clearCache() (requires 'anilist' permission). + */ + clearEpisodeMetadataCache(): void } interface Manga { @@ -1380,6 +1386,11 @@ declare namespace $storage { } declare namespace $anilist { + /** + * Deletes all cached data. + */ + function clearCache(): void + /** * Refresh the anime collection. * This will cause the frontend to refetch queries that depend on the anime collection. diff --git a/internal/hook/README.md b/internal/hook/README.md index 7948471b..8690a76d 100644 --- a/internal/hook/README.md +++ b/internal/hook/README.md @@ -10,26 +10,3 @@ - Called before creation of a struct - Native job cannot be interrupted even if `e.next()` isn't called - Followed by event containing the struct, e.g. `onAnimeEntry` - - -### TODO - -- [ ] Scanning -- [ ] Torrent client -- [ ] Torrent search -- [ ] AutoDownloader -- [ ] Torrent streaming -- [ ] Debrid / Debrid streaming -- [ ] PlaybackManager -- [ ] Media Player -- [ ] Sync / Offline -- [ ] Online streaming -- [ ] Metadata provider -- [ ] Manga -- [ ] Media streaming - ---- - -- [ ] Command palette -- [ ] Database -- [ ] App Context \ No newline at end of file diff --git a/internal/hook/helper.go b/internal/hook/helper.go new file mode 100644 index 00000000..872d7da9 --- /dev/null +++ b/internal/hook/helper.go @@ -0,0 +1,65 @@ +package hook + +import "seanime/internal/hook_resolver" + +type HookTriggerOptions[T hook_resolver.Resolver] struct { + Event T + Hook func() *Hook[hook_resolver.Resolver] + OnError func(error) error + OnDefaultPrevented func() error + OnSuccess func() error +} + +// TriggerHook triggers the given hook with the provided event and handles +// +// Example: +// ok, err := hook.TriggerHook(&hook.HookTriggerOptions[*MissingEpisodesRequestedEvent]{ +// Hook: hook.GlobalHookManager.OnMissingEpisodesRequested, +// Event: reqEvent, +// OnDefaultPrevented: func() error { +// event := new(MissingEpisodesEvent) +// event.MissingEpisodes = missing +// err = hook.GlobalHookManager.OnMissingEpisodes().Trigger(event) +// if err != nil { +// return nil +// } +// missing = event.MissingEpisodes +// return nil +// }, +// OnSuccess: func() error { +// opts.AnimeCollection = reqEvent.AnimeCollection // Override the anime collection +// opts.LocalFiles = reqEvent.LocalFiles // Override the local files +// opts.SilencedMediaIds = reqEvent.SilencedMediaIds // Override the silenced media IDs +// missing = reqEvent.MissingEpisodes +// return nil +// }, +// }) +// if err != nil { +// return nil +// } +// if !ok { +// return missing +// } +func TriggerHook[T hook_resolver.Resolver](opts *HookTriggerOptions[T]) (cont bool, _ error) { + if err := opts.Hook().Trigger(opts.Event); err != nil { + // The hook errored out + if opts.OnError != nil { + return false, opts.OnError(err) + } + return false, err + } + + // Default prevented + if opts.Event.IsDefaultPrevented() { + if opts.OnDefaultPrevented != nil { + return false, opts.OnDefaultPrevented() + } + // No error but don't continue + return false, nil + } + + if opts.OnSuccess != nil { + return true, opts.OnSuccess() + } + return true, nil +} diff --git a/internal/hook/hooks.go b/internal/hook/hooks.go index 17b35f18..31f46900 100644 --- a/internal/hook/hooks.go +++ b/internal/hook/hooks.go @@ -29,6 +29,8 @@ type Manager interface { OnPostUpdateEntryProgress() *Hook[hook_resolver.Resolver] OnPreUpdateEntryRepeat() *Hook[hook_resolver.Resolver] OnPostUpdateEntryRepeat() *Hook[hook_resolver.Resolver] + OnPreDeleteEntry() *Hook[hook_resolver.Resolver] + OnPostDeleteEntry() *Hook[hook_resolver.Resolver] // Anime library events OnAnimeEntryRequested() *Hook[hook_resolver.Resolver] @@ -171,6 +173,8 @@ type ManagerImpl struct { onPostUpdateEntryProgress *Hook[hook_resolver.Resolver] onPreUpdateEntryRepeat *Hook[hook_resolver.Resolver] onPostUpdateEntryRepeat *Hook[hook_resolver.Resolver] + onPreDeleteEntry *Hook[hook_resolver.Resolver] + onPostDeleteEntry *Hook[hook_resolver.Resolver] // Anime library events onAnimeEntryRequested *Hook[hook_resolver.Resolver] onAnimeEntry *Hook[hook_resolver.Resolver] @@ -311,6 +315,8 @@ func (m *ManagerImpl) initHooks() { m.onPostUpdateEntryProgress = &Hook[hook_resolver.Resolver]{} m.onPreUpdateEntryRepeat = &Hook[hook_resolver.Resolver]{} m.onPostUpdateEntryRepeat = &Hook[hook_resolver.Resolver]{} + m.onPreDeleteEntry = &Hook[hook_resolver.Resolver]{} + m.onPostDeleteEntry = &Hook[hook_resolver.Resolver]{} // Anime library events m.onAnimeEntryRequested = &Hook[hook_resolver.Resolver]{} m.onAnimeEntry = &Hook[hook_resolver.Resolver]{} @@ -541,6 +547,20 @@ func (m *ManagerImpl) OnPostUpdateEntryRepeat() *Hook[hook_resolver.Resolver] { return m.onPostUpdateEntryRepeat } +func (m *ManagerImpl) OnPreDeleteEntry() *Hook[hook_resolver.Resolver] { + if m == nil { + return &Hook[hook_resolver.Resolver]{} + } + return m.onPreDeleteEntry +} + +func (m *ManagerImpl) OnPostDeleteEntry() *Hook[hook_resolver.Resolver] { + if m == nil { + return &Hook[hook_resolver.Resolver]{} + } + return m.onPostDeleteEntry +} + // Anime entry events func (m *ManagerImpl) OnAnimeEntryRequested() *Hook[hook_resolver.Resolver] { diff --git a/internal/hook_resolver/hook_resolver.go b/internal/hook_resolver/hook_resolver.go index 7944c0c6..12422e07 100644 --- a/internal/hook_resolver/hook_resolver.go +++ b/internal/hook_resolver/hook_resolver.go @@ -10,6 +10,8 @@ type Resolver interface { // PreventDefault prevents the native handler from being called. PreventDefault() + IsDefaultPrevented() bool + SetNextFunc(f func() error) } @@ -44,6 +46,10 @@ func (e *Event) PreventDefault() { e.DefaultPrevented = true } +func (e *Event) IsDefaultPrevented() bool { + return e.DefaultPrevented +} + // NextFunc returns the function that Next calls. func (e *Event) NextFunc() func() error { return e.next diff --git a/internal/library/anime/episode_collection.go b/internal/library/anime/episode_collection.go index 88956e3d..17464788 100644 --- a/internal/library/anime/episode_collection.go +++ b/internal/library/anime/episode_collection.go @@ -21,6 +21,11 @@ import ( var episodeCollectionCache = result.NewBoundedCache[int, *EpisodeCollection](10) var EpisodeCollectionFromLocalFilesCache = result.NewBoundedCache[int, *EpisodeCollection](10) +func ClearEpisodeCollectionCache() { + episodeCollectionCache.Clear() + EpisodeCollectionFromLocalFilesCache.Clear() +} + type ( // EpisodeCollection represents a collection of episodes. EpisodeCollection struct { @@ -187,10 +192,6 @@ func NewEpisodeCollection(opts NewEpisodeCollectionOptions) (ec *EpisodeCollecti return } -func ClearEpisodeCollectionCache() { - episodeCollectionCache.Clear() -} - ///////// type NewEpisodeCollectionFromLocalFilesOptions struct { diff --git a/internal/local/metadata.go b/internal/local/metadata.go index 6b9ff100..e0b2d7f7 100644 --- a/internal/local/metadata.go +++ b/internal/local/metadata.go @@ -40,6 +40,10 @@ func (mp *OfflineMetadataProvider) Close() { // no-op } +func (mp *OfflineMetadataProvider) ClearCache() { + // no-op +} + func (mp *OfflineMetadataProvider) SetUseFallbackProvider(useFallback bool) { // no-op } diff --git a/internal/nakama/TODO.md b/internal/nakama/TODO.md new file mode 100644 index 00000000..8ce1ef1f --- /dev/null +++ b/internal/nakama/TODO.md @@ -0,0 +1,36 @@ +# TODO + +- Make a generic interface that combines PlaybackManager, DirectstreamManager/NativePlayer, OnlinestreamPlayer + - Generic events & calls so watch party manager doesn't need to know implementation details + - Generic player state + +```go +package main + +func main() { + wpm.playback.listenToPlayerEvents() + + wpm.playback.StartLocalFileStream(...) + wpm.playback.StartTorrentStream(...) + wpm.playback.StartOnlineStream(...) + wpm.playback.StartDebridStream(...) + + type PlaybackStatus struct { + ID string `json:"id"` // path or url + CompletionPercentage float64 `json:"completionPercentage"` + Playing bool `json:"playing"` + CurrentTime float64 `json:"currentTimeInSeconds"` // in seconds + Duration float64 `json:"durationInSeconds"` // in seconds + PlaybackType PlaybackType `json:"playbackType"` // file, torrentstream, onlinestream, debridstream + } + type PlaybackState struct { + EpisodeNumber int `json:"episodeNumber"` // The episode number + AniDbEpisode string `json:"aniDbEpisode"` // The AniDB episode number + MediaTitle string `json:"mediaTitle"` // The title of the media + MediaCoverImage string `json:"mediaCoverImage"` // The cover image of the media + MediaTotalEpisodes int `json:"mediaTotalEpisodes"` // The total number of episodes + MediaId int `json:"mediaId"` // The media ID + } +} + +``` diff --git a/internal/platforms/anilist_platform/anilist_platform.go b/internal/platforms/anilist_platform/anilist_platform.go index c256ad49..a562963f 100644 --- a/internal/platforms/anilist_platform/anilist_platform.go +++ b/internal/platforms/anilist_platform/anilist_platform.go @@ -159,16 +159,17 @@ func (ap *AnilistPlatform) UpdateEntryRepeat(ctx context.Context, mediaID int, r func (ap *AnilistPlatform) DeleteEntry(ctx context.Context, mediaID, entryId int) error { ap.logger.Trace().Msg("anilist platform: Deleting entry") - // Check if this is a custom source entry - if handled, err := ap.helper.HandleCustomSourceDeleteEntry(ctx, mediaID, entryId); handled { - return err - } + return ap.helper.TriggerDeleteEntryHooks(ctx, mediaID, entryId, func(event *platform.PreDeleteEntryEvent) error { + if handled, err := ap.helper.HandleCustomSourceDeleteEntry(ctx, *event.MediaID, *event.EntryID); handled { + return err + } - _, err := ap.anilistClient.DeleteEntry(ctx, &entryId) - if err != nil { - return err - } - return nil + _, err := ap.anilistClient.DeleteEntry(ctx, event.EntryID) + if err != nil { + return err + } + return nil + }) } func (ap *AnilistPlatform) GetAnime(ctx context.Context, mediaID int) (*anilist.BaseAnime, error) { diff --git a/internal/platforms/platform/hook_events.go b/internal/platforms/platform/hook_events.go index f8913ea7..a26dd202 100644 --- a/internal/platforms/platform/hook_events.go +++ b/internal/platforms/platform/hook_events.go @@ -119,3 +119,17 @@ type PostUpdateEntryRepeatEvent struct { hook_resolver.Event MediaID *int `json:"mediaId"` } + +// PreDeleteEntryEvent is triggered when an entry is about to be deleted. +// Prevent default to skip the default deletion and override the deletion. +type PreDeleteEntryEvent struct { + hook_resolver.Event + MediaID *int `json:"mediaId"` + EntryID *int `json:"entryId"` +} + +type PostDeleteEntryEvent struct { + hook_resolver.Event + MediaID *int `json:"mediaId"` + EntryID *int `json:"entryId"` +} diff --git a/internal/platforms/shared_platform/shared.go b/internal/platforms/shared_platform/shared.go index 2c7cafa5..1f3b2e17 100644 --- a/internal/platforms/shared_platform/shared.go +++ b/internal/platforms/shared_platform/shared.go @@ -478,6 +478,35 @@ func (h *PlatformHelper) TriggerUpdateEntryRepeatHooks(ctx context.Context, medi return err } +func (h *PlatformHelper) TriggerDeleteEntryHooks(ctx context.Context, mediaID int, entryId int, deleteFunc func(event *platform.PreDeleteEntryEvent) error) error { + // Trigger pre-delete hook + event := new(platform.PreDeleteEntryEvent) + event.MediaID = &mediaID + event.EntryID = &entryId + + err := hook.GlobalHookManager.OnPreDeleteEntry().Trigger(event) + if err != nil { + return err + } + + if event.DefaultPrevented { + return nil + } + + // Execute the deletion + err = deleteFunc(event) + if err != nil { + return err + } + + // Trigger post-delete hook + postEvent := new(platform.PostDeleteEntryEvent) + postEvent.MediaID = &mediaID + postEvent.EntryID = &entryId + err = hook.GlobalHookManager.OnPostDeleteEntry().Trigger(postEvent) + return err +} + func (h *PlatformHelper) FilterOutCustomAnimeLists(lists []*anilist.AnimeCollection_MediaListCollection_Lists) []*anilist.AnimeCollection_MediaListCollection_Lists { return lo.Filter(lists, func(list *anilist.AnimeCollection_MediaListCollection_Lists, _ int) bool { return list.Status != nil diff --git a/internal/platforms/simulated_platform/simulated_platform.go b/internal/platforms/simulated_platform/simulated_platform.go index 6e95d19b..7cb0777f 100644 --- a/internal/platforms/simulated_platform/simulated_platform.go +++ b/internal/platforms/simulated_platform/simulated_platform.go @@ -232,27 +232,28 @@ func (sp *SimulatedPlatform) UpdateEntryRepeat(ctx context.Context, mediaID int, func (sp *SimulatedPlatform) DeleteEntry(ctx context.Context, mediaId, entryId int) error { sp.logger.Trace().Int("entryId", entryId).Int("mediaId", mediaId).Msg("simulated platform: Deleting entry") - // Check if this is a custom source entry - if handled, err := sp.helper.HandleCustomSourceDeleteEntry(ctx, mediaId, entryId); handled { - return err - } + return sp.helper.TriggerDeleteEntryHooks(ctx, mediaId, entryId, func(event *platform.PreDeleteEntryEvent) error { + if handled, err := sp.helper.HandleCustomSourceDeleteEntry(ctx, *event.MediaID, *event.EntryID); handled { + return err + } - sp.mu.Lock() - defer sp.mu.Unlock() + sp.mu.Lock() + defer sp.mu.Unlock() - // Try anime first - wrapper := sp.GetAnimeCollectionWrapper() - if _, err := wrapper.FindEntry(entryId, true); err == nil { - return wrapper.DeleteEntry(entryId, true) - } + // Try anime first + wrapper := sp.GetAnimeCollectionWrapper() + if _, err := wrapper.FindEntry(*event.EntryID, true); err == nil { + return wrapper.DeleteEntry(*event.EntryID, true) + } - // Try manga - wrapper = sp.GetMangaCollectionWrapper() - if _, err := wrapper.FindEntry(entryId, true); err == nil { - return wrapper.DeleteEntry(entryId, true) - } + // Try manga + wrapper = sp.GetMangaCollectionWrapper() + if _, err := wrapper.FindEntry(*event.EntryID, true); err == nil { + return wrapper.DeleteEntry(*event.EntryID, true) + } - return ErrMediaNotFound + return ErrMediaNotFound + }) } func (sp *SimulatedPlatform) GetAnime(ctx context.Context, mediaID int) (*anilist.BaseAnime, error) { diff --git a/internal/plugin/anilist.go b/internal/plugin/anilist.go index 9625c27d..eaf1ef02 100644 --- a/internal/plugin/anilist.go +++ b/internal/plugin/anilist.go @@ -5,6 +5,7 @@ import ( "seanime/internal/api/anilist" "seanime/internal/events" "seanime/internal/extension" + "seanime/internal/library/anime" "github.com/dop251/goja" "github.com/rs/zerolog" @@ -78,7 +79,6 @@ func (a *AppContextImpl) BindAnilist(vm *goja.Runtime, logger *zerolog.Logger, e _ = anilistObj.Set("getStudioDetails", func(studioID int) (*anilist.StudioDetails, error) { return anilistPlatformRef.Get().GetStudioDetails(context.Background(), studioID) }) - _ = anilistObj.Set("listAnime", func(page *int, search *string, perPage *int, sort []*anilist.MediaSort, status []*anilist.MediaStatus, genres []*string, averageScoreGreater *int, season *anilist.MediaSeason, seasonYear *int, format *anilist.MediaFormat, isAdult *bool) (*anilist.ListAnime, error) { return anilistPlatformRef.Get().GetAnilistClient().ListAnime(context.Background(), page, search, perPage, sort, status, genres, averageScoreGreater, season, seasonYear, format, isAdult) }) @@ -88,6 +88,10 @@ func (a *AppContextImpl) BindAnilist(vm *goja.Runtime, logger *zerolog.Logger, e _ = anilistObj.Set("listRecentAnime", func(page *int, perPage *int, airingAtGreater *int, airingAtLesser *int, notYetAired *bool) (*anilist.ListRecentAnime, error) { return anilistPlatformRef.Get().GetAnilistClient().ListRecentAnime(context.Background(), page, perPage, airingAtGreater, airingAtLesser, notYetAired) }) + _ = anilistObj.Set("clearCache", func() { + anilistPlatformRef.Get().ClearCache() + anime.ClearEpisodeCollectionCache() + }) _ = anilistObj.Set("customQuery", func(body map[string]interface{}, token string) (interface{}, error) { return anilist.CustomQuery(body, a.logger, token) }) diff --git a/internal/plugin/anime.go b/internal/plugin/anime.go index b4c8464f..bc012568 100644 --- a/internal/plugin/anime.go +++ b/internal/plugin/anime.go @@ -34,6 +34,13 @@ func (a *AppContextImpl) BindAnimeToContextObj(vm *goja.Runtime, obj *goja.Objec // Get downloaded chapter containers _ = animeObj.Set("getAnimeEntry", m.getAnimeEntry) + _ = animeObj.Set("clearEpisodeMetadataCache", func(call goja.FunctionCall) goja.Value { + metadataProviderRef, ok := a.metadataProviderRef.Get() + if ok { + metadataProviderRef.Get().ClearCache() + } + return goja.Undefined() + }) _ = obj.Set("anime", animeObj) } diff --git a/seanime-web/public/jassub/jassub-worker-modern.wasm b/seanime-web/public/jassub/jassub-worker-modern.wasm old mode 100644 new mode 100755 index 2e8a0bed..04b1e327 Binary files a/seanime-web/public/jassub/jassub-worker-modern.wasm and b/seanime-web/public/jassub/jassub-worker-modern.wasm differ diff --git a/seanime-web/public/jassub/jassub-worker.js b/seanime-web/public/jassub/jassub-worker.js index 1e510aa4..3ee132da 100644 --- a/seanime-web/public/jassub/jassub-worker.js +++ b/seanime-web/public/jassub/jassub-worker.js @@ -1,15 +1,15 @@ -"use strict";var Module=function(f={}){var f=f,h;f.ready=new Promise(function(t,r){h=t});var C=function(t){return console.log(t)},y=function(t){return console.error(t)};function _(){h(f)}function A(t,r){if(!t)throw r}var w=null;C=function(t){t==="JASSUB: No usable fontconfig configuration file found, using fallback."?console.debug(t):console.log(t)},y=function(t){t==="Fontconfig error: Cannot load default config file: No such file: (null)"?console.debug(t):console.error(t)},Y=function(t){return function(){t(),self.wasmMemory=B,self.HEAPU8C=new Uint8ClampedArray(B.buffer),self.HEAPU8=new Uint8Array(B.buffer)}}(Y);function F(t){throw t}var j,E,M,O,J,k,fe,le,B,ue;function Y(){var t=B.buffer;j=new Int8Array(t),E=new Int16Array(t),M=new Int32Array(t),O=new Uint8Array(t),J=new Uint16Array(t),k=new Uint32Array(t),fe=new Float32Array(t),le=new Float64Array(t)}if(B=new WebAssembly.Memory({initial:256,maximum:32768}),Y(),(!Math.imul||Math.imul(4294967295,5)!==-5)&&(Math.imul=function(t,r){var n=t>>>16,i=t&65535,o=r>>>16,u=r&65535;return i*u+(n*u+i*o<<16)|0}),!Math.fround){var ce=new Float32Array(1);Math.fround=function(t){return ce[0]=t,ce[0]}}Math.clz32||(Math.clz32=function(t){var r=32,n=t>>16;return n&&(r-=16,t=n),n=t>>8,n&&(r-=8,t=n),n=t>>4,n&&(r-=4,t=n),n=t>>2,n&&(r-=2,t=n),n=t>>1,n?r-2:r-t}),Math.trunc||(Math.trunc=function(t){return t<0?Math.ceil(t):Math.floor(t)});var de=typeof TextDecoder<"u"?new TextDecoder("utf8"):void 0;function pe(t,r,n){for(var i=r+n,o=r;t[o]&&!(o>=i);)++o;if(o-r>16&&t.buffer&&de)return de.decode(t.subarray(r,o));for(var u="";r>10,56320|g&1023)}}return u}function X(t,r){return t?pe(O,t,r):""}function Te(t,r,n,i){F("Assertion failed: "+X(t)+", at: "+[r?X(r):"unknown filename",n,i?X(i):"unknown function"])}function Pe(t,r,n){return 0}function Ae(t,r){}function Fe(t,r,n,i){if(!(i>0))return 0;for(var o=n,u=n+i-1,p=0;p=55296&&l<=57343){var v=t.charCodeAt(++p);l=65536+((l&1023)<<10)|v&1023}if(l<=127){if(n>=u)break;r[n++]=l}else if(l<=2047){if(n+1>=u)break;r[n++]=192|l>>6,r[n++]=128|l&63}else if(l<=65535){if(n+2>=u)break;r[n++]=224|l>>12,r[n++]=128|l>>6&63,r[n++]=128|l&63}else{if(n+3>=u)break;r[n++]=240|l>>18,r[n++]=128|l>>12&63,r[n++]=128|l>>6&63,r[n++]=128|l&63}}return r[n]=0,n-o}function at(t,r,n){return Fe(t,O,r,n)}function st(t,r,n){}function ot(t,r,n){return 0}function ft(t,r,n,i){}function lt(t,r,n,i){}function ut(t,r){}function ct(t,r,n,i,o){}function $e(t){switch(t){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+t)}}function dt(){for(var t=new Array(256),r=0;r<256;++r)t[r]=String.fromCharCode(r);Le=t}var Le=void 0;function D(t){for(var r="",n=t;O[n];)r+=Le[O[n++]];return r}var Q={},Z={},ge={},pt=48,gt=57;function Se(t){if(t===void 0)return"_unknown";t=t.replace(/[^a-zA-Z0-9_]/g,"$");var r=t.charCodeAt(0);return r>=pt&&r<=gt?"_"+t:t}function je(t,r){t=Se(t);var n={};return(n[t]=function(){return r.apply(this,arguments)},n)[t]}function Oe(t,r){var n=je(r,function(i){this.name=r,this.message=i;var o=new Error(i).stack;o!==void 0&&(this.stack=this.toString()+` -`+o.replace(/^Error(:[^\n]*)?\n/,""))});return n.prototype=Object.create(t.prototype),n.prototype.constructor=n,n.prototype.toString=function(){return this.message===void 0?this.name:this.name+": "+this.message},n}var N=void 0;function T(t){throw new N(t)}var He=void 0;function he(t){throw new He(t)}function K(t,r,n){t.forEach(function(l){ge[l]=r});function i(l){var v=n(l);v.length!==t.length&&he("Mismatched type converter count");for(var g=0;g>u])},destructorFunction:null})}function vt(t){if(!(this instanceof G)||!(t instanceof G))return!1;for(var r=this.$$.ptrType.registeredClass,n=this.$$.ptr,i=t.$$.ptrType.registeredClass,o=t.$$.ptr;r.baseClass;)n=r.upcast(n),r=r.baseClass;for(;i.baseClass;)o=i.upcast(o),i=i.baseClass;return r===i&&n===o}function yt(t){return{count:t.count,deleteScheduled:t.deleteScheduled,preservePointerOnDelete:t.preservePointerOnDelete,ptr:t.ptr,ptrType:t.ptrType,smartPtr:t.smartPtr,smartPtrType:t.smartPtrType}}function Ee(t){function r(n){return n.$$.ptrType.registeredClass.name}T(r(t)+" instance already deleted")}var Me=!1;function Be(t){}function bt(t){t.smartPtr?t.smartPtrType.rawDestructor(t.smartPtr):t.ptrType.registeredClass.rawDestructor(t.ptr)}function xe(t){t.count.value-=1;var r=t.count.value===0;r&&bt(t)}function Ve(t,r,n){if(r===n)return t;if(n.baseClass===void 0)return null;var i=Ve(t,r,n.baseClass);return i===null?null:n.downcast(i)}var ze={};function mt(){return Object.keys(re).length}function wt(){var t=[];for(var r in re)re.hasOwnProperty(r)&&t.push(re[r]);return t}var ee=[];function ke(){for(;ee.length;){var t=ee.pop();t.$$.deleteScheduled=!1,t.delete()}}var te=void 0;function Ct(t){te=t,ee.length&&te&&te(ke)}function _t(){f.getInheritedInstanceCount=mt,f.getLiveInheritedInstances=wt,f.flushPendingDeletes=ke,f.setDelayFunction=Ct}var re={};function Tt(t,r){for(r===void 0&&T("ptr should not be undefined");t.baseClass;)r=t.upcast(r),t=t.baseClass;return r}function Pt(t,r){return r=Tt(t,r),re[r]}function ve(t,r){(!r.ptrType||!r.ptr)&&he("makeClassHandle requires ptr and ptrType");var n=!!r.smartPtrType,i=!!r.smartPtr;return n!==i&&he("Both smartPtrType and smartPtr must be specified"),r.count={value:1},ne(Object.create(t,{$$:{value:r}}))}function At(t){var r=this.getPointee(t);if(!r)return this.destructor(t),null;var n=Pt(this.registeredClass,r);if(n!==void 0){if(n.$$.count.value===0)return n.$$.ptr=r,n.$$.smartPtr=t,n.clone();var i=n.clone();return this.destructor(t),i}function o(){return this.isSmartPointer?ve(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:r,smartPtrType:this,smartPtr:t}):ve(this.registeredClass.instancePrototype,{ptrType:this,ptr:t})}var u=this.registeredClass.getActualType(r),p=ze[u];if(!p)return o.call(this);var l;this.isConst?l=p.constPointerType:l=p.pointerType;var v=Ve(r,this.registeredClass,l.registeredClass);return v===null?o.call(this):this.isSmartPointer?ve(l.registeredClass.instancePrototype,{ptrType:l,ptr:v,smartPtrType:this,smartPtr:t}):ve(l.registeredClass.instancePrototype,{ptrType:l,ptr:v})}function ne(t){return typeof FinalizationRegistry>"u"?(ne=function(r){return r},t):(Me=new FinalizationRegistry(function(r){xe(r.$$)}),ne=function(r){var n=r.$$,i=!!n.smartPtr;if(i){var o={$$:n};Me.register(r,o,r)}return r},Be=function(r){return Me.unregister(r)},ne(t))}function Ft(){if(this.$$.ptr||Ee(this),this.$$.preservePointerOnDelete)return this.$$.count.value+=1,this;var t=ne(Object.create(Object.getPrototypeOf(this),{$$:{value:yt(this.$$)}}));return t.$$.count.value+=1,t.$$.deleteScheduled=!1,t}function $t(){this.$$.ptr||Ee(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&T("Object already scheduled for deletion"),Be(this),xe(this.$$),this.$$.preservePointerOnDelete||(this.$$.smartPtr=void 0,this.$$.ptr=void 0)}function St(){return!this.$$.ptr}function jt(){return this.$$.ptr||Ee(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&T("Object already scheduled for deletion"),ee.push(this),ee.length===1&&te&&te(ke),this.$$.deleteScheduled=!0,this}function Ot(){G.prototype.isAliasOf=vt,G.prototype.clone=Ft,G.prototype.delete=$t,G.prototype.isDeleted=St,G.prototype.deleteLater=jt}function G(){}function Je(t,r,n){if(t[r].overloadTable===void 0){var i=t[r];t[r]=function(){return t[r].overloadTable.hasOwnProperty(arguments.length)||T("Function '"+n+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+t[r].overloadTable+")!"),t[r].overloadTable[arguments.length].apply(this,arguments)},t[r].overloadTable=[],t[r].overloadTable[i.argCount]=i}}function Et(t,r,n){f.hasOwnProperty(t)?((n===void 0||f[t].overloadTable!==void 0&&f[t].overloadTable[n]!==void 0)&&T("Cannot register public name '"+t+"' twice"),Je(f,t,t),f.hasOwnProperty(n)&&T("Cannot register multiple overloads of a function with the same number of arguments ("+n+")!"),f[t].overloadTable[n]=r):(f[t]=r,n!==void 0&&(f[t].numArguments=n))}function Mt(t,r,n,i,o,u,p,l){this.name=t,this.constructor=r,this.instancePrototype=n,this.rawDestructor=i,this.baseClass=o,this.getActualType=u,this.upcast=p,this.downcast=l,this.pureVirtualFunctions=[]}function ye(t,r,n){for(;r!==n;)r.upcast||T("Expected null or instance of "+n.name+", got an instance of "+r.name),t=r.upcast(t),r=r.baseClass;return t}function kt(t,r){if(r===null)return this.isReference&&T("null is not a valid "+this.name),0;r.$$||T('Cannot pass "'+Re(r)+'" as a '+this.name),r.$$.ptr||T("Cannot pass deleted object as a pointer of type "+this.name);var n=r.$$.ptrType.registeredClass,i=ye(r.$$.ptr,n,this.registeredClass);return i}function Ut(t,r){var n;if(r===null)return this.isReference&&T("null is not a valid "+this.name),this.isSmartPointer?(n=this.rawConstructor(),t!==null&&t.push(this.rawDestructor,n),n):0;r.$$||T('Cannot pass "'+Re(r)+'" as a '+this.name),r.$$.ptr||T("Cannot pass deleted object as a pointer of type "+this.name),!this.isConst&&r.$$.ptrType.isConst&&T("Cannot convert argument of type "+(r.$$.smartPtrType?r.$$.smartPtrType.name:r.$$.ptrType.name)+" to parameter type "+this.name);var i=r.$$.ptrType.registeredClass;if(n=ye(r.$$.ptr,i,this.registeredClass),this.isSmartPointer)switch(r.$$.smartPtr===void 0&&T("Passing raw pointer to smart pointer is illegal"),this.sharingPolicy){case 0:r.$$.smartPtrType===this?n=r.$$.smartPtr:T("Cannot convert argument of type "+(r.$$.smartPtrType?r.$$.smartPtrType.name:r.$$.ptrType.name)+" to parameter type "+this.name);break;case 1:n=r.$$.smartPtr;break;case 2:if(r.$$.smartPtrType===this)n=r.$$.smartPtr;else{var o=r.clone();n=this.rawShare(n,Ue.toHandle(function(){o.delete()})),t!==null&&t.push(this.rawDestructor,n)}break;default:T("Unsupporting sharing policy")}return n}function Rt(t,r){if(r===null)return this.isReference&&T("null is not a valid "+this.name),0;r.$$||T('Cannot pass "'+Re(r)+'" as a '+this.name),r.$$.ptr||T("Cannot pass deleted object as a pointer of type "+this.name),r.$$.ptrType.isConst&&T("Cannot convert argument of type "+r.$$.ptrType.name+" to parameter type "+this.name);var n=r.$$.ptrType.registeredClass,i=ye(r.$$.ptr,n,this.registeredClass);return i}function be(t){return this.fromWireType(M[t>>2])}function Wt(t){return this.rawGetPointee&&(t=this.rawGetPointee(t)),t}function It(t){this.rawDestructor&&this.rawDestructor(t)}function Dt(t){t!==null&&t.delete()}function Lt(){V.prototype.getPointee=Wt,V.prototype.destructor=It,V.prototype.argPackAdvance=8,V.prototype.readValueFromPointer=be,V.prototype.deleteObject=Dt,V.prototype.fromWireType=At}function V(t,r,n,i,o,u,p,l,v,g,m){this.name=t,this.registeredClass=r,this.isReference=n,this.isConst=i,this.isSmartPointer=o,this.pointeeType=u,this.sharingPolicy=p,this.rawGetPointee=l,this.rawConstructor=v,this.rawShare=g,this.rawDestructor=m,!o&&r.baseClass===void 0?i?(this.toWireType=kt,this.destructorFunction=null):(this.toWireType=Rt,this.destructorFunction=null):this.toWireType=Ut}function Ht(t,r,n){f.hasOwnProperty(t)||he("Replacing nonexistant public symbol"),f[t].overloadTable!==void 0&&n!==void 0?f[t].overloadTable[n]=r:(f[t]=r,f[t].argCount=n)}function Bt(t,r,n){var i=dynCalls[t];return n&&n.length?i.apply(null,[r].concat(n)):i.call(null,r)}var me=[];function ie(t){var r=me[t];return r||(t>=me.length&&(me.length=t+1),me[t]=r=ue.get(t)),r}function xt(t,r,n){if(t.includes("j"))return Bt(t,r,n);var i=ie(r).apply(null,n);return i}function Vt(t,r){var n=[];return function(){return n.length=0,Object.assign(n,arguments),xt(t,r,n)}}function q(t,r){t=D(t);function n(){return t.includes("j")?Vt(t,r):ie(r)}var i=n();return typeof i!="function"&&T("unknown function pointer with signature "+t+": "+r),i}var Ge=void 0;function zt(t){var r=Ne(t),n=D(r);return z(r),n}function ae(t,r){var n=[],i={};function o(u){if(!i[u]&&!Z[u]){if(ge[u]){ge[u].forEach(o);return}n.push(u),i[u]=!0}}throw r.forEach(o),new Ge(t+": "+n.map(zt).join([", "]))}function Jt(t,r,n,i,o,u,p,l,v,g,m,P,$){m=D(m),u=q(o,u),l&&(l=q(p,l)),g&&(g=q(v,g)),$=q(P,$);var S=Se(m);Et(S,function(){ae("Cannot construct "+m+" due to unbound types",[i])}),K([t,r,n],i?[i]:[],function(U){U=U[0];var W,R;i?(W=U.registeredClass,R=W.instancePrototype):R=G.prototype;var L=je(S,function(){if(Object.getPrototypeOf(this)!==oe)throw new N("Use 'new' to construct "+m);if(I.constructor_body===void 0)throw new N(m+" has no accessible constructor");var it=I.constructor_body[arguments.length];if(it===void 0)throw new N("Tried to invoke ctor of "+m+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(I.constructor_body).toString()+") parameters instead!");return it.apply(this,arguments)}),oe=Object.create(R,{constructor:{value:L}});L.prototype=oe;var I=new Mt(m,L,oe,$,W,u,l,g);I.baseClass&&(I.baseClass.__derivedClasses===void 0&&(I.baseClass.__derivedClasses=[]),I.baseClass.__derivedClasses.push(I));var Hr=new V(m,I,!0,!1,!1),rt=new V(m+"*",I,!1,!1,!1),nt=new V(m+" const*",I,!1,!0,!1);return ze[t]={pointerType:rt,constPointerType:nt},Ht(S,L),[Hr,rt,nt]})}function qe(t,r){for(var n=[],i=0;i>2]);return n}function Ke(t){for(;t.length;){var r=t.pop(),n=t.pop();n(r)}}function Gt(t,r){if(!(t instanceof Function))throw new TypeError("new_ called with constructor type "+typeof t+" which is not a function");var n=je(t.name||"unknownFunctionName",function(){});n.prototype=t.prototype;var i=new n,o=t.apply(i,r);return o instanceof Object?o:i}function Ye(t,r,n,i,o,u){var p=r.length;p<2&&T("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var l=r[1]!==null&&n!==null,v=!1,g=1;g>>16,i=t&65535,o=r>>>16,u=r&65535;return i*u+(n*u+i*o<<16)|0}),!Math.fround){var ce=new Float32Array(1);Math.fround=function(t){return ce[0]=t,ce[0]}}Math.clz32||(Math.clz32=function(t){var r=32,n=t>>16;return n&&(r-=16,t=n),n=t>>8,n&&(r-=8,t=n),n=t>>4,n&&(r-=4,t=n),n=t>>2,n&&(r-=2,t=n),n=t>>1,n?r-2:r-t}),Math.trunc||(Math.trunc=function(t){return t<0?Math.ceil(t):Math.floor(t)});var de=typeof TextDecoder<"u"?new TextDecoder("utf8"):void 0;function pe(t,r,n){for(var i=r+n,o=r;t[o]&&!(o>=i);)++o;if(o-r>16&&t.buffer&&de)return de.decode(t.subarray(r,o));for(var u="";r>10,56320|g&1023)}}return u}function X(t,r){return t?pe(O,t,r):""}function Te(t,r,n,i){F("Assertion failed: "+X(t)+", at: "+[r?X(r):"unknown filename",n,i?X(i):"unknown function"])}function Pe(t,r,n){return 0}function Ae(t,r,n,i){if(!(i>0))return 0;for(var o=n,u=n+i-1,p=0;p=55296&&f<=57343){var v=t.charCodeAt(++p);f=65536+((f&1023)<<10)|v&1023}if(f<=127){if(n>=u)break;r[n++]=f}else if(f<=2047){if(n+1>=u)break;r[n++]=192|f>>6,r[n++]=128|f&63}else if(f<=65535){if(n+2>=u)break;r[n++]=224|f>>12,r[n++]=128|f>>6&63,r[n++]=128|f&63}else{if(n+3>=u)break;r[n++]=240|f>>18,r[n++]=128|f>>12&63,r[n++]=128|f>>6&63,r[n++]=128|f&63}}return r[n]=0,n-o}function Fe(t,r,n){return Ae(t,O,r,n)}function at(t,r,n){}function st(t,r,n){return 0}function ot(t,r,n,i){}function lt(t,r,n,i,o){}function $e(t){switch(t){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+t)}}function ft(){for(var t=new Array(256),r=0;r<256;++r)t[r]=String.fromCharCode(r);Le=t}var Le=void 0;function D(t){for(var r="",n=t;O[n];)r+=Le[O[n++]];return r}var Q={},Z={},ge={},ut=48,ct=57;function Se(t){if(t===void 0)return"_unknown";t=t.replace(/[^a-zA-Z0-9_]/g,"$");var r=t.charCodeAt(0);return r>=ut&&r<=ct?"_"+t:t}function je(t,r){t=Se(t);var n={};return(n[t]=function(){return r.apply(this,arguments)},n)[t]}function Oe(t,r){var n=je(r,function(i){this.name=r,this.message=i;var o=new Error(i).stack;o!==void 0&&(this.stack=this.toString()+` +`+o.replace(/^Error(:[^\n]*)?\n/,""))});return n.prototype=Object.create(t.prototype),n.prototype.constructor=n,n.prototype.toString=function(){return this.message===void 0?this.name:this.name+": "+this.message},n}var N=void 0;function T(t){throw new N(t)}var He=void 0;function he(t){throw new He(t)}function K(t,r,n){t.forEach(function(f){ge[f]=r});function i(f){var v=n(f);v.length!==t.length&&he("Mismatched type converter count");for(var g=0;g>u])},destructorFunction:null})}function pt(t){if(!(this instanceof G)||!(t instanceof G))return!1;for(var r=this.$$.ptrType.registeredClass,n=this.$$.ptr,i=t.$$.ptrType.registeredClass,o=t.$$.ptr;r.baseClass;)n=r.upcast(n),r=r.baseClass;for(;i.baseClass;)o=i.upcast(o),i=i.baseClass;return r===i&&n===o}function gt(t){return{count:t.count,deleteScheduled:t.deleteScheduled,preservePointerOnDelete:t.preservePointerOnDelete,ptr:t.ptr,ptrType:t.ptrType,smartPtr:t.smartPtr,smartPtrType:t.smartPtrType}}function Ee(t){function r(n){return n.$$.ptrType.registeredClass.name}T(r(t)+" instance already deleted")}var Me=!1;function Be(t){}function ht(t){t.smartPtr?t.smartPtrType.rawDestructor(t.smartPtr):t.ptrType.registeredClass.rawDestructor(t.ptr)}function xe(t){t.count.value-=1;var r=t.count.value===0;r&&ht(t)}function Ve(t,r,n){if(r===n)return t;if(n.baseClass===void 0)return null;var i=Ve(t,r,n.baseClass);return i===null?null:n.downcast(i)}var ze={};function vt(){return Object.keys(re).length}function yt(){var t=[];for(var r in re)re.hasOwnProperty(r)&&t.push(re[r]);return t}var ee=[];function ke(){for(;ee.length;){var t=ee.pop();t.$$.deleteScheduled=!1,t.delete()}}var te=void 0;function bt(t){te=t,ee.length&&te&&te(ke)}function mt(){l.getInheritedInstanceCount=vt,l.getLiveInheritedInstances=yt,l.flushPendingDeletes=ke,l.setDelayFunction=bt}var re={};function wt(t,r){for(r===void 0&&T("ptr should not be undefined");t.baseClass;)r=t.upcast(r),t=t.baseClass;return r}function Ct(t,r){return r=wt(t,r),re[r]}function ve(t,r){(!r.ptrType||!r.ptr)&&he("makeClassHandle requires ptr and ptrType");var n=!!r.smartPtrType,i=!!r.smartPtr;return n!==i&&he("Both smartPtrType and smartPtr must be specified"),r.count={value:1},ne(Object.create(t,{$$:{value:r}}))}function _t(t){var r=this.getPointee(t);if(!r)return this.destructor(t),null;var n=Ct(this.registeredClass,r);if(n!==void 0){if(n.$$.count.value===0)return n.$$.ptr=r,n.$$.smartPtr=t,n.clone();var i=n.clone();return this.destructor(t),i}function o(){return this.isSmartPointer?ve(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:r,smartPtrType:this,smartPtr:t}):ve(this.registeredClass.instancePrototype,{ptrType:this,ptr:t})}var u=this.registeredClass.getActualType(r),p=ze[u];if(!p)return o.call(this);var f;this.isConst?f=p.constPointerType:f=p.pointerType;var v=Ve(r,this.registeredClass,f.registeredClass);return v===null?o.call(this):this.isSmartPointer?ve(f.registeredClass.instancePrototype,{ptrType:f,ptr:v,smartPtrType:this,smartPtr:t}):ve(f.registeredClass.instancePrototype,{ptrType:f,ptr:v})}function ne(t){return typeof FinalizationRegistry>"u"?(ne=function(r){return r},t):(Me=new FinalizationRegistry(function(r){xe(r.$$)}),ne=function(r){var n=r.$$,i=!!n.smartPtr;if(i){var o={$$:n};Me.register(r,o,r)}return r},Be=function(r){return Me.unregister(r)},ne(t))}function Tt(){if(this.$$.ptr||Ee(this),this.$$.preservePointerOnDelete)return this.$$.count.value+=1,this;var t=ne(Object.create(Object.getPrototypeOf(this),{$$:{value:gt(this.$$)}}));return t.$$.count.value+=1,t.$$.deleteScheduled=!1,t}function Pt(){this.$$.ptr||Ee(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&T("Object already scheduled for deletion"),Be(this),xe(this.$$),this.$$.preservePointerOnDelete||(this.$$.smartPtr=void 0,this.$$.ptr=void 0)}function At(){return!this.$$.ptr}function Ft(){return this.$$.ptr||Ee(this),this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete&&T("Object already scheduled for deletion"),ee.push(this),ee.length===1&&te&&te(ke),this.$$.deleteScheduled=!0,this}function $t(){G.prototype.isAliasOf=pt,G.prototype.clone=Tt,G.prototype.delete=Pt,G.prototype.isDeleted=At,G.prototype.deleteLater=Ft}function G(){}function Je(t,r,n){if(t[r].overloadTable===void 0){var i=t[r];t[r]=function(){return t[r].overloadTable.hasOwnProperty(arguments.length)||T("Function '"+n+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+t[r].overloadTable+")!"),t[r].overloadTable[arguments.length].apply(this,arguments)},t[r].overloadTable=[],t[r].overloadTable[i.argCount]=i}}function St(t,r,n){l.hasOwnProperty(t)?((n===void 0||l[t].overloadTable!==void 0&&l[t].overloadTable[n]!==void 0)&&T("Cannot register public name '"+t+"' twice"),Je(l,t,t),l.hasOwnProperty(n)&&T("Cannot register multiple overloads of a function with the same number of arguments ("+n+")!"),l[t].overloadTable[n]=r):(l[t]=r,n!==void 0&&(l[t].numArguments=n))}function jt(t,r,n,i,o,u,p,f){this.name=t,this.constructor=r,this.instancePrototype=n,this.rawDestructor=i,this.baseClass=o,this.getActualType=u,this.upcast=p,this.downcast=f,this.pureVirtualFunctions=[]}function ye(t,r,n){for(;r!==n;)r.upcast||T("Expected null or instance of "+n.name+", got an instance of "+r.name),t=r.upcast(t),r=r.baseClass;return t}function Ot(t,r){if(r===null)return this.isReference&&T("null is not a valid "+this.name),0;r.$$||T('Cannot pass "'+Re(r)+'" as a '+this.name),r.$$.ptr||T("Cannot pass deleted object as a pointer of type "+this.name);var n=r.$$.ptrType.registeredClass,i=ye(r.$$.ptr,n,this.registeredClass);return i}function Et(t,r){var n;if(r===null)return this.isReference&&T("null is not a valid "+this.name),this.isSmartPointer?(n=this.rawConstructor(),t!==null&&t.push(this.rawDestructor,n),n):0;r.$$||T('Cannot pass "'+Re(r)+'" as a '+this.name),r.$$.ptr||T("Cannot pass deleted object as a pointer of type "+this.name),!this.isConst&&r.$$.ptrType.isConst&&T("Cannot convert argument of type "+(r.$$.smartPtrType?r.$$.smartPtrType.name:r.$$.ptrType.name)+" to parameter type "+this.name);var i=r.$$.ptrType.registeredClass;if(n=ye(r.$$.ptr,i,this.registeredClass),this.isSmartPointer)switch(r.$$.smartPtr===void 0&&T("Passing raw pointer to smart pointer is illegal"),this.sharingPolicy){case 0:r.$$.smartPtrType===this?n=r.$$.smartPtr:T("Cannot convert argument of type "+(r.$$.smartPtrType?r.$$.smartPtrType.name:r.$$.ptrType.name)+" to parameter type "+this.name);break;case 1:n=r.$$.smartPtr;break;case 2:if(r.$$.smartPtrType===this)n=r.$$.smartPtr;else{var o=r.clone();n=this.rawShare(n,Ue.toHandle(function(){o.delete()})),t!==null&&t.push(this.rawDestructor,n)}break;default:T("Unsupporting sharing policy")}return n}function Mt(t,r){if(r===null)return this.isReference&&T("null is not a valid "+this.name),0;r.$$||T('Cannot pass "'+Re(r)+'" as a '+this.name),r.$$.ptr||T("Cannot pass deleted object as a pointer of type "+this.name),r.$$.ptrType.isConst&&T("Cannot convert argument of type "+r.$$.ptrType.name+" to parameter type "+this.name);var n=r.$$.ptrType.registeredClass,i=ye(r.$$.ptr,n,this.registeredClass);return i}function be(t){return this.fromWireType(M[t>>2])}function kt(t){return this.rawGetPointee&&(t=this.rawGetPointee(t)),t}function Ut(t){this.rawDestructor&&this.rawDestructor(t)}function Rt(t){t!==null&&t.delete()}function Wt(){V.prototype.getPointee=kt,V.prototype.destructor=Ut,V.prototype.argPackAdvance=8,V.prototype.readValueFromPointer=be,V.prototype.deleteObject=Rt,V.prototype.fromWireType=_t}function V(t,r,n,i,o,u,p,f,v,g,m){this.name=t,this.registeredClass=r,this.isReference=n,this.isConst=i,this.isSmartPointer=o,this.pointeeType=u,this.sharingPolicy=p,this.rawGetPointee=f,this.rawConstructor=v,this.rawShare=g,this.rawDestructor=m,!o&&r.baseClass===void 0?i?(this.toWireType=Ot,this.destructorFunction=null):(this.toWireType=Mt,this.destructorFunction=null):this.toWireType=Et}function It(t,r,n){l.hasOwnProperty(t)||he("Replacing nonexistant public symbol"),l[t].overloadTable!==void 0&&n!==void 0?l[t].overloadTable[n]=r:(l[t]=r,l[t].argCount=n)}function Dt(t,r,n){var i=dynCalls[t];return n&&n.length?i.apply(null,[r].concat(n)):i.call(null,r)}var me=[];function ie(t){var r=me[t];return r||(t>=me.length&&(me.length=t+1),me[t]=r=ue.get(t)),r}function Lt(t,r,n){if(t.includes("j"))return Dt(t,r,n);var i=ie(r).apply(null,n);return i}function Ht(t,r){var n=[];return function(){return n.length=0,Object.assign(n,arguments),Lt(t,r,n)}}function q(t,r){t=D(t);function n(){return t.includes("j")?Ht(t,r):ie(r)}var i=n();return typeof i!="function"&&T("unknown function pointer with signature "+t+": "+r),i}var Ge=void 0;function Bt(t){var r=Ne(t),n=D(r);return z(r),n}function ae(t,r){var n=[],i={};function o(u){if(!i[u]&&!Z[u]){if(ge[u]){ge[u].forEach(o);return}n.push(u),i[u]=!0}}throw r.forEach(o),new Ge(t+": "+n.map(Bt).join([", "]))}function xt(t,r,n,i,o,u,p,f,v,g,m,P,$){m=D(m),u=q(o,u),f&&(f=q(p,f)),g&&(g=q(v,g)),$=q(P,$);var S=Se(m);St(S,function(){ae("Cannot construct "+m+" due to unbound types",[i])}),K([t,r,n],i?[i]:[],function(U){U=U[0];var W,R;i?(W=U.registeredClass,R=W.instancePrototype):R=G.prototype;var L=je(S,function(){if(Object.getPrototypeOf(this)!==oe)throw new N("Use 'new' to construct "+m);if(I.constructor_body===void 0)throw new N(m+" has no accessible constructor");var it=I.constructor_body[arguments.length];if(it===void 0)throw new N("Tried to invoke ctor of "+m+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(I.constructor_body).toString()+") parameters instead!");return it.apply(this,arguments)}),oe=Object.create(R,{constructor:{value:L}});L.prototype=oe;var I=new jt(m,L,oe,$,W,u,f,g);I.baseClass&&(I.baseClass.__derivedClasses===void 0&&(I.baseClass.__derivedClasses=[]),I.baseClass.__derivedClasses.push(I));var Rr=new V(m,I,!0,!1,!1),rt=new V(m+"*",I,!1,!1,!1),nt=new V(m+" const*",I,!1,!0,!1);return ze[t]={pointerType:rt,constPointerType:nt},It(S,L),[Rr,rt,nt]})}function qe(t,r){for(var n=[],i=0;i>2]);return n}function Ke(t){for(;t.length;){var r=t.pop(),n=t.pop();n(r)}}function Vt(t,r){if(!(t instanceof Function))throw new TypeError("new_ called with constructor type "+typeof t+" which is not a function");var n=je(t.name||"unknownFunctionName",function(){});n.prototype=t.prototype;var i=new n,o=t.apply(i,r);return o instanceof Object?o:i}function Ye(t,r,n,i,o,u){var p=r.length;p<2&&T("argTypes array size mismatch! Must at least get return value and 'this' types!");for(var f=r[1]!==null&&n!==null,v=!1,g=1;g0?", ":"")+$),S+=(m||u?"var rv = ":"")+"invoker(fn"+($.length>0?", ":"")+$+`); +`,W.push("argType"+g),R.push(r[g+2]);if(f&&($="thisWired"+($.length>0?", ":"")+$),S+=(m||u?"var rv = ":"")+"invoker(fn"+($.length>0?", ":"")+$+`); `,v)S+=`runDestructors(destructors); -`;else for(var g=l?1:2;g0);var p=qe(r,n);o=q(i,o),K([],[t],function(l){l=l[0];var v="constructor "+l.name;if(l.registeredClass.constructor_body===void 0&&(l.registeredClass.constructor_body=[]),l.registeredClass.constructor_body[r-1]!==void 0)throw new N("Cannot register multiple constructors with identical number of parameters ("+(r-1)+") for class '"+l.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!");return l.registeredClass.constructor_body[r-1]=function(){ae("Cannot construct "+l.name+" due to unbound types",p)},K([],p,function(g){return g.splice(1,0,null),l.registeredClass.constructor_body[r-1]=Ye(v,g,null,o,u),[]}),[]})}function Kt(t,r,n,i,o,u,p,l,v){var g=qe(n,i);r=D(r),u=q(o,u),K([],[t],function(m){m=m[0];var P=m.name+"."+r;r.startsWith("@@")&&(r=Symbol[r.substring(2)]),l&&m.registeredClass.pureVirtualFunctions.push(r);function $(){ae("Cannot call "+P+" due to unbound types",g)}var S=m.registeredClass.instancePrototype,U=S[r];return U===void 0||U.overloadTable===void 0&&U.className!==m.name&&U.argCount===n-2?($.argCount=n-2,$.className=m.name,S[r]=$):(Je(S,r,P),S[r].overloadTable[n-2]=$),K([],g,function(W){var R=Ye(P,W,m,u,p,v);return S[r].overloadTable===void 0?(R.argCount=n-2,S[r]=R):S[r].overloadTable[n-2]=R,[]}),[]})}function Xe(t,r,n){return t instanceof Object||T(n+' with invalid "this": '+t),t instanceof r.registeredClass.constructor||T(n+' incompatible with "this" of type '+t.constructor.name),t.$$.ptr||T("cannot call emscripten binding method "+n+" on deleted object"),ye(t.$$.ptr,t.$$.ptrType.registeredClass,r.registeredClass)}function Yt(t,r,n,i,o,u,p,l,v,g){r=D(r),o=q(i,o),K([],[t],function(m){m=m[0];var P=m.name+"."+r,$={get:function(){ae("Cannot access "+P+" due to unbound types",[n,p])},enumerable:!0,configurable:!0};return v?$.set=function(){ae("Cannot access "+P+" due to unbound types",[n,p])}:$.set=function(S){T(P+" is a read-only property")},Object.defineProperty(m.registeredClass.instancePrototype,r,$),K([],v?[n,p]:[n],function(S){var U=S[0],W={get:function(){var L=Xe(this,m,P+" getter");return U.fromWireType(o(u,L))},enumerable:!0};if(v){v=q(l,v);var R=S[1];W.set=function(L){var oe=Xe(this,m,P+" setter"),I=[];v(g,oe,R.toWireType(I,L)),Ke(I)}}return Object.defineProperty(m.registeredClass.instancePrototype,r,W),[]}),[]})}function Xt(){this.allocated=[void 0],this.freelist=[],this.get=function(t){return this.allocated[t]},this.has=function(t){return this.allocated[t]!==void 0},this.allocate=function(t){var r=this.freelist.pop()||this.allocated.length;return this.allocated[r]=t,r},this.free=function(t){this.allocated[t]=void 0,this.freelist.push(t)}}var H=new Xt;function Qt(t){t>=H.reserved&&--H.get(t).refcount===0&&H.free(t)}function Zt(){for(var t=0,r=H.reserved;r>2])};case 3:return function(n){return this.fromWireType(le[n>>3])};default:throw new TypeError("Unknown float type: "+t)}}function rr(t,r,n){var i=$e(n);r=D(r),x(t,{name:r,fromWireType:function(o){return o},toWireType:function(o,u){return u},argPackAdvance:8,readValueFromPointer:tr(r,i),destructorFunction:null})}function nr(t,r,n){switch(r){case 0:return n?function(o){return j[o]}:function(o){return O[o]};case 1:return n?function(o){return E[o>>1]}:function(o){return J[o>>1]};case 2:return n?function(o){return M[o>>2]}:function(o){return k[o>>2]};default:throw new TypeError("Unknown integer type: "+t)}}function ir(t,r,n,i,o){r=D(r);var u=$e(n),p=function(P){return P};if(i===0){var l=32-8*n;p=function(P){return P<>>l}}var v=r.includes("unsigned"),g=function(P,$){},m;v?m=function(P,$){return g($,this.name),$>>>0}:m=function(P,$){return g($,this.name),$},x(t,{name:r,fromWireType:p,toWireType:m,argPackAdvance:8,readValueFromPointer:nr(r,u,i!==0),destructorFunction:null})}function ar(t,r,n){var i=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array],o=i[r];function u(p){p=p>>2;var l=k,v=l[p],g=l[p+1];return new o(l.buffer,g,v)}n=D(n),x(t,{name:n,fromWireType:u,argPackAdvance:8,readValueFromPointer:u},{ignoreDuplicateRegistrations:!0})}function sr(t){for(var r=0,n=0;n=55296&&i<=57343?(r+=4,++n):r+=3}return r}function or(t,r){r=D(r);var n=r==="std::string";x(t,{name:r,fromWireType:function(i){var o=k[i>>2],u=i+4,p;if(n)for(var l=u,v=0;v<=o;++v){var g=u+v;if(v==o||O[g]==0){var m=g-l,P=X(l,m);p===void 0?p=P:(p+=String.fromCharCode(0),p+=P),l=g+1}}else{for(var $=new Array(o),v=0;v>2]=u,n&&p)at(o,v,u+1);else if(p)for(var g=0;g255&&(z(v),T("String has UTF-16 code units that do not fit in 8 bits")),O[v+g]=m}else for(var g=0;g>1,o=i+r/2;!(i>=o)&&J[i];)++i;if(n=i<<1,n-t>32&&Qe)return Qe.decode(O.subarray(t,n));for(var u="",p=0;!(p>=r/2);++p){var l=E[t+p*2>>1];if(l==0)break;u+=String.fromCharCode(l)}return u}function lr(t,r,n){if(n===void 0&&(n=2147483647),n<2)return 0;n-=2;for(var i=r,o=n>1]=p,r+=2}return E[r>>1]=0,r-i}function ur(t){return t.length*2}function cr(t,r){for(var n=0,i="";!(n>=r/4);){var o=M[t+n*4>>2];if(o==0)break;if(++n,o>=65536){var u=o-65536;i+=String.fromCharCode(55296|u>>10,56320|u&1023)}else i+=String.fromCharCode(o)}return i}function dr(t,r,n){if(n===void 0&&(n=2147483647),n<4)return 0;for(var i=r,o=i+n-4,u=0;u=55296&&p<=57343){var l=t.charCodeAt(++u);p=65536+((p&1023)<<10)|l&1023}if(M[r>>2]=p,r+=4,r+4>o)break}return M[r>>2]=0,r-i}function pr(t){for(var r=0,n=0;n=55296&&i<=57343&&++n,r+=4}return r}function gr(t,r,n){n=D(n);var i,o,u,p,l;r===2?(i=fr,o=lr,p=ur,u=function(){return J},l=1):r===4&&(i=cr,o=dr,p=pr,u=function(){return k},l=2),x(t,{name:n,fromWireType:function(v){for(var g=k[v>>2],m=u(),P,$=v+4,S=0;S<=g;++S){var U=v+4+S*r;if(S==g||m[U>>l]==0){var W=U-$,R=i($,W);P===void 0?P=R:(P+=String.fromCharCode(0),P+=R),$=U+r}}return z(v),P},toWireType:function(v,g){typeof g!="string"&&T("Cannot pass non-string to C++ string type "+n);var m=p(g),P=De(4+m+r);return k[P>>2]=m>>l,o(g,P+4,m+r),v!==null&&v.push(z,P),P},argPackAdvance:8,readValueFromPointer:be,destructorFunction:function(v){z(v)}})}function hr(t,r){r=D(r),x(t,{isVoid:!0,name:r,argPackAdvance:0,fromWireType:function(){},toWireType:function(n,i){}})}function vr(){throw 1/0}function yr(t,r,n,i,o,u,p){return-52}function br(t,r,n,i,o,u){}function mr(){F("")}var We;typeof performance<"u"&&performance.now?We=function(){return performance.now()}:We=Date.now;function wr(){return 2147483648}function Cr(t){var r=B.buffer;try{return B.grow(t-r.byteLength+65535>>>16),Y(),1}catch{}}function _r(t){var r=O.length;t=t>>>0;var n=wr();if(t>n)return!1;for(var i=function(v,g){return v+(g-v%g)%g},o=1;o<=4;o*=2){var u=r*(1+.2/o);u=Math.min(u,t+100663296);var p=Math.min(n,i(Math.max(t,u),65536)),l=Cr(p);if(l)return!0}return!1}var Ie={};function Tr(){return"./this.program"}function se(){if(!se.strings){var t=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",r={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:t,_:Tr()};for(var n in Ie)Ie[n]===void 0?delete r[n]:r[n]=Ie[n];var i=[];for(var n in r)i.push(n+"="+r[n]);se.strings=i}return se.strings}function Pr(t,r){for(var n=0;n>0]=t.charCodeAt(n);j[r>>0]=0}function Ar(t,r){var n=0;return se().forEach(function(i,o){var u=r+n;k[t+o*4>>2]=u,Pr(i,u),n+=i.length+1}),0}function Fr(t,r){var n=se();k[t>>2]=n.length;var i=0;return n.forEach(function(o){i+=o.length+1}),k[r>>2]=i,0}function $r(t){throw"exit("+t+")"}var Sr=$r;function jr(t){return 52}function Or(t,r,n,i){return 52}function Er(t,r,n,i,o){return 70}var Mr=[null,[],[]];function kr(t,r){var n=Mr[t];r===0||r===10?((t===1?C:y)(pe(n,0)),n.length=0):n.push(r)}function Ur(t,r,n,i){for(var o=0,u=0;u>2],l=k[r+4>>2];r+=8;for(var v=0;v>2]=o,0}dt(),N=f.BindingError=Oe(Error,"BindingError"),He=f.InternalError=Oe(Error,"InternalError"),Ot(),_t(),Lt(),Ge=f.UnboundTypeError=Oe(Error,"UnboundTypeError"),Nt();var Rr={b:Te,k:Pe,I:Ae,B:st,K:ot,G:ft,n:lt,H:ut,u:ct,r:ht,j:Jt,A:qt,d:Kt,c:Yt,L:er,p:rr,g:ir,e:ar,q:or,l:gr,s:hr,y:vr,C:yr,D:br,f:mr,m:We,z:_r,E:Ar,F:Fr,h:Sr,i:jr,o:Or,t:Er,J:Ur,x:Wr,v:Dr,w:Ir,a:B};function Wr(t,r,n){var i=Ce();try{return ie(t)(r,n)}catch(o){if(_e(i),o!==o+0)throw o;we(1,0)}}function Ir(t,r,n,i,o){var u=Ce();try{return ie(t)(r,n,i,o)}catch(p){if(_e(u),p!==p+0)throw p;we(1,0)}}function Dr(t,r,n,i){var o=Ce();try{return ie(t)(r,n,i)}catch(u){if(_e(o),u!==u+0)throw u;we(1,0)}}f.getTempRet0=tt,f.setTempRet0=et;function Lr(t){t.M()}var Ze={a:Rr},De,z,Ne,we,et,tt,Ce,_e;return(WebAssembly.instantiateStreaming?WebAssembly.instantiateStreaming(fetch("jassub-worker-modern.wasm"),Ze):WebAssembly.instantiate(f.wasm,Ze)).then(function(t){w=(t.instance||t).exports,f._malloc=De=w.N,z=w.O,Ne=w.P,f.__embind_initialize_bindings=w.Q,we=w.S,et=w.T,tt=w.U,Ce=w.V,_e=w.W,w.X,w.Y,w.Z,w._,ue=w.R,Lr(w),_()}),f.ready};String.prototype.startsWith||(String.prototype.startsWith=function(s,f=0){return this.substring(f,s.length)===s});String.prototype.includes||(String.prototype.includes=function(s,f){return this.indexOf(s,f)!==-1});Uint8Array.prototype.slice||(Uint8Array.prototype.slice=function(s,f){return new Uint8Array(this.subarray(s,f))});function toAbsoluteIndex(s,f){const h=s>>0;return h<0?Math.max(h+f,0):Math.min(h,f)}Uint8Array.prototype.fill||(Int8Array.prototype.fill=Int16Array.prototype.fill=Int32Array.prototype.fill=Uint8Array.prototype.fill=Uint16Array.prototype.fill=Uint32Array.prototype.fill=Float32Array.prototype.fill=Float64Array.prototype.fill=Array.prototype.fill=function(s){if(this==null)throw new TypeError("this is null or not defined");const f=Object(this),h=f.length>>>0,C=arguments.length;let y=toAbsoluteIndex(C>1?arguments[1]:void 0,h);const _=C>2?arguments[2]:void 0,A=_===void 0?h:toAbsoluteIndex(_,h);for(;A>y;)f[y++]=s;return f});Uint8Array.prototype.copyWithin||(Int8Array.prototype.copyWithin=Int16Array.prototype.copyWithin=Int32Array.prototype.copyWithin=Uint8Array.prototype.copyWithin=Uint16Array.prototype.copyWithin=Uint32Array.prototype.copyWithin=Float32Array.prototype.copyWithin=Float64Array.prototype.copyWithin=Array.prototype.copyWithin=function(s,f){const h=Object(this),C=h.length>>>0;let y=toAbsoluteIndex(s,C),_=toAbsoluteIndex(f,C);const A=arguments.length>2?arguments[2]:void 0;let w=Math.min((A===void 0?C:toAbsoluteIndex(A,C))-_,C-y),F=1;for(_0;)_ in h?h[y]=h[_]:delete h[y],y+=F,_+=F;return h});Date.now||(Date.now=()=>new Date().getTime());"performance"in self||(self.performance={now:()=>Date.now()});if(typeof console>"u"){const s=(f,h)=>{postMessage({target:"console",command:f,content:JSON.stringify(Array.prototype.slice.call(h))})};self.console={log:function(){s("log",arguments)},debug:function(){s("debug",arguments)},info:function(){s("info",arguments)},warn:function(){s("warn",arguments)},error:function(){s("error",arguments)}},console.log("Detected lack of console, overridden console")}let promiseSupported=typeof Promise<"u";if(promiseSupported)try{let s;new Promise(f=>{s=f}),s()}catch{promiseSupported=!1}promiseSupported||(self.Promise=function(s){let f=()=>{};return s(h=>setTimeout(()=>f(h),0)),{then:h=>f=h}});const read_=(s,f)=>{const h=new XMLHttpRequest;return h.open("GET",s,!1),h.responseType=f?"arraybuffer":"text",h.send(null),h.response},readAsync=(s,f,h)=>{const C=new XMLHttpRequest;C.open("GET",s,!0),C.responseType="arraybuffer",C.onload=()=>{if((C.status===200||C.status===0)&&C.response)return f(C.response)},C.onerror=h,C.send(null)};let lastCurrentTime=0;const rate=1;let rafId=null,nextIsRaf=!1,lastCurrentTimeReceivedAt=Date.now(),targetFps=24,useLocalFonts=!1,blendMode="js",availableFonts={};const fontMap_={};let fontId=0,debug;self.width=0;self.height=0;let asyncRender=!1;self.addFont=({font:s})=>asyncWrite(s);const findAvailableFonts=s=>{s=s.trim().toLowerCase(),s.startsWith("@")&&(s=s.substring(1)),!fontMap_[s]&&(fontMap_[s]=!0,availableFonts[s]?asyncWrite(availableFonts[s]):useLocalFonts&&postMessage({target:"getLocalFont",font:s}))},asyncWrite=s=>{typeof s=="string"?readAsync(s,f=>{allocFont(new Uint8Array(f))},console.error):allocFont(s)},allocFont=s=>{const f=_malloc(s.byteLength);self.HEAPU8.set(s,f),jassubObj.addFont("font-"+fontId++,f,s.byteLength),jassubObj.reloadFonts()},processAvailableFonts=s=>{if(!availableFonts)return;const f=parseAss(s);for(let y=0;y{processAvailableFonts(s),dropAllBlur&&(s=dropBlur(s)),jassubObj.createTrackMem(s),subtitleColorSpace=libassYCbCrMap[jassubObj.trackColorSpace],postMessage({target:"verifyColorSpace",subtitleColorSpace})};self.getColorSpace=()=>postMessage({target:"verifyColorSpace",subtitleColorSpace});self.freeTrack=()=>{jassubObj.removeTrack()};self.setTrackByUrl=({url:s})=>{self.setTrack({content:read_(s)})};const getCurrentTime=()=>{const s=(Date.now()-lastCurrentTimeReceivedAt)/1e3;return _isPaused?lastCurrentTime:(s>5&&(console.error("Didn't received currentTime > 5 seconds. Assuming video was paused."),setIsPaused(!0)),lastCurrentTime+s*rate)},setCurrentTime=s=>{lastCurrentTime=s,lastCurrentTimeReceivedAt=Date.now(),rafId||(nextIsRaf?rafId=requestAnimationFrame(renderLoop):(renderLoop(),setTimeout(()=>{nextIsRaf=!1},20)))};let _isPaused=!0;const setIsPaused=s=>{s!==_isPaused&&(_isPaused=s,s?rafId&&(clearTimeout(rafId),rafId=null):(lastCurrentTimeReceivedAt=Date.now(),rafId=requestAnimationFrame(renderLoop)))},a="BT601",b="BT709",c="SMPTE240M",d="FCC",libassYCbCrMap=[null,a,null,a,a,b,b,c,c,d,d],render=(s,f)=>{const h={},C=performance.now(),y=blendMode==="wasm"?jassubObj.renderBlend(s,f||0):jassubObj.renderImage(s,f||0);if(debug){const _=performance.now(),A=jassubObj.time;h.WASMRenderTime=A-C,h.WASMBitmapDecodeTime=_-A,h.JSRenderTime=Date.now()}if(jassubObj.changed!==0||f){const _=[],A=[];if(!y)return paintImages({images:_,buffers:A,times:h});if(asyncRender){const w=[];for(let F=y,j=0;j{for(let j=0;j<_.length;j++)_[j].image=F[j];debug&&(h.JSBitmapGenerationTime=Date.now()-h.JSRenderTime),paintImages({images:_,buffers:F,times:h})})}else{for(let w=y,F=0;F{lastCurrentTime=s,render(s)};const renderLoop=s=>{rafId=0,render(getCurrentTime(),s),_isPaused||(rafId=requestAnimationFrame(renderLoop))},paintImages=({times:s,images:f,buffers:h})=>{const C={target:"render",asyncRender,images:f,times:s,width:self.width,height:self.height,colorSpace:subtitleColorSpace};if(offscreenRender){(offCanvas.height!==self.height||offCanvas.width!==self.width)&&(offCanvas.width=self.width,offCanvas.height=self.height),offCanvasCtx.clearRect(0,0,self.width,self.height);for(const y of f)y.image&&(asyncRender?(offCanvasCtx.drawImage(y.image,y.x,y.y),y.image.close()):(bufferCanvas.width=y.w,bufferCanvas.height=y.h,bufferCtx.putImageData(new ImageData(self.HEAPU8C.subarray(y.image,y.image+y.w*y.h*4),y.w,y.h),0,0),offCanvasCtx.drawImage(bufferCanvas,y.x,y.y)));if(offscreenRender==="hybrid"){if(!f.length)return postMessage(C);debug&&(s.bitmaps=f.length);try{const y=offCanvas.transferToImageBitmap();C.images=[{image:y,x:0,y:0}],C.asyncRender=!0,postMessage(C,[y])}catch{postMessage({target:"unbusy"})}}else{if(debug){s.JSRenderTime=Date.now()-s.JSRenderTime-(s.JSBitmapGenerationTime||0);let y=0;for(const _ in s)y+=s[_];console.log("Bitmaps: "+f.length+" Total: "+(y|0)+"ms",s)}postMessage({target:"unbusy"})}}else postMessage(C,h)},parseAss=s=>{let f,h,C,y,_,A,w,F,j,E;const M=[],O=s.split(/[\r\n]+/g);for(F=0;Fh.length&&(C=A.slice(h.length-1).join(","),A=A.slice(0,h.length-1),A.push(C)),A=A.map(J=>J.trim()),h)){for(w={},j=0;js.replace(blurRegex,""),requestAnimationFrame=(()=>{let s=0;return f=>{const h=Date.now();if(s===0)s=h+1e3/targetFps;else for(;h+2>=s;)s+=1e3/targetFps;const C=Math.max(s-h,0);return setTimeout(f,C)}})(),_applyKeys=(s,f)=>{for(const h of Object.keys(s))f[h]=s[h]};let offCanvas,offCanvasCtx,offscreenRender,bufferCanvas,bufferCtx,jassubObj,subtitleColorSpace,dropAllBlur,_malloc,hasBitmapBug;self.init=data=>{hasBitmapBug=data.hasBitmapBug;try{const s=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));if(!(s instanceof WebAssembly.Module)||!(new WebAssembly.Instance(s)instanceof WebAssembly.Instance))throw new Error("WASM not supported")}catch(e){console.warn(e),eval(read_(data.legacyWasmUrl))}if(WebAssembly.instantiateStreaming){const s=self.fetch;self.fetch=f=>s(data.wasmUrl)}Module({wasm:!WebAssembly.instantiateStreaming&&read_(data.wasmUrl,!0)}).then(s=>{_malloc=s._malloc,self.width=data.width,self.height=data.height,blendMode=data.blendMode,asyncRender=data.asyncRender,asyncRender&&typeof createImageBitmap>"u"&&(asyncRender=!1,console.error("'createImageBitmap' needed for 'asyncRender' unsupported!")),availableFonts=data.availableFonts,debug=data.debug,targetFps=data.targetFps||targetFps,useLocalFonts=data.useLocalFonts,dropAllBlur=data.dropAllBlur;const f=data.fallbackFont.toLowerCase();jassubObj=new s.JASSUB(self.width,self.height,f||null,debug),f&&findAvailableFonts(f);let h=data.subContent;h||(h=read_(data.subUrl)),processAvailableFonts(h),dropAllBlur&&(h=dropBlur(h));for(const C of data.fonts||[])asyncWrite(C);jassubObj.createTrackMem(h),subtitleColorSpace=libassYCbCrMap[jassubObj.trackColorSpace],jassubObj.setDropAnimations(data.dropAllAnimations||0),(data.libassMemoryLimit>0||data.libassGlyphLimit>0)&&jassubObj.setMemoryLimits(data.libassGlyphLimit||0,data.libassMemoryLimit||0),postMessage({target:"ready"}),postMessage({target:"verifyColorSpace",subtitleColorSpace})})};self.offscreenCanvas=({transferable:s})=>{offCanvas=s[0],offCanvasCtx=offCanvas.getContext("2d"),asyncRender||(bufferCanvas=new OffscreenCanvas(self.height,self.width),bufferCtx=bufferCanvas.getContext("2d",{desynchronized:!0})),offscreenRender=!0};self.detachOffscreen=()=>{offCanvas=new OffscreenCanvas(self.height,self.width),offCanvasCtx=offCanvas.getContext("2d",{desynchronized:!0}),offscreenRender="hybrid"};self.canvas=({width:s,height:f,force:h})=>{if(s==null)throw new Error("Invalid canvas size specified");self.width=s,self.height=f,jassubObj&&jassubObj.resizeCanvas(s,f),h&&render(lastCurrentTime,!0)};self.video=({currentTime:s,isPaused:f,rate:h})=>{s!=null&&setCurrentTime(s),f!=null&&setIsPaused(f),h=h||h};self.destroy=()=>{jassubObj.quitLibrary()};self.createEvent=({event:s})=>{_applyKeys(s,jassubObj.getEvent(jassubObj.allocEvent()))};self.getEvents=()=>{const s=[];for(let f=0;f{_applyKeys(s,jassubObj.getEvent(f))};self.removeEvent=({index:s})=>{jassubObj.removeEvent(s)};self.createStyle=({style:s})=>{_applyKeys(s,jassubObj.getStyle(jassubObj.allocStyle()))};self.getStyles=()=>{const s=[];for(let f=0;f{_applyKeys(s,jassubObj.getStyle(f))};self.removeStyle=({index:s})=>{jassubObj.removeStyle(s)};onmessage=({data:s})=>{if(self[s.target])self[s.target](s);else throw new Error("Unknown event target "+s.target)}; +`,W.push(S),Vt(Function,W).apply(null,R)}function zt(t,r,n,i,o,u){A(r>0);var p=qe(r,n);o=q(i,o),K([],[t],function(f){f=f[0];var v="constructor "+f.name;if(f.registeredClass.constructor_body===void 0&&(f.registeredClass.constructor_body=[]),f.registeredClass.constructor_body[r-1]!==void 0)throw new N("Cannot register multiple constructors with identical number of parameters ("+(r-1)+") for class '"+f.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!");return f.registeredClass.constructor_body[r-1]=function(){ae("Cannot construct "+f.name+" due to unbound types",p)},K([],p,function(g){return g.splice(1,0,null),f.registeredClass.constructor_body[r-1]=Ye(v,g,null,o,u),[]}),[]})}function Jt(t,r,n,i,o,u,p,f,v){var g=qe(n,i);r=D(r),u=q(o,u),K([],[t],function(m){m=m[0];var P=m.name+"."+r;r.startsWith("@@")&&(r=Symbol[r.substring(2)]),f&&m.registeredClass.pureVirtualFunctions.push(r);function $(){ae("Cannot call "+P+" due to unbound types",g)}var S=m.registeredClass.instancePrototype,U=S[r];return U===void 0||U.overloadTable===void 0&&U.className!==m.name&&U.argCount===n-2?($.argCount=n-2,$.className=m.name,S[r]=$):(Je(S,r,P),S[r].overloadTable[n-2]=$),K([],g,function(W){var R=Ye(P,W,m,u,p,v);return S[r].overloadTable===void 0?(R.argCount=n-2,S[r]=R):S[r].overloadTable[n-2]=R,[]}),[]})}function Xe(t,r,n){return t instanceof Object||T(n+' with invalid "this": '+t),t instanceof r.registeredClass.constructor||T(n+' incompatible with "this" of type '+t.constructor.name),t.$$.ptr||T("cannot call emscripten binding method "+n+" on deleted object"),ye(t.$$.ptr,t.$$.ptrType.registeredClass,r.registeredClass)}function Gt(t,r,n,i,o,u,p,f,v,g){r=D(r),o=q(i,o),K([],[t],function(m){m=m[0];var P=m.name+"."+r,$={get:function(){ae("Cannot access "+P+" due to unbound types",[n,p])},enumerable:!0,configurable:!0};return v?$.set=function(){ae("Cannot access "+P+" due to unbound types",[n,p])}:$.set=function(S){T(P+" is a read-only property")},Object.defineProperty(m.registeredClass.instancePrototype,r,$),K([],v?[n,p]:[n],function(S){var U=S[0],W={get:function(){var L=Xe(this,m,P+" getter");return U.fromWireType(o(u,L))},enumerable:!0};if(v){v=q(f,v);var R=S[1];W.set=function(L){var oe=Xe(this,m,P+" setter"),I=[];v(g,oe,R.toWireType(I,L)),Ke(I)}}return Object.defineProperty(m.registeredClass.instancePrototype,r,W),[]}),[]})}function qt(){this.allocated=[void 0],this.freelist=[],this.get=function(t){return this.allocated[t]},this.has=function(t){return this.allocated[t]!==void 0},this.allocate=function(t){var r=this.freelist.pop()||this.allocated.length;return this.allocated[r]=t,r},this.free=function(t){this.allocated[t]=void 0,this.freelist.push(t)}}var H=new qt;function Kt(t){t>=H.reserved&&--H.get(t).refcount===0&&H.free(t)}function Yt(){for(var t=0,r=H.reserved;r>2])};case 3:return function(n){return this.fromWireType(fe[n>>3])};default:throw new TypeError("Unknown float type: "+t)}}function Nt(t,r,n){var i=$e(n);r=D(r),x(t,{name:r,fromWireType:function(o){return o},toWireType:function(o,u){return u},argPackAdvance:8,readValueFromPointer:Zt(r,i),destructorFunction:null})}function er(t,r,n){switch(r){case 0:return n?function(o){return j[o]}:function(o){return O[o]};case 1:return n?function(o){return E[o>>1]}:function(o){return J[o>>1]};case 2:return n?function(o){return M[o>>2]}:function(o){return k[o>>2]};default:throw new TypeError("Unknown integer type: "+t)}}function tr(t,r,n,i,o){r=D(r);var u=$e(n),p=function(P){return P};if(i===0){var f=32-8*n;p=function(P){return P<>>f}}var v=r.includes("unsigned"),g=function(P,$){},m;v?m=function(P,$){return g($,this.name),$>>>0}:m=function(P,$){return g($,this.name),$},x(t,{name:r,fromWireType:p,toWireType:m,argPackAdvance:8,readValueFromPointer:er(r,u,i!==0),destructorFunction:null})}function rr(t,r,n){var i=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array],o=i[r];function u(p){p=p>>2;var f=k,v=f[p],g=f[p+1];return new o(f.buffer,g,v)}n=D(n),x(t,{name:n,fromWireType:u,argPackAdvance:8,readValueFromPointer:u},{ignoreDuplicateRegistrations:!0})}function nr(t){for(var r=0,n=0;n=55296&&i<=57343?(r+=4,++n):r+=3}return r}function ir(t,r){r=D(r);var n=r==="std::string";x(t,{name:r,fromWireType:function(i){var o=k[i>>2],u=i+4,p;if(n)for(var f=u,v=0;v<=o;++v){var g=u+v;if(v==o||O[g]==0){var m=g-f,P=X(f,m);p===void 0?p=P:(p+=String.fromCharCode(0),p+=P),f=g+1}}else{for(var $=new Array(o),v=0;v>2]=u,n&&p)Fe(o,v,u+1);else if(p)for(var g=0;g255&&(z(v),T("String has UTF-16 code units that do not fit in 8 bits")),O[v+g]=m}else for(var g=0;g>1,o=i+r/2;!(i>=o)&&J[i];)++i;if(n=i<<1,n-t>32&&Qe)return Qe.decode(O.subarray(t,n));for(var u="",p=0;!(p>=r/2);++p){var f=E[t+p*2>>1];if(f==0)break;u+=String.fromCharCode(f)}return u}function sr(t,r,n){if(n===void 0&&(n=2147483647),n<2)return 0;n-=2;for(var i=r,o=n>1]=p,r+=2}return E[r>>1]=0,r-i}function or(t){return t.length*2}function lr(t,r){for(var n=0,i="";!(n>=r/4);){var o=M[t+n*4>>2];if(o==0)break;if(++n,o>=65536){var u=o-65536;i+=String.fromCharCode(55296|u>>10,56320|u&1023)}else i+=String.fromCharCode(o)}return i}function fr(t,r,n){if(n===void 0&&(n=2147483647),n<4)return 0;for(var i=r,o=i+n-4,u=0;u=55296&&p<=57343){var f=t.charCodeAt(++u);p=65536+((p&1023)<<10)|f&1023}if(M[r>>2]=p,r+=4,r+4>o)break}return M[r>>2]=0,r-i}function ur(t){for(var r=0,n=0;n=55296&&i<=57343&&++n,r+=4}return r}function cr(t,r,n){n=D(n);var i,o,u,p,f;r===2?(i=ar,o=sr,p=or,u=function(){return J},f=1):r===4&&(i=lr,o=fr,p=ur,u=function(){return k},f=2),x(t,{name:n,fromWireType:function(v){for(var g=k[v>>2],m=u(),P,$=v+4,S=0;S<=g;++S){var U=v+4+S*r;if(S==g||m[U>>f]==0){var W=U-$,R=i($,W);P===void 0?P=R:(P+=String.fromCharCode(0),P+=R),$=U+r}}return z(v),P},toWireType:function(v,g){typeof g!="string"&&T("Cannot pass non-string to C++ string type "+n);var m=p(g),P=De(4+m+r);return k[P>>2]=m>>f,o(g,P+4,m+r),v!==null&&v.push(z,P),P},argPackAdvance:8,readValueFromPointer:be,destructorFunction:function(v){z(v)}})}function dr(t,r){r=D(r),x(t,{isVoid:!0,name:r,argPackAdvance:0,fromWireType:function(){},toWireType:function(n,i){}})}function pr(){throw 1/0}function gr(){F("")}var We;typeof performance<"u"&&performance.now?We=function(){return performance.now()}:We=Date.now;function hr(){return 2147483648}function vr(t){var r=B.buffer;try{return B.grow(t-r.byteLength+65535>>>16),Y(),1}catch{}}function yr(t){var r=O.length;t=t>>>0;var n=hr();if(t>n)return!1;for(var i=function(v,g){return v+(g-v%g)%g},o=1;o<=4;o*=2){var u=r*(1+.2/o);u=Math.min(u,t+100663296);var p=Math.min(n,i(Math.max(t,u),65536)),f=vr(p);if(f)return!0}return!1}var Ie={};function br(){return"./this.program"}function se(){if(!se.strings){var t=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",r={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:t,_:br()};for(var n in Ie)Ie[n]===void 0?delete r[n]:r[n]=Ie[n];var i=[];for(var n in r)i.push(n+"="+r[n]);se.strings=i}return se.strings}function mr(t,r){for(var n=0;n>0]=t.charCodeAt(n);j[r>>0]=0}function wr(t,r){var n=0;return se().forEach(function(i,o){var u=r+n;k[t+o*4>>2]=u,mr(i,u),n+=i.length+1}),0}function Cr(t,r){var n=se();k[t>>2]=n.length;var i=0;return n.forEach(function(o){i+=o.length+1}),k[r>>2]=i,0}function _r(t){throw"exit("+t+")"}var Tr=_r;function Pr(t){return 52}function Ar(t,r,n,i){return 52}function Fr(t,r,n,i,o){return 70}var $r=[null,[],[]];function Sr(t,r){var n=$r[t];r===0||r===10?((t===1?w:y)(pe(n,0)),n.length=0):n.push(r)}function jr(t,r,n,i){for(var o=0,u=0;u>2],f=k[r+4>>2];r+=8;for(var v=0;v>2]=o,0}ft(),N=l.BindingError=Oe(Error,"BindingError"),He=l.InternalError=Oe(Error,"InternalError"),$t(),mt(),Wt(),Ge=l.UnboundTypeError=Oe(Error,"UnboundTypeError"),Xt();var Or={b:Te,m:Pe,A:at,F:st,n:ot,t:lt,q:dt,i:xt,u:zt,d:Jt,c:Gt,G:Qt,o:Nt,g:tr,e:rr,p:ir,k:cr,r:dr,y:pr,f:gr,l:We,z:yr,B:wr,C:Cr,j:Tr,h:Pr,E:Ar,s:Fr,D:jr,x:Er,v:kr,w:Mr,a:B};function Er(t,r,n){var i=Ce();try{return ie(t)(r,n)}catch(o){if(_e(i),o!==o+0)throw o;we(1,0)}}function Mr(t,r,n,i,o){var u=Ce();try{return ie(t)(r,n,i,o)}catch(p){if(_e(u),p!==p+0)throw p;we(1,0)}}function kr(t,r,n,i){var o=Ce();try{return ie(t)(r,n,i)}catch(u){if(_e(o),u!==u+0)throw u;we(1,0)}}l.getTempRet0=tt,l.setTempRet0=et;function Ur(t){t.H()}var Ze={a:Or},De,z,Ne,we,et,tt,Ce,_e;return(WebAssembly.instantiateStreaming?WebAssembly.instantiateStreaming(fetch("jassub-worker-modern.wasm"),Ze):WebAssembly.instantiate(l.wasm,Ze)).then(function(t){C=(t.instance||t).exports,l._malloc=De=C.I,z=C.J,Ne=C.K,l.__embind_initialize_bindings=C.L,we=C.N,et=C.O,tt=C.P,Ce=C.Q,_e=C.R,C.S,C.T,C.U,C.V,ue=C.M,Ur(C),_()}),l.ready};String.prototype.startsWith||(String.prototype.startsWith=function(s,l=0){return this.substring(l,s.length)===s});String.prototype.includes||(String.prototype.includes=function(s,l){return this.indexOf(s,l)!==-1});Uint8Array.prototype.slice||(Uint8Array.prototype.slice=function(s,l){return new Uint8Array(this.subarray(s,l))});function toAbsoluteIndex(s,l){const h=s>>0;return h<0?Math.max(h+l,0):Math.min(h,l)}Uint8Array.prototype.fill||(Int8Array.prototype.fill=Int16Array.prototype.fill=Int32Array.prototype.fill=Uint8Array.prototype.fill=Uint16Array.prototype.fill=Uint32Array.prototype.fill=Float32Array.prototype.fill=Float64Array.prototype.fill=Array.prototype.fill=function(s){if(this==null)throw new TypeError("this is null or not defined");const l=Object(this),h=l.length>>>0,w=arguments.length;let y=toAbsoluteIndex(w>1?arguments[1]:void 0,h);const _=w>2?arguments[2]:void 0,A=_===void 0?h:toAbsoluteIndex(_,h);for(;A>y;)l[y++]=s;return l});Uint8Array.prototype.copyWithin||(Int8Array.prototype.copyWithin=Int16Array.prototype.copyWithin=Int32Array.prototype.copyWithin=Uint8Array.prototype.copyWithin=Uint16Array.prototype.copyWithin=Uint32Array.prototype.copyWithin=Float32Array.prototype.copyWithin=Float64Array.prototype.copyWithin=Array.prototype.copyWithin=function(s,l){const h=Object(this),w=h.length>>>0;let y=toAbsoluteIndex(s,w),_=toAbsoluteIndex(l,w);const A=arguments.length>2?arguments[2]:void 0;let C=Math.min((A===void 0?w:toAbsoluteIndex(A,w))-_,w-y),F=1;for(_0;)_ in h?h[y]=h[_]:delete h[y],y+=F,_+=F;return h});Date.now||(Date.now=()=>new Date().getTime());"performance"in self||(self.performance={now:()=>Date.now()});if(typeof console>"u"){const s=(l,h)=>{postMessage({target:"console",command:l,content:JSON.stringify(Array.prototype.slice.call(h))})};self.console={log:function(){s("log",arguments)},debug:function(){s("debug",arguments)},info:function(){s("info",arguments)},warn:function(){s("warn",arguments)},error:function(){s("error",arguments)}},console.log("Detected lack of console, overridden console")}let promiseSupported=typeof Promise<"u";if(promiseSupported)try{let s;new Promise(l=>{s=l}),s()}catch{promiseSupported=!1}promiseSupported||(self.Promise=function(s){let l=()=>{};return s(h=>setTimeout(()=>l(h),0)),{then:h=>l=h}});const read_=(s,l)=>{const h=new XMLHttpRequest;return h.open("GET",s,!1),h.responseType=l?"arraybuffer":"text",h.send(null),h.response},readAsync=(s,l,h)=>{const w=new XMLHttpRequest;w.open("GET",s,!0),w.responseType="arraybuffer",w.onload=()=>{if((w.status===200||w.status===0)&&w.response)return l(w.response)},w.onerror=h,w.send(null)};let lastCurrentTime=0;const rate=1;let rafId=null,nextIsRaf=!1,lastCurrentTimeReceivedAt=Date.now(),targetFps=24,useLocalFonts=!1,blendMode="js",availableFonts={};const fontMap_={};let fontId=0,debug;self.width=0;self.height=0;let asyncRender=!1;self.addFont=({font:s})=>asyncWrite(s);const findAvailableFonts=s=>{s=s.trim().toLowerCase(),s.startsWith("@")&&(s=s.substring(1)),!fontMap_[s]&&(fontMap_[s]=!0,availableFonts[s]?asyncWrite(availableFonts[s]):useLocalFonts&&postMessage({target:"getLocalFont",font:s}))},asyncWrite=s=>{typeof s=="string"?readAsync(s,l=>{allocFont(new Uint8Array(l))},console.error):allocFont(s)},allocFont=s=>{const l=_malloc(s.byteLength);self.HEAPU8.set(s,l),jassubObj.addFont("font-"+fontId++,l,s.byteLength),jassubObj.reloadFonts()},processAvailableFonts=s=>{if(!availableFonts)return;const l=parseAss(s);for(let y=0;y{processAvailableFonts(s),dropAllBlur&&(s=dropBlur(s)),jassubObj.createTrackMem(s),subtitleColorSpace=libassYCbCrMap[jassubObj.trackColorSpace],postMessage({target:"verifyColorSpace",subtitleColorSpace})};self.getColorSpace=()=>postMessage({target:"verifyColorSpace",subtitleColorSpace});self.freeTrack=()=>{jassubObj.removeTrack()};self.setTrackByUrl=({url:s})=>{self.setTrack({content:read_(s)})};const getCurrentTime=()=>{const s=(Date.now()-lastCurrentTimeReceivedAt)/1e3;return _isPaused?lastCurrentTime:(s>5&&(console.error("Didn't received currentTime > 5 seconds. Assuming video was paused."),setIsPaused(!0)),lastCurrentTime+s*rate)},setCurrentTime=s=>{lastCurrentTime=s,lastCurrentTimeReceivedAt=Date.now(),rafId||(nextIsRaf?rafId=requestAnimationFrame(renderLoop):(renderLoop(),setTimeout(()=>{nextIsRaf=!1},20)))};let _isPaused=!0;const setIsPaused=s=>{s!==_isPaused&&(_isPaused=s,s?rafId&&(clearTimeout(rafId),rafId=null):(lastCurrentTimeReceivedAt=Date.now(),rafId=requestAnimationFrame(renderLoop)))},a="BT601",b="BT709",c="SMPTE240M",d="FCC",libassYCbCrMap=[null,a,null,a,a,b,b,c,c,d,d],render=(s,l)=>{const h={},w=performance.now(),y=blendMode==="wasm"?jassubObj.renderBlend(s,l||0):jassubObj.renderImage(s,l||0);if(debug){const _=performance.now(),A=jassubObj.time;h.WASMRenderTime=A-w,h.WASMBitmapDecodeTime=_-A,h.JSRenderTime=Date.now()}if(jassubObj.changed!==0||l){const _=[],A=[];if(!y)return paintImages({images:_,buffers:A,times:h});if(asyncRender){const C=[];for(let F=y,j=0;j{for(let j=0;j<_.length;j++)_[j].image=F[j];debug&&(h.JSBitmapGenerationTime=Date.now()-h.JSRenderTime),paintImages({images:_,buffers:F,times:h})})}else{for(let C=y,F=0;F{lastCurrentTime=s,render(s)};const renderLoop=s=>{rafId=0,render(getCurrentTime(),s),_isPaused||(rafId=requestAnimationFrame(renderLoop))},paintImages=({times:s,images:l,buffers:h})=>{const w={target:"render",asyncRender,images:l,times:s,width:self.width,height:self.height,colorSpace:subtitleColorSpace};if(offscreenRender){(offCanvas.height!==self.height||offCanvas.width!==self.width)&&(offCanvas.width=self.width,offCanvas.height=self.height),offCanvasCtx.clearRect(0,0,self.width,self.height);for(const y of l)y.image&&(asyncRender?(offCanvasCtx.drawImage(y.image,y.x,y.y),y.image.close()):(bufferCanvas.width=y.w,bufferCanvas.height=y.h,bufferCtx.putImageData(new ImageData(self.HEAPU8C.subarray(y.image,y.image+y.w*y.h*4),y.w,y.h),0,0),offCanvasCtx.drawImage(bufferCanvas,y.x,y.y)));if(offscreenRender==="hybrid"){if(!l.length)return postMessage(w);debug&&(s.bitmaps=l.length);try{const y=offCanvas.transferToImageBitmap();w.images=[{image:y,x:0,y:0}],w.asyncRender=!0,postMessage(w,[y])}catch{postMessage({target:"unbusy"})}}else{if(debug){s.JSRenderTime=Date.now()-s.JSRenderTime-(s.JSBitmapGenerationTime||0);let y=0;for(const _ in s)y+=s[_];console.log("Bitmaps: "+l.length+" Total: "+(y|0)+"ms",s)}postMessage({target:"unbusy"})}}else postMessage(w,h)},parseAss=s=>{let l,h,w,y,_,A,C,F,j,E;const M=[],O=s.split(/[\r\n]+/g);for(F=0;Fh.length&&(w=A.slice(h.length-1).join(","),A=A.slice(0,h.length-1),A.push(w)),A=A.map(J=>J.trim()),h)){for(C={},j=0;js.replace(blurRegex,""),requestAnimationFrame=(()=>{let s=0;return l=>{const h=Date.now();if(s===0)s=h+1e3/targetFps;else for(;h+2>=s;)s+=1e3/targetFps;const w=Math.max(s-h,0);return setTimeout(l,w)}})(),_applyKeys=(s,l)=>{for(const h of Object.keys(s))l[h]=s[h]};let offCanvas,offCanvasCtx,offscreenRender,bufferCanvas,bufferCtx,jassubObj,subtitleColorSpace,dropAllBlur,_malloc,hasBitmapBug;self.init=data=>{hasBitmapBug=data.hasBitmapBug;try{const s=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));if(!(s instanceof WebAssembly.Module)||!(new WebAssembly.Instance(s)instanceof WebAssembly.Instance))throw new Error("WASM not supported")}catch(e){console.warn(e),eval(read_(data.legacyWasmUrl))}if(WebAssembly.instantiateStreaming){const s=self.fetch;self.fetch=l=>s(data.wasmUrl)}Module({wasm:!WebAssembly.instantiateStreaming&&read_(data.wasmUrl,!0)}).then(s=>{_malloc=s._malloc,self.width=data.width,self.height=data.height,blendMode=data.blendMode,asyncRender=data.asyncRender,asyncRender&&typeof createImageBitmap>"u"&&(asyncRender=!1,console.error("'createImageBitmap' needed for 'asyncRender' unsupported!")),availableFonts=data.availableFonts,debug=data.debug,targetFps=data.targetFps||targetFps,useLocalFonts=data.useLocalFonts,dropAllBlur=data.dropAllBlur;const l=data.fallbackFont.toLowerCase();jassubObj=new s.JASSUB(self.width,self.height,l||null,debug),l&&findAvailableFonts(l);let h=data.subContent;h||(h=read_(data.subUrl)),processAvailableFonts(h),dropAllBlur&&(h=dropBlur(h));for(const w of data.fonts||[])asyncWrite(w);jassubObj.createTrackMem(h),subtitleColorSpace=libassYCbCrMap[jassubObj.trackColorSpace],jassubObj.setDropAnimations(data.dropAllAnimations||0),(data.libassMemoryLimit>0||data.libassGlyphLimit>0)&&jassubObj.setMemoryLimits(data.libassGlyphLimit||0,data.libassMemoryLimit||0),postMessage({target:"ready"}),postMessage({target:"verifyColorSpace",subtitleColorSpace})})};self.offscreenCanvas=({transferable:s})=>{offCanvas=s[0],offCanvasCtx=offCanvas.getContext("2d"),asyncRender||(bufferCanvas=new OffscreenCanvas(self.height,self.width),bufferCtx=bufferCanvas.getContext("2d",{desynchronized:!0})),offscreenRender=!0};self.detachOffscreen=()=>{offCanvas=new OffscreenCanvas(self.height,self.width),offCanvasCtx=offCanvas.getContext("2d",{desynchronized:!0}),offscreenRender="hybrid"};self.canvas=({width:s,height:l,videoWidth:h,videoHeight:w,force:y})=>{if(s==null)throw new Error("Invalid canvas size specified");self.width=s,self.height=l,jassubObj&&jassubObj.resizeCanvas(s,l,h,w),y&&render(lastCurrentTime,!0)};self.video=({currentTime:s,isPaused:l,rate:h})=>{s!=null&&setCurrentTime(s),l!=null&&setIsPaused(l),h=h||h};self.destroy=()=>{jassubObj.quitLibrary()};self.createEvent=({event:s})=>{_applyKeys(s,jassubObj.getEvent(jassubObj.allocEvent()))};self.getEvents=()=>{const s=[];for(let l=0;l{_applyKeys(s,jassubObj.getEvent(l))};self.removeEvent=({index:s})=>{jassubObj.removeEvent(s)};self.createStyle=({style:s})=>{const l=jassubObj.getStyle(jassubObj.allocStyle());return _applyKeys(s,l),l};self.getStyles=()=>{const s=[];for(let l=0;l{_applyKeys(s,jassubObj.getStyle(l))};self.removeStyle=({index:s})=>{jassubObj.removeStyle(s)};self.styleOverride=s=>{jassubObj.styleOverride(self.createStyle(s))};self.disableStyleOverride=()=>{jassubObj.disableStyleOverride()};self.defaultFont=({font:s})=>{jassubObj.setDefaultFont(s)};onmessage=({data:s})=>{if(self[s.target])self[s.target](s);else throw new Error("Unknown event target "+s.target)}; diff --git a/seanime-web/public/jassub/jassub-worker.wasm b/seanime-web/public/jassub/jassub-worker.wasm old mode 100644 new mode 100755 index ad64426e..d261b2f6 Binary files a/seanime-web/public/jassub/jassub-worker.wasm and b/seanime-web/public/jassub/jassub-worker.wasm differ diff --git a/seanime-web/src/app/(main)/_features/video-core/video-core-pgs-renderer.ts b/seanime-web/src/app/(main)/_features/video-core/video-core-pgs-renderer.ts index a4f15813..02cfd4d1 100644 --- a/seanime-web/src/app/(main)/_features/video-core/video-core-pgs-renderer.ts +++ b/seanime-web/src/app/(main)/_features/video-core/video-core-pgs-renderer.ts @@ -32,6 +32,7 @@ export class VideoCorePgsRenderer { private _isDestroyed: boolean = false private _canvasWidth: number = 0 private _canvasHeight: number = 0 + private _resizeObserver: ResizeObserver | null = null constructor(options: VideoCorePgsRendererOptions) { this._videoElement = options.videoElement @@ -134,9 +135,9 @@ export class VideoCorePgsRenderer { } // Clean up resize observer - if (this._canvas && (this._canvas as any)._resizeObserver) { - (this._canvas as any)._resizeObserver.disconnect() - (this._canvas as any)._resizeObserver = null + if (this._resizeObserver) { + this._resizeObserver.disconnect() + this._resizeObserver = null } if (this._canvas && this._canvas.parentElement) { @@ -213,14 +214,11 @@ export class VideoCorePgsRenderer { private _setupResizeObserver() { if (!this._videoElement || !this._canvas) return - const resizeObserver = new ResizeObserver(() => { + this._resizeObserver = new ResizeObserver(() => { this.resize() }) - resizeObserver.observe(this._videoElement) - - // Store for cleanup - ;(this._canvas as any)._resizeObserver = resizeObserver + this._resizeObserver.observe(this._videoElement) } private _startRenderLoop() { diff --git a/seanime-web/src/app/(main)/_features/video-core/video-core-subtitles.ts b/seanime-web/src/app/(main)/_features/video-core/video-core-subtitles.ts index a0f95734..bfaf458f 100644 --- a/seanime-web/src/app/(main)/_features/video-core/video-core-subtitles.ts +++ b/seanime-web/src/app/(main)/_features/video-core/video-core-subtitles.ts @@ -398,7 +398,7 @@ Style: Default, Roboto Medium,24,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0 } } - if (!this.pgsRenderer) { + if (!this.pgsRenderer && this.playbackInfo.mkvMetadata?.tracks?.some(t => isPGS(t.codecID))) { this.pgsRenderer = new VideoCorePgsRenderer({ videoElement: this.videoElement, // debug: process.env.NODE_ENV === "development",