node build fixed
This commit is contained in:
343
seanime-2.9.10/internal/extension_repo/repository.go
Normal file
343
seanime-2.9.10/internal/extension_repo/repository.go
Normal file
@@ -0,0 +1,343 @@
|
||||
package extension_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"seanime/internal/events"
|
||||
"seanime/internal/extension"
|
||||
hibikemanga "seanime/internal/extension/hibike/manga"
|
||||
hibiketorrent "seanime/internal/extension/hibike/torrent"
|
||||
"seanime/internal/goja/goja_runtime"
|
||||
"seanime/internal/hook"
|
||||
"seanime/internal/util"
|
||||
"seanime/internal/util/filecache"
|
||||
"seanime/internal/util/result"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type (
|
||||
// Repository manages all extensions
|
||||
Repository struct {
|
||||
logger *zerolog.Logger
|
||||
fileCacher *filecache.Cacher
|
||||
wsEventManager events.WSEventManagerInterface
|
||||
// Absolute path to the directory containing all extensions
|
||||
extensionDir string
|
||||
// Store all active Goja VMs
|
||||
// - When reloading extensions, all VMs are interrupted
|
||||
gojaExtensions *result.Map[string, GojaExtension]
|
||||
|
||||
gojaRuntimeManager *goja_runtime.Manager
|
||||
// Extension bank
|
||||
// - When reloading extensions, external extensions are removed & re-added
|
||||
extensionBank *extension.UnifiedBank
|
||||
|
||||
invalidExtensions *result.Map[string, *extension.InvalidExtension]
|
||||
|
||||
hookManager hook.Manager
|
||||
|
||||
client *http.Client
|
||||
|
||||
// Cache the of all built-in extensions when they're first loaded
|
||||
// This is used to quickly determine if an extension is built-in or not and to reload them
|
||||
builtinExtensions *result.Map[string, *builtinExtension]
|
||||
|
||||
updateData []UpdateData
|
||||
updateDataMu sync.Mutex
|
||||
|
||||
// Called when the external extensions are loaded for the first time
|
||||
firstExternalExtensionLoadedFunc context.CancelFunc
|
||||
}
|
||||
|
||||
builtinExtension struct {
|
||||
extension.Extension
|
||||
provider interface{}
|
||||
}
|
||||
|
||||
AllExtensions struct {
|
||||
Extensions []*extension.Extension `json:"extensions"`
|
||||
InvalidExtensions []*extension.InvalidExtension `json:"invalidExtensions"`
|
||||
// List of extensions with invalid user config extensions, these extensions are still loaded
|
||||
InvalidUserConfigExtensions []*extension.InvalidExtension `json:"invalidUserConfigExtensions"`
|
||||
// List of extension IDs that have an update available
|
||||
// This is only populated when the user clicks on "Check for updates"
|
||||
HasUpdate []UpdateData `json:"hasUpdate"`
|
||||
}
|
||||
|
||||
UpdateData struct {
|
||||
ExtensionID string `json:"extensionID"`
|
||||
ManifestURI string `json:"manifestURI"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
MangaProviderExtensionItem struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Lang string `json:"lang"` // ISO 639-1 language code
|
||||
Settings hibikemanga.Settings `json:"settings"`
|
||||
}
|
||||
|
||||
OnlinestreamProviderExtensionItem struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Lang string `json:"lang"` // ISO 639-1 language code
|
||||
EpisodeServers []string `json:"episodeServers"`
|
||||
SupportsDub bool `json:"supportsDub"`
|
||||
}
|
||||
|
||||
AnimeTorrentProviderExtensionItem struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Lang string `json:"lang"` // ISO 639-1 language code
|
||||
Settings hibiketorrent.AnimeProviderSettings `json:"settings"`
|
||||
}
|
||||
)
|
||||
|
||||
type NewRepositoryOptions struct {
|
||||
Logger *zerolog.Logger
|
||||
ExtensionDir string
|
||||
WSEventManager events.WSEventManagerInterface
|
||||
FileCacher *filecache.Cacher
|
||||
HookManager hook.Manager
|
||||
}
|
||||
|
||||
func NewRepository(opts *NewRepositoryOptions) *Repository {
|
||||
|
||||
// Make sure the extension directory exists
|
||||
_ = os.MkdirAll(opts.ExtensionDir, os.ModePerm)
|
||||
|
||||
ret := &Repository{
|
||||
logger: opts.Logger,
|
||||
extensionDir: opts.ExtensionDir,
|
||||
wsEventManager: opts.WSEventManager,
|
||||
gojaExtensions: result.NewResultMap[string, GojaExtension](),
|
||||
gojaRuntimeManager: goja_runtime.NewManager(opts.Logger),
|
||||
extensionBank: extension.NewUnifiedBank(),
|
||||
invalidExtensions: result.NewResultMap[string, *extension.InvalidExtension](),
|
||||
fileCacher: opts.FileCacher,
|
||||
hookManager: opts.HookManager,
|
||||
client: http.DefaultClient,
|
||||
builtinExtensions: result.NewResultMap[string, *builtinExtension](),
|
||||
updateData: make([]UpdateData, 0),
|
||||
}
|
||||
|
||||
firstExtensionLoadedCtx, firstExtensionLoadedCancel := context.WithCancel(context.Background())
|
||||
ret.firstExternalExtensionLoadedFunc = firstExtensionLoadedCancel
|
||||
|
||||
// Fetch extension updates at launch and every 12 hours
|
||||
go func(firstExtensionLoadedCtx context.Context) {
|
||||
defer util.HandlePanicInModuleThen("extension_repo/fetchExtensionUpdates", func() {
|
||||
ret.firstExternalExtensionLoadedFunc = nil
|
||||
})
|
||||
for {
|
||||
if ret.firstExternalExtensionLoadedFunc != nil {
|
||||
// Block until the first external extensions are loaded
|
||||
select {
|
||||
case <-firstExtensionLoadedCtx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
ret.firstExternalExtensionLoadedFunc = nil
|
||||
|
||||
ret.updateData = ret.checkForUpdates()
|
||||
if len(ret.updateData) > 0 {
|
||||
// Signal the frontend that there are updates available
|
||||
ret.wsEventManager.SendEvent(events.ExtensionUpdatesFound, ret.updateData)
|
||||
}
|
||||
time.Sleep(12 * time.Hour)
|
||||
}
|
||||
}(firstExtensionLoadedCtx)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Repository) GetAllExtensions(withUpdates bool) (ret *AllExtensions) {
|
||||
invalidExtensions := r.ListInvalidExtensions()
|
||||
|
||||
fatalInvalidExtensions := lo.Filter(invalidExtensions, func(ext *extension.InvalidExtension, _ int) bool {
|
||||
return ext.Code != extension.InvalidExtensionUserConfigError
|
||||
})
|
||||
|
||||
userConfigInvalidExtensions := lo.Filter(invalidExtensions, func(ext *extension.InvalidExtension, _ int) bool {
|
||||
return ext.Code == extension.InvalidExtensionUserConfigError
|
||||
})
|
||||
|
||||
ret = &AllExtensions{
|
||||
Extensions: r.ListExtensionData(),
|
||||
InvalidExtensions: fatalInvalidExtensions,
|
||||
InvalidUserConfigExtensions: userConfigInvalidExtensions,
|
||||
}
|
||||
|
||||
// Send the update data to the frontend if there are any updates
|
||||
if len(r.updateData) > 0 {
|
||||
ret.HasUpdate = r.updateData
|
||||
}
|
||||
|
||||
if withUpdates {
|
||||
ret.HasUpdate = r.checkForUpdates()
|
||||
r.updateData = ret.HasUpdate
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Repository) GetUpdateData() (ret []UpdateData) {
|
||||
return r.updateData
|
||||
}
|
||||
|
||||
func (r *Repository) ListExtensionData() (ret []*extension.Extension) {
|
||||
r.extensionBank.Range(func(key string, ext extension.BaseExtension) bool {
|
||||
retExt := extension.ToExtensionData(ext)
|
||||
retExt.Payload = ""
|
||||
ret = append(ret, retExt)
|
||||
return true
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Repository) ListDevelopmentModeExtensions() (ret []*extension.Extension) {
|
||||
r.extensionBank.Range(func(key string, ext extension.BaseExtension) bool {
|
||||
if ext.GetIsDevelopment() {
|
||||
retExt := extension.ToExtensionData(ext)
|
||||
retExt.Payload = ""
|
||||
ret = append(ret, retExt)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Repository) ListInvalidExtensions() (ret []*extension.InvalidExtension) {
|
||||
r.invalidExtensions.Range(func(key string, ext *extension.InvalidExtension) bool {
|
||||
ext.Extension.Payload = ""
|
||||
ret = append(ret, ext)
|
||||
return true
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Repository) GetExtensionPayload(id string) (ret string) {
|
||||
ext, found := r.extensionBank.Get(id)
|
||||
if !found {
|
||||
return ""
|
||||
}
|
||||
return ext.GetPayload()
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Lists
|
||||
// - Lists are used to display available options to the user based on the extensions installed
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (r *Repository) ListMangaProviderExtensions() []*MangaProviderExtensionItem {
|
||||
ret := make([]*MangaProviderExtensionItem, 0)
|
||||
|
||||
extension.RangeExtensions(r.extensionBank, func(key string, ext extension.MangaProviderExtension) bool {
|
||||
settings := ext.GetProvider().GetSettings()
|
||||
ret = append(ret, &MangaProviderExtensionItem{
|
||||
ID: ext.GetID(),
|
||||
Name: ext.GetName(),
|
||||
Lang: extension.GetExtensionLang(ext.GetLang()),
|
||||
Settings: settings,
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Repository) ListOnlinestreamProviderExtensions() []*OnlinestreamProviderExtensionItem {
|
||||
ret := make([]*OnlinestreamProviderExtensionItem, 0)
|
||||
|
||||
extension.RangeExtensions(r.extensionBank, func(key string, ext extension.OnlinestreamProviderExtension) bool {
|
||||
settings := ext.GetProvider().GetSettings()
|
||||
ret = append(ret, &OnlinestreamProviderExtensionItem{
|
||||
ID: ext.GetID(),
|
||||
Name: ext.GetName(),
|
||||
Lang: extension.GetExtensionLang(ext.GetLang()),
|
||||
EpisodeServers: settings.EpisodeServers,
|
||||
SupportsDub: settings.SupportsDub,
|
||||
})
|
||||
return true
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (r *Repository) ListAnimeTorrentProviderExtensions() []*AnimeTorrentProviderExtensionItem {
|
||||
ret := make([]*AnimeTorrentProviderExtensionItem, 0)
|
||||
|
||||
extension.RangeExtensions(r.extensionBank, func(key string, ext extension.AnimeTorrentProviderExtension) bool {
|
||||
settings := ext.GetProvider().GetSettings()
|
||||
ret = append(ret, &AnimeTorrentProviderExtensionItem{
|
||||
ID: ext.GetID(),
|
||||
Name: ext.GetName(),
|
||||
Lang: extension.GetExtensionLang(ext.GetLang()),
|
||||
Settings: hibiketorrent.AnimeProviderSettings{
|
||||
Type: settings.Type,
|
||||
CanSmartSearch: settings.CanSmartSearch,
|
||||
SupportsAdult: settings.SupportsAdult,
|
||||
SmartSearchFilters: lo.Map(settings.SmartSearchFilters, func(value hibiketorrent.AnimeProviderSmartSearchFilter, _ int) hibiketorrent.AnimeProviderSmartSearchFilter {
|
||||
return value
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// GetLoadedExtension returns the loaded extension by ID.
|
||||
// It returns an extension.BaseExtension interface, so it can be used to get the extension's details.
|
||||
func (r *Repository) GetLoadedExtension(id string) (extension.BaseExtension, bool) {
|
||||
var ext extension.BaseExtension
|
||||
ext, found := r.extensionBank.Get(id)
|
||||
if found {
|
||||
return ext, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *Repository) GetExtensionBank() *extension.UnifiedBank {
|
||||
return r.extensionBank
|
||||
}
|
||||
|
||||
func (r *Repository) GetMangaProviderExtensionByID(id string) (extension.MangaProviderExtension, bool) {
|
||||
ext, found := extension.GetExtension[extension.MangaProviderExtension](r.extensionBank, id)
|
||||
return ext, found
|
||||
}
|
||||
|
||||
func (r *Repository) GetOnlinestreamProviderExtensionByID(id string) (extension.OnlinestreamProviderExtension, bool) {
|
||||
ext, found := extension.GetExtension[extension.OnlinestreamProviderExtension](r.extensionBank, id)
|
||||
return ext, found
|
||||
}
|
||||
|
||||
func (r *Repository) GetAnimeTorrentProviderExtensionByID(id string) (extension.AnimeTorrentProviderExtension, bool) {
|
||||
ext, found := extension.GetExtension[extension.AnimeTorrentProviderExtension](r.extensionBank, id)
|
||||
return ext, found
|
||||
}
|
||||
|
||||
func (r *Repository) loadPlugin(ext *extension.Extension) (err error) {
|
||||
defer util.HandlePanicInModuleWithError("extension_repo/loadPlugin", &err)
|
||||
|
||||
err = r.loadPluginExtension(ext)
|
||||
if err != nil {
|
||||
r.logger.Error().Err(err).Str("id", ext.ID).Msg("extensions: Failed to load plugin")
|
||||
return err
|
||||
}
|
||||
|
||||
r.logger.Debug().Str("id", ext.ID).Msg("extensions: Loaded plugin")
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user