node build fixed

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

View File

@@ -0,0 +1,491 @@
package extension_playground
import (
"bytes"
"context"
"fmt"
"runtime"
"seanime/internal/api/anilist"
"seanime/internal/api/metadata"
"seanime/internal/extension"
hibikemanga "seanime/internal/extension/hibike/manga"
hibikeonlinestream "seanime/internal/extension/hibike/onlinestream"
hibiketorrent "seanime/internal/extension/hibike/torrent"
"seanime/internal/extension_repo"
goja_runtime "seanime/internal/goja/goja_runtime"
"seanime/internal/manga"
"seanime/internal/onlinestream"
"seanime/internal/platforms/platform"
"seanime/internal/util"
"seanime/internal/util/result"
"strconv"
"strings"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/goccy/go-json"
"github.com/rs/zerolog"
)
type (
PlaygroundRepository struct {
logger *zerolog.Logger
platform platform.Platform
baseAnimeCache *result.Cache[int, *anilist.BaseAnime]
baseMangaCache *result.Cache[int, *anilist.BaseManga]
metadataProvider metadata.Provider
gojaRuntimeManager *goja_runtime.Manager
}
RunPlaygroundCodeResponse struct {
Logs string `json:"logs"`
Value string `json:"value"`
}
RunPlaygroundCodeParams struct {
Type extension.Type `json:"type"`
Language extension.Language `json:"language"`
Code string `json:"code"`
Inputs map[string]interface{} `json:"inputs"`
Function string `json:"function"`
}
)
func NewPlaygroundRepository(logger *zerolog.Logger, platform platform.Platform, metadataProvider metadata.Provider) *PlaygroundRepository {
return &PlaygroundRepository{
logger: logger,
platform: platform,
metadataProvider: metadataProvider,
baseAnimeCache: result.NewCache[int, *anilist.BaseAnime](),
baseMangaCache: result.NewCache[int, *anilist.BaseManga](),
gojaRuntimeManager: goja_runtime.NewManager(logger),
}
}
func (r *PlaygroundRepository) RunPlaygroundCode(params *RunPlaygroundCodeParams) (resp *RunPlaygroundCodeResponse, err error) {
defer util.HandlePanicInModuleWithError("extension_playground/RunPlaygroundCode", &err)
if params == nil {
return nil, fmt.Errorf("no parameters provided")
}
ext := &extension.Extension{
ID: "playground-extension",
Name: "Playground",
Version: "0.0.0",
ManifestURI: "",
Language: params.Language,
Type: params.Type,
Description: "",
Author: "",
Icon: "",
Website: "",
Payload: params.Code,
}
r.logger.Debug().Msgf("playground: Inputs: %s", strings.ReplaceAll(spew.Sprint(params.Inputs), "\n", ""))
switch params.Type {
case extension.TypeMangaProvider:
return r.runPlaygroundCodeMangaProvider(ext, params)
case extension.TypeOnlinestreamProvider:
return r.runPlaygroundCodeOnlinestreamProvider(ext, params)
case extension.TypeAnimeTorrentProvider:
return r.runPlaygroundCodeAnimeTorrentProvider(ext, params)
default:
}
runtime.GC()
return nil, fmt.Errorf("invalid extension type")
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
type PlaygroundDebugLogger struct {
logger *zerolog.Logger
buff *bytes.Buffer
}
func (r *PlaygroundRepository) newPlaygroundDebugLogger() *PlaygroundDebugLogger {
buff := bytes.NewBuffer(nil)
consoleWritier := zerolog.ConsoleWriter{
Out: buff,
TimeFormat: time.DateTime,
FormatMessage: util.ZerologFormatMessageSimple,
FormatLevel: util.ZerologFormatLevelSimple,
NoColor: true, // Needed to prevent color codes from being written to the file
}
logger := zerolog.New(consoleWritier).With().Timestamp().Logger()
return &PlaygroundDebugLogger{
logger: &logger,
buff: buff,
}
}
func newPlaygroundResponse(playgroundLogger *PlaygroundDebugLogger, value interface{}) *RunPlaygroundCodeResponse {
v := ""
switch value.(type) {
case error:
v = fmt.Sprintf("ERROR: %+v", value)
case string:
v = value.(string)
default:
// Pretty print the value to json
prettyJSON, err := json.MarshalIndent(value, "", " ")
if err != nil {
v = fmt.Sprintf("ERROR: Failed to marshal value to JSON: %+v", err)
} else {
v = string(prettyJSON)
}
}
logs := playgroundLogger.buff.String()
return &RunPlaygroundCodeResponse{
Logs: logs,
Value: v,
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (r *PlaygroundRepository) getAnime(mediaId int) (anime *anilist.BaseAnime, am *metadata.AnimeMetadata, err error) {
var ok bool
anime, ok = r.baseAnimeCache.Get(mediaId)
if !ok {
anime, err = r.platform.GetAnime(context.Background(), mediaId)
if err != nil {
return nil, nil, err
}
r.baseAnimeCache.SetT(mediaId, anime, 24*time.Hour)
}
am, _ = r.metadataProvider.GetAnimeMetadata(metadata.AnilistPlatform, mediaId)
return anime, am, nil
}
func (r *PlaygroundRepository) getManga(mediaId int) (manga *anilist.BaseManga, err error) {
var ok bool
manga, ok = r.baseMangaCache.Get(mediaId)
if !ok {
manga, err = r.platform.GetManga(context.Background(), mediaId)
if err != nil {
return nil, err
}
r.baseMangaCache.SetT(mediaId, manga, 24*time.Hour)
}
return
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (r *PlaygroundRepository) runPlaygroundCodeAnimeTorrentProvider(ext *extension.Extension, params *RunPlaygroundCodeParams) (resp *RunPlaygroundCodeResponse, err error) {
playgroundLogger := r.newPlaygroundDebugLogger()
// Inputs
// - mediaId int
// - options struct
mediaId, ok := params.Inputs["mediaId"].(float64)
if !ok || mediaId <= 0 {
return nil, fmt.Errorf("invalid mediaId")
}
// Fetch the anime
anime, animeMetadata, err := r.getAnime(int(mediaId))
if err != nil {
return nil, err
}
queryMedia := hibiketorrent.Media{
ID: anime.GetID(),
IDMal: anime.GetIDMal(),
Status: string(*anime.GetStatus()),
Format: string(*anime.GetFormat()),
EnglishTitle: anime.GetTitle().GetEnglish(),
RomajiTitle: anime.GetRomajiTitleSafe(),
EpisodeCount: anime.GetTotalEpisodeCount(),
AbsoluteSeasonOffset: 0,
Synonyms: anime.GetSynonymsContainingSeason(),
IsAdult: *anime.GetIsAdult(),
StartDate: &hibiketorrent.FuzzyDate{
Year: *anime.GetStartDate().GetYear(),
Month: anime.GetStartDate().GetMonth(),
Day: anime.GetStartDate().GetDay(),
},
}
switch params.Language {
case extension.LanguageGo:
//...
case extension.LanguageJavascript, extension.LanguageTypescript:
_, provider, err := extension_repo.NewGojaAnimeTorrentProvider(ext, params.Language, playgroundLogger.logger, r.gojaRuntimeManager)
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
defer r.gojaRuntimeManager.DeletePluginPool(ext.ID)
// Run the code
switch params.Function {
case "search":
res, err := provider.Search(hibiketorrent.AnimeSearchOptions{
Media: queryMedia,
Query: params.Inputs["query"].(string),
})
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
case "smartSearch":
type p struct {
Query string `json:"query"`
Batch bool `json:"batch"`
EpisodeNumber int `json:"episodeNumber"`
Resolution string `json:"resolution"`
BestReleases bool `json:"bestReleases"`
}
m, _ := json.Marshal(params.Inputs["options"])
var options p
_ = json.Unmarshal(m, &options)
anidbAID := 0
anidbEID := 0
// Get the AniDB Anime ID and Episode ID
if animeMetadata != nil {
// Override absolute offset value of queryMedia
queryMedia.AbsoluteSeasonOffset = animeMetadata.GetOffset()
if animeMetadata.GetMappings() != nil {
anidbAID = animeMetadata.GetMappings().AnidbId
// Find Animap Episode based on inputted episode number
anizipEpisode, found := animeMetadata.FindEpisode(strconv.Itoa(options.EpisodeNumber))
if found {
anidbEID = anizipEpisode.AnidbEid
}
}
}
res, err := provider.SmartSearch(hibiketorrent.AnimeSmartSearchOptions{
Media: queryMedia,
Query: options.Query,
Batch: options.Batch,
EpisodeNumber: options.EpisodeNumber,
Resolution: options.Resolution,
BestReleases: options.BestReleases,
AnidbAID: anidbAID,
AnidbEID: anidbEID,
})
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
case "getTorrentInfoHash":
var torrent hibiketorrent.AnimeTorrent
_ = json.Unmarshal([]byte(params.Inputs["torrent"].(string)), &torrent)
res, err := provider.GetTorrentInfoHash(&torrent)
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
case "getTorrentMagnetLink":
var torrent hibiketorrent.AnimeTorrent
_ = json.Unmarshal([]byte(params.Inputs["torrent"].(string)), &torrent)
res, err := provider.GetTorrentMagnetLink(&torrent)
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
case "getLatest":
res, err := provider.GetLatest()
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
case "getSettings":
res := provider.GetSettings()
return newPlaygroundResponse(playgroundLogger, res), nil
}
}
return nil, fmt.Errorf("unknown call")
}
func (r *PlaygroundRepository) runPlaygroundCodeMangaProvider(ext *extension.Extension, params *RunPlaygroundCodeParams) (resp *RunPlaygroundCodeResponse, err error) {
playgroundLogger := r.newPlaygroundDebugLogger()
mediaId, ok := params.Inputs["mediaId"].(float64)
if !ok || mediaId <= 0 {
return nil, fmt.Errorf("invalid mediaId")
}
media, err := r.getManga(int(mediaId))
if err != nil {
return nil, err
}
titles := media.GetAllTitles()
switch params.Language {
case extension.LanguageGo:
//...
case extension.LanguageJavascript, extension.LanguageTypescript:
_, provider, err := extension_repo.NewGojaMangaProvider(ext, params.Language, playgroundLogger.logger, r.gojaRuntimeManager)
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
defer r.gojaRuntimeManager.DeletePluginPool(ext.ID)
// Run the code
switch params.Function {
case "search":
// Search
y := 0
if media.GetStartDate().GetYear() != nil {
y = *media.GetStartDate().GetYear()
}
ret := make([]*hibikemanga.SearchResult, 0)
for _, title := range titles {
res, err := provider.Search(hibikemanga.SearchOptions{
Query: *title,
Year: y,
})
if err != nil {
playgroundLogger.logger.Error().Err(err).Msgf("playground: Search failed for title \"%s\"", *title)
}
manga.HydrateSearchResultSearchRating(res, title)
ret = append(ret, res...)
}
var selected *hibikemanga.SearchResult
if len(ret) > 0 {
selected = manga.GetBestSearchResult(ret)
}
return newPlaygroundResponse(playgroundLogger, selected), nil
case "findChapters":
res, err := provider.FindChapters(params.Inputs["id"].(string))
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
case "findChapterPages":
res, err := provider.FindChapterPages(params.Inputs["id"].(string))
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
}
}
return nil, fmt.Errorf("unknown call")
}
func (r *PlaygroundRepository) runPlaygroundCodeOnlinestreamProvider(ext *extension.Extension, params *RunPlaygroundCodeParams) (resp *RunPlaygroundCodeResponse, err error) {
playgroundLogger := r.newPlaygroundDebugLogger()
mediaId, ok := params.Inputs["mediaId"].(float64)
if !ok || mediaId <= 0 {
return nil, fmt.Errorf("invalid mediaId")
}
// Fetch the anime
anime, _, err := r.getAnime(int(mediaId))
if err != nil {
return nil, err
}
titles := anime.GetAllTitles()
queryMedia := hibikeonlinestream.Media{
ID: anime.GetID(),
IDMal: anime.GetIDMal(),
Status: string(*anime.GetStatus()),
Format: string(*anime.GetFormat()),
EnglishTitle: anime.GetTitle().GetEnglish(),
RomajiTitle: anime.GetRomajiTitleSafe(),
EpisodeCount: anime.GetTotalEpisodeCount(),
Synonyms: anime.GetSynonymsContainingSeason(),
IsAdult: *anime.GetIsAdult(),
StartDate: &hibikeonlinestream.FuzzyDate{
Year: *anime.GetStartDate().GetYear(),
Month: anime.GetStartDate().GetMonth(),
Day: anime.GetStartDate().GetDay(),
},
}
switch params.Language {
case extension.LanguageGo:
//...
case extension.LanguageJavascript, extension.LanguageTypescript:
_, provider, err := extension_repo.NewGojaOnlinestreamProvider(ext, params.Language, playgroundLogger.logger, r.gojaRuntimeManager)
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
defer r.gojaRuntimeManager.DeletePluginPool(ext.ID)
// Run the code
switch params.Function {
case "search":
// Search - params: dub: boolean
ret := make([]*hibikeonlinestream.SearchResult, 0)
for _, title := range titles {
res, err := provider.Search(hibikeonlinestream.SearchOptions{
Media: queryMedia,
Query: *title,
Dub: params.Inputs["dub"].(bool),
Year: anime.GetStartYearSafe(),
})
if err != nil {
playgroundLogger.logger.Error().Err(err).Msgf("playground: Search failed for title \"%s\"", *title)
}
ret = append(ret, res...)
}
if len(ret) == 0 {
return newPlaygroundResponse(playgroundLogger, onlinestream.ErrNoAnimeFound), nil
}
bestRes, found := onlinestream.GetBestSearchResult(ret, titles)
if !found {
return newPlaygroundResponse(playgroundLogger, onlinestream.ErrNoAnimeFound), nil
}
return newPlaygroundResponse(playgroundLogger, bestRes), nil
case "findEpisodes":
// FindEpisodes - params: id: string
res, err := provider.FindEpisodes(params.Inputs["id"].(string))
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
case "findEpisodeServer":
// FindEpisodeServer - params: episode: EpisodeDetails, server: string
var episode hibikeonlinestream.EpisodeDetails
_ = json.Unmarshal([]byte(params.Inputs["episode"].(string)), &episode)
res, err := provider.FindEpisodeServer(&episode, params.Inputs["server"].(string))
if err != nil {
return newPlaygroundResponse(playgroundLogger, err), nil
}
return newPlaygroundResponse(playgroundLogger, res), nil
}
}
return nil, fmt.Errorf("unknown call")
}