fix: harden gomobile extension bindings and m4a cover retention

This commit is contained in:
zarzet 2026-04-04 21:30:11 +07:00
parent f0013fac16
commit 4c4553913f
19 changed files with 261 additions and 251 deletions

View file

@ -2302,7 +2302,7 @@ func ReEnrichFile(requestJSON string) (string, error) {
deezerClient := GetDeezerClient()
GoLog("[ReEnrich] Trying metadata providers in configured priority...\n")
manager := GetExtensionManager()
manager := getExtensionManager()
if identifierTrack, err := resolveReEnrichTrackFromIdentifiers(req); err == nil && identifierTrack != nil {
GoLog("[ReEnrich] Identifier-first metadata match (%s): %s - %s (album: %s, date: %s)\n",
identifierTrack.ProviderID, identifierTrack.Name, identifierTrack.Artists, identifierTrack.AlbumName, identifierTrack.ReleaseDate)
@ -2542,7 +2542,7 @@ func ReEnrichFile(requestJSON string) (string, error) {
}
func InitExtensionSystem(extensionsDir, dataDir string) error {
manager := GetExtensionManager()
manager := getExtensionManager()
if err := manager.SetDirectories(extensionsDir, dataDir); err != nil {
return err
}
@ -2556,7 +2556,7 @@ func InitExtensionSystem(extensionsDir, dataDir string) error {
}
func LoadExtensionsFromDir(dirPath string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
loaded, errors := manager.LoadExtensionsFromDirectory(dirPath)
result := map[string]interface{}{
@ -2577,7 +2577,7 @@ func LoadExtensionsFromDir(dirPath string) (string, error) {
}
func LoadExtensionFromPath(filePath string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.LoadExtensionFromFile(filePath)
if err != nil {
return "", err
@ -2600,17 +2600,17 @@ func LoadExtensionFromPath(filePath string) (string, error) {
}
func UnloadExtensionByID(extensionID string) error {
manager := GetExtensionManager()
manager := getExtensionManager()
return manager.UnloadExtension(extensionID)
}
func RemoveExtensionByID(extensionID string) error {
manager := GetExtensionManager()
manager := getExtensionManager()
return manager.RemoveExtension(extensionID)
}
func UpgradeExtensionFromPath(filePath string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.UpgradeExtension(filePath)
if err != nil {
return "", err
@ -2632,17 +2632,17 @@ func UpgradeExtensionFromPath(filePath string) (string, error) {
}
func CheckExtensionUpgradeFromPath(filePath string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
return manager.CheckExtensionUpgradeJSON(filePath)
}
func GetInstalledExtensions() (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
return manager.GetInstalledExtensionsJSON()
}
func SetExtensionEnabledByID(extensionID string, enabled bool) error {
manager := GetExtensionManager()
manager := getExtensionManager()
return manager.SetExtensionEnabled(extensionID, enabled)
}
@ -2707,12 +2707,12 @@ func SetExtensionSettingsJSON(extensionID, settingsJSON string) error {
return err
}
manager := GetExtensionManager()
manager := getExtensionManager()
return manager.InitializeExtension(extensionID, settings)
}
func SearchTracksWithExtensionsJSON(query string, limit int) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
tracks, err := manager.SearchTracksWithExtensions(query, limit)
if err != nil {
return "", err
@ -2727,7 +2727,7 @@ func SearchTracksWithExtensionsJSON(query string, limit int) (string, error) {
}
func SearchTracksWithMetadataProvidersJSON(query string, limit int, includeExtensions bool) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
tracks, err := manager.SearchTracksWithMetadataProviders(query, limit, includeExtensions)
if err != nil {
return "", err
@ -2774,12 +2774,12 @@ func DownloadWithExtensionsJSON(requestJSON string) (string, error) {
}
func CleanupExtensions() {
manager := GetExtensionManager()
manager := getExtensionManager()
manager.UnloadAllExtensions()
}
func InvokeExtensionActionJSON(extensionID, actionName string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
result, err := manager.InvokeAction(extensionID, actionName)
if err != nil {
return "", err
@ -2916,7 +2916,7 @@ func GetAllPendingFFmpegCommandsJSON() (string, error) {
}
func EnrichTrackWithExtensionJSON(extensionID, trackJSON string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.GetExtension(extensionID)
if err != nil {
return trackJSON, nil
@ -2931,7 +2931,7 @@ func EnrichTrackWithExtensionJSON(extensionID, trackJSON string) (string, error)
return trackJSON, fmt.Errorf("failed to parse track: %w", err)
}
provider := NewExtensionProviderWrapper(ext)
provider := newExtensionProviderWrapper(ext)
enrichedTrack, err := provider.EnrichTrack(&track)
if err != nil {
return trackJSON, nil
@ -2946,7 +2946,7 @@ func EnrichTrackWithExtensionJSON(extensionID, trackJSON string) (string, error)
}
func CustomSearchWithExtensionJSON(extensionID, query string, optionsJSON string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.GetExtension(extensionID)
if err != nil {
return "", err
@ -2963,7 +2963,7 @@ func CustomSearchWithExtensionJSON(extensionID, query string, optionsJSON string
}
}
provider := NewExtensionProviderWrapper(ext)
provider := newExtensionProviderWrapper(ext)
tracks, err := provider.CustomSearch(query, options)
if err != nil {
return "", err
@ -3001,7 +3001,7 @@ func CustomSearchWithExtensionJSON(extensionID, query string, optionsJSON string
}
func GetSearchProvidersJSON() (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
providers := manager.GetSearchProviders()
result := make([]map[string]interface{}, 0, len(providers))
@ -3024,7 +3024,7 @@ func GetSearchProvidersJSON() (string, error) {
}
func HandleURLWithExtensionJSON(url string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
resultWithID, err := manager.HandleURLWithExtension(url)
if err != nil {
return "", err
@ -3194,7 +3194,7 @@ func HandleURLWithExtensionJSON(url string) (string, error) {
}
func FindURLHandlerJSON(url string) string {
manager := GetExtensionManager()
manager := getExtensionManager()
handler := manager.FindURLHandler(url)
if handler == nil {
return ""
@ -3203,7 +3203,7 @@ func FindURLHandlerJSON(url string) string {
}
func GetAlbumWithExtensionJSON(extensionID, albumID string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.GetExtension(extensionID)
if err != nil {
return "", err
@ -3216,7 +3216,7 @@ func GetAlbumWithExtensionJSON(extensionID, albumID string) (string, error) {
return "", fmt.Errorf("extension '%s' is disabled", extensionID)
}
provider := NewExtensionProviderWrapper(ext)
provider := newExtensionProviderWrapper(ext)
album, err := provider.GetAlbum(albumID)
if err != nil {
return "", err
@ -3279,7 +3279,7 @@ func GetAlbumWithExtensionJSON(extensionID, albumID string) (string, error) {
}
func GetPlaylistWithExtensionJSON(extensionID, playlistID string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.GetExtension(extensionID)
if err != nil {
return "", err
@ -3380,7 +3380,7 @@ func GetPlaylistWithExtensionJSON(extensionID, playlistID string) (string, error
}
func GetArtistWithExtensionJSON(extensionID, artistID string) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.GetExtension(extensionID)
if err != nil {
return "", err
@ -3390,7 +3390,7 @@ func GetArtistWithExtensionJSON(extensionID, artistID string) (string, error) {
return "", fmt.Errorf("extension '%s' is not a metadata provider", extensionID)
}
provider := NewExtensionProviderWrapper(ext)
provider := newExtensionProviderWrapper(ext)
artist, err := provider.GetArtist(artistID)
if err != nil {
return "", err
@ -3485,7 +3485,7 @@ func GetArtistWithExtensionJSON(extensionID, artistID string) (string, error) {
}
func GetURLHandlersJSON() (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
handlers := manager.GetURLHandlers()
result := make([]map[string]interface{}, 0, len(handlers))
@ -3513,7 +3513,7 @@ func RunPostProcessingJSON(filePath, metadataJSON string) (string, error) {
}
}
manager := GetExtensionManager()
manager := getExtensionManager()
result, err := manager.RunPostProcessing(filePath, metadata)
if err != nil {
return "", err
@ -3542,7 +3542,7 @@ func RunPostProcessingV2JSON(inputJSON, metadataJSON string) (string, error) {
}
}
manager := GetExtensionManager()
manager := getExtensionManager()
result, err := manager.RunPostProcessingV2(input, metadata)
if err != nil {
return "", err
@ -3557,7 +3557,7 @@ func RunPostProcessingV2JSON(inputJSON, metadataJSON string) (string, error) {
}
func GetPostProcessingProvidersJSON() (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
providers := manager.GetPostProcessingProviders()
result := make([]map[string]interface{}, 0, len(providers))
@ -3723,7 +3723,7 @@ func ClearStoreCacheJSON() error {
}
func callExtensionFunctionJSON(extensionID, functionName string, timeout time.Duration) (string, error) {
manager := GetExtensionManager()
manager := getExtensionManager()
ext, err := manager.GetExtension(extensionID)
if err != nil {
return "", err

View file

@ -43,12 +43,12 @@ func compareVersions(v1, v2 string) int {
return 0
}
type LoadedExtension struct {
type loadedExtension struct {
ID string `json:"id"`
Manifest *ExtensionManifest `json:"manifest"`
VM *goja.Runtime `json:"-"`
VMMu sync.Mutex `json:"-"`
runtime *ExtensionRuntime
runtime *extensionRuntime
initialized bool
Enabled bool `json:"enabled"`
Error string `json:"error,omitempty"`
@ -73,7 +73,7 @@ func getExtensionInitSettings(extensionID string) map[string]interface{} {
return filtered
}
func ensureRuntimeReadyLocked(ext *LoadedExtension, applyStoredSettings bool) error {
func ensureRuntimeReadyLocked(ext *loadedExtension, applyStoredSettings bool) error {
if ext.VM == nil || ext.runtime == nil {
if err := initializeVMLocked(ext); err != nil {
ext.Error = err.Error()
@ -100,14 +100,14 @@ func ensureRuntimeReadyLocked(ext *LoadedExtension, applyStoredSettings bool) er
return nil
}
func (ext *LoadedExtension) ensureRuntimeReady() error {
func (ext *loadedExtension) ensureRuntimeReady() error {
ext.VMMu.Lock()
defer ext.VMMu.Unlock()
return ensureRuntimeReadyLocked(ext, true)
}
func (ext *LoadedExtension) lockReadyVM() (*goja.Runtime, error) {
func (ext *loadedExtension) lockReadyVM() (*goja.Runtime, error) {
ext.VMMu.Lock()
if err := ensureRuntimeReadyLocked(ext, true); err != nil {
ext.VMMu.Unlock()
@ -116,28 +116,28 @@ func (ext *LoadedExtension) lockReadyVM() (*goja.Runtime, error) {
return ext.VM, nil
}
type ExtensionManager struct {
type extensionManager struct {
mu sync.RWMutex
extensions map[string]*LoadedExtension
extensions map[string]*loadedExtension
extensionsDir string
dataDir string
}
var (
globalExtManager *ExtensionManager
globalExtManager *extensionManager
globalExtManagerOnce sync.Once
)
func GetExtensionManager() *ExtensionManager {
func getExtensionManager() *extensionManager {
globalExtManagerOnce.Do(func() {
globalExtManager = &ExtensionManager{
extensions: make(map[string]*LoadedExtension),
globalExtManager = &extensionManager{
extensions: make(map[string]*loadedExtension),
}
})
return globalExtManager
}
func (m *ExtensionManager) SetDirectories(extensionsDir, dataDir string) error {
func (m *extensionManager) SetDirectories(extensionsDir, dataDir string) error {
m.mu.Lock()
defer m.mu.Unlock()
@ -154,7 +154,7 @@ func (m *ExtensionManager) SetDirectories(extensionsDir, dataDir string) error {
return nil
}
func (m *ExtensionManager) LoadExtensionFromFile(filePath string) (*LoadedExtension, error) {
func (m *extensionManager) LoadExtensionFromFile(filePath string) (*loadedExtension, error) {
if !strings.HasSuffix(strings.ToLower(filePath), ".spotiflac-ext") {
return nil, fmt.Errorf("Invalid file format. Please select a .spotiflac-ext file")
}
@ -272,7 +272,7 @@ func (m *ExtensionManager) LoadExtensionFromFile(filePath string) (*LoadedExtens
return nil, fmt.Errorf("failed to create extension data directory: %w", err)
}
ext := &LoadedExtension{
ext := &loadedExtension{
ID: manifest.Name,
Manifest: manifest,
Enabled: false, // New extensions start disabled
@ -292,7 +292,7 @@ func (m *ExtensionManager) LoadExtensionFromFile(filePath string) (*LoadedExtens
return ext, nil
}
func initializeVMLocked(ext *LoadedExtension) error {
func initializeVMLocked(ext *loadedExtension) error {
ext.VM = nil
ext.runtime = nil
ext.initialized = false
@ -305,7 +305,7 @@ func initializeVMLocked(ext *LoadedExtension) error {
return fmt.Errorf("failed to read index.js: %w", err)
}
runtime := NewExtensionRuntime(ext)
runtime := newExtensionRuntime(ext)
ext.runtime = runtime
runtime.RegisterAPIs(vm)
runtime.RegisterGoBackendAPIs(vm)
@ -342,14 +342,14 @@ func initializeVMLocked(ext *LoadedExtension) error {
return nil
}
func (m *ExtensionManager) initializeVM(ext *LoadedExtension) error {
func (m *extensionManager) initializeVM(ext *loadedExtension) error {
ext.VMMu.Lock()
defer ext.VMMu.Unlock()
return initializeVMLocked(ext)
}
func initializeExtensionWithSettingsLocked(
ext *LoadedExtension,
ext *loadedExtension,
settings map[string]interface{},
) error {
if ext.VM == nil {
@ -405,7 +405,7 @@ func initializeExtensionWithSettingsLocked(
return nil
}
func runCleanupLocked(ext *LoadedExtension) error {
func runCleanupLocked(ext *loadedExtension) error {
if ext.VM != nil {
script := `
(function() {
@ -446,7 +446,7 @@ func runCleanupLocked(ext *LoadedExtension) error {
return nil
}
func teardownVMLocked(ext *LoadedExtension) {
func teardownVMLocked(ext *loadedExtension) {
if err := runCleanupLocked(ext); err != nil {
GoLog("[Extension] Error calling cleanup for %s: %v\n", ext.ID, err)
}
@ -461,7 +461,7 @@ func teardownVMLocked(ext *LoadedExtension) {
ext.initialized = false
}
func validateExtensionLoad(ext *LoadedExtension) error {
func validateExtensionLoad(ext *loadedExtension) error {
ext.VMMu.Lock()
defer ext.VMMu.Unlock()
@ -472,7 +472,7 @@ func validateExtensionLoad(ext *LoadedExtension) error {
return nil
}
func (m *ExtensionManager) UnloadExtension(extensionID string) error {
func (m *extensionManager) UnloadExtension(extensionID string) error {
m.mu.Lock()
defer m.mu.Unlock()
@ -491,7 +491,7 @@ func (m *ExtensionManager) UnloadExtension(extensionID string) error {
return nil
}
func (m *ExtensionManager) GetExtension(extensionID string) (*LoadedExtension, error) {
func (m *extensionManager) GetExtension(extensionID string) (*loadedExtension, error) {
m.mu.RLock()
defer m.mu.RUnlock()
@ -502,18 +502,18 @@ func (m *ExtensionManager) GetExtension(extensionID string) (*LoadedExtension, e
return ext, nil
}
func (m *ExtensionManager) GetAllExtensions() []*LoadedExtension {
func (m *extensionManager) GetAllExtensions() []*loadedExtension {
m.mu.RLock()
defer m.mu.RUnlock()
result := make([]*LoadedExtension, 0, len(m.extensions))
result := make([]*loadedExtension, 0, len(m.extensions))
for _, ext := range m.extensions {
result = append(result, ext)
}
return result
}
func (m *ExtensionManager) SetExtensionEnabled(extensionID string, enabled bool) error {
func (m *extensionManager) SetExtensionEnabled(extensionID string, enabled bool) error {
m.mu.Lock()
defer m.mu.Unlock()
@ -547,7 +547,7 @@ func (m *ExtensionManager) SetExtensionEnabled(extensionID string, enabled bool)
return nil
}
func (m *ExtensionManager) LoadExtensionsFromDirectory(dirPath string) ([]string, []error) {
func (m *extensionManager) LoadExtensionsFromDirectory(dirPath string) ([]string, []error) {
var loaded []string
var errors []error
@ -585,7 +585,7 @@ func (m *ExtensionManager) LoadExtensionsFromDirectory(dirPath string) ([]string
return loaded, errors
}
func (m *ExtensionManager) loadExtensionFromDirectory(dirPath string) (*LoadedExtension, error) {
func (m *extensionManager) loadExtensionFromDirectory(dirPath string) (*loadedExtension, error) {
m.mu.Lock()
defer m.mu.Unlock()
@ -615,7 +615,7 @@ func (m *ExtensionManager) loadExtensionFromDirectory(dirPath string) (*LoadedEx
return nil, fmt.Errorf("failed to create extension data directory: %w", err)
}
ext := &LoadedExtension{
ext := &loadedExtension{
ID: manifest.Name,
Manifest: manifest,
Enabled: false, // Will be restored from settings store
@ -643,7 +643,7 @@ func (m *ExtensionManager) loadExtensionFromDirectory(dirPath string) (*LoadedEx
return ext, nil
}
func (m *ExtensionManager) RemoveExtension(extensionID string) error {
func (m *extensionManager) RemoveExtension(extensionID string) error {
ext, err := m.GetExtension(extensionID)
if err != nil {
return err
@ -663,7 +663,7 @@ func (m *ExtensionManager) RemoveExtension(extensionID string) error {
}
// Only allows upgrades (new version > current version), not downgrades
func (m *ExtensionManager) UpgradeExtension(filePath string) (*LoadedExtension, error) {
func (m *extensionManager) UpgradeExtension(filePath string) (*loadedExtension, error) {
if !strings.HasSuffix(strings.ToLower(filePath), ".spotiflac-ext") {
return nil, fmt.Errorf("Invalid file format. Please select a .spotiflac-ext file")
}
@ -777,7 +777,7 @@ func (m *ExtensionManager) UpgradeExtension(filePath string) (*LoadedExtension,
}
}
ext := &LoadedExtension{
ext := &loadedExtension{
ID: newManifest.Name,
Manifest: newManifest,
Enabled: wasEnabled, // Preserve enabled state from before upgrade
@ -812,7 +812,7 @@ type ExtensionUpgradeInfo struct {
IsInstalled bool `json:"is_installed"`
}
func (m *ExtensionManager) checkExtensionUpgradeInternal(filePath string) (*ExtensionUpgradeInfo, error) {
func (m *extensionManager) checkExtensionUpgradeInternal(filePath string) (*ExtensionUpgradeInfo, error) {
if !strings.HasSuffix(strings.ToLower(filePath), ".spotiflac-ext") {
return nil, fmt.Errorf("Invalid file format. Please select a .spotiflac-ext file")
}
@ -871,7 +871,7 @@ func (m *ExtensionManager) checkExtensionUpgradeInternal(filePath string) (*Exte
return info, nil
}
func (m *ExtensionManager) CheckExtensionUpgradeJSON(filePath string) (string, error) {
func (m *extensionManager) CheckExtensionUpgradeJSON(filePath string) (string, error) {
info, err := m.checkExtensionUpgradeInternal(filePath)
if err != nil {
return "", err
@ -885,7 +885,7 @@ func (m *ExtensionManager) CheckExtensionUpgradeJSON(filePath string) (string, e
return string(jsonBytes), nil
}
func (m *ExtensionManager) GetInstalledExtensionsJSON() (string, error) {
func (m *extensionManager) GetInstalledExtensionsJSON() (string, error) {
extensions := m.GetAllExtensions()
type ExtensionInfo struct {
@ -982,7 +982,7 @@ func (m *ExtensionManager) GetInstalledExtensionsJSON() (string, error) {
return string(jsonBytes), nil
}
func (m *ExtensionManager) InitializeExtension(extensionID string, settings map[string]interface{}) error {
func (m *extensionManager) InitializeExtension(extensionID string, settings map[string]interface{}) error {
m.mu.Lock()
defer m.mu.Unlock()
@ -1000,7 +1000,7 @@ func (m *ExtensionManager) InitializeExtension(extensionID string, settings map[
return initializeExtensionWithSettingsLocked(ext, settings)
}
func (m *ExtensionManager) CleanupExtension(extensionID string) error {
func (m *extensionManager) CleanupExtension(extensionID string) error {
m.mu.Lock()
defer m.mu.Unlock()
@ -1022,7 +1022,7 @@ func (m *ExtensionManager) CleanupExtension(extensionID string) error {
return nil
}
func (m *ExtensionManager) UnloadAllExtensions() {
func (m *extensionManager) UnloadAllExtensions() {
m.mu.Lock()
extensionIDs := make([]string, 0, len(m.extensions))
for id := range m.extensions {
@ -1037,7 +1037,7 @@ func (m *ExtensionManager) UnloadAllExtensions() {
GoLog("[Extension] All extensions unloaded\n")
}
func (m *ExtensionManager) InvokeAction(extensionID string, actionName string) (map[string]interface{}, error) {
func (m *extensionManager) InvokeAction(extensionID string, actionName string) (map[string]interface{}, error) {
m.mu.Lock()
defer m.mu.Unlock()

View file

@ -116,19 +116,19 @@ type ExtDownloadResult struct {
DecryptionKey string `json:"decryption_key,omitempty"`
}
type ExtensionProviderWrapper struct {
extension *LoadedExtension
type extensionProviderWrapper struct {
extension *loadedExtension
vm *goja.Runtime
}
func NewExtensionProviderWrapper(ext *LoadedExtension) *ExtensionProviderWrapper {
return &ExtensionProviderWrapper{
func newExtensionProviderWrapper(ext *loadedExtension) *extensionProviderWrapper {
return &extensionProviderWrapper{
extension: ext,
vm: ext.VM,
}
}
func (p *ExtensionProviderWrapper) lockReadyVM() error {
func (p *extensionProviderWrapper) lockReadyVM() error {
vm, err := p.extension.lockReadyVM()
if err != nil {
return err
@ -137,7 +137,7 @@ func (p *ExtensionProviderWrapper) lockReadyVM() error {
return nil
}
func (p *ExtensionProviderWrapper) SearchTracks(query string, limit int) (*ExtSearchResult, error) {
func (p *extensionProviderWrapper) SearchTracks(query string, limit int) (*ExtSearchResult, error) {
if !p.extension.Manifest.IsMetadataProvider() {
return nil, fmt.Errorf("extension '%s' is not a metadata provider", p.extension.ID)
}
@ -197,7 +197,7 @@ func (p *ExtensionProviderWrapper) SearchTracks(query string, limit int) (*ExtSe
return &searchResult, nil
}
func (p *ExtensionProviderWrapper) GetTrack(trackID string) (*ExtTrackMetadata, error) {
func (p *extensionProviderWrapper) GetTrack(trackID string) (*ExtTrackMetadata, error) {
if !p.extension.Manifest.IsMetadataProvider() {
return nil, fmt.Errorf("extension '%s' is not a metadata provider", p.extension.ID)
}
@ -246,7 +246,7 @@ func (p *ExtensionProviderWrapper) GetTrack(trackID string) (*ExtTrackMetadata,
return &track, nil
}
func (p *ExtensionProviderWrapper) GetAlbum(albumID string) (*ExtAlbumMetadata, error) {
func (p *extensionProviderWrapper) GetAlbum(albumID string) (*ExtAlbumMetadata, error) {
if !p.extension.Manifest.IsMetadataProvider() {
return nil, fmt.Errorf("extension '%s' is not a metadata provider", p.extension.ID)
}
@ -298,7 +298,7 @@ func (p *ExtensionProviderWrapper) GetAlbum(albumID string) (*ExtAlbumMetadata,
return &album, nil
}
func (p *ExtensionProviderWrapper) GetArtist(artistID string) (*ExtArtistMetadata, error) {
func (p *extensionProviderWrapper) GetArtist(artistID string) (*ExtArtistMetadata, error) {
if !p.extension.Manifest.IsMetadataProvider() {
return nil, fmt.Errorf("extension '%s' is not a metadata provider", p.extension.ID)
}
@ -353,7 +353,7 @@ func (p *ExtensionProviderWrapper) GetArtist(artistID string) (*ExtArtistMetadat
return &artist, nil
}
func (p *ExtensionProviderWrapper) EnrichTrack(track *ExtTrackMetadata) (*ExtTrackMetadata, error) {
func (p *extensionProviderWrapper) EnrichTrack(track *ExtTrackMetadata) (*ExtTrackMetadata, error) {
if !p.extension.Manifest.IsMetadataProvider() {
return track, nil
}
@ -415,7 +415,7 @@ func (p *ExtensionProviderWrapper) EnrichTrack(track *ExtTrackMetadata) (*ExtTra
return &enrichedTrack, nil
}
func (p *ExtensionProviderWrapper) CheckAvailability(isrc, trackName, artistName, spotifyID, deezerID string) (*ExtAvailabilityResult, error) {
func (p *extensionProviderWrapper) CheckAvailability(isrc, trackName, artistName, spotifyID, deezerID string) (*ExtAvailabilityResult, error) {
if !p.extension.Manifest.IsDownloadProvider() {
return nil, fmt.Errorf("extension '%s' is not a download provider", p.extension.ID)
}
@ -463,7 +463,7 @@ func (p *ExtensionProviderWrapper) CheckAvailability(isrc, trackName, artistName
return &availability, nil
}
func (p *ExtensionProviderWrapper) GetDownloadURL(trackID, quality string) (*ExtDownloadURLResult, error) {
func (p *extensionProviderWrapper) GetDownloadURL(trackID, quality string) (*ExtDownloadURLResult, error) {
if !p.extension.Manifest.IsDownloadProvider() {
return nil, fmt.Errorf("extension '%s' is not a download provider", p.extension.ID)
}
@ -513,7 +513,7 @@ func (p *ExtensionProviderWrapper) GetDownloadURL(trackID, quality string) (*Ext
const ExtDownloadTimeout = DownloadTimeout
func (p *ExtensionProviderWrapper) Download(trackID, quality, outputPath, itemID string, onProgress func(percent int)) (*ExtDownloadResult, error) {
func (p *extensionProviderWrapper) Download(trackID, quality, outputPath, itemID string, onProgress func(percent int)) (*ExtDownloadResult, error) {
if !p.extension.Manifest.IsDownloadProvider() {
return nil, fmt.Errorf("extension '%s' is not a download provider", p.extension.ID)
}
@ -604,40 +604,40 @@ func (p *ExtensionProviderWrapper) Download(trackID, quality, outputPath, itemID
return &downloadResult, nil
}
func (m *ExtensionManager) GetMetadataProviders() []*ExtensionProviderWrapper {
func (m *extensionManager) GetMetadataProviders() []*extensionProviderWrapper {
m.mu.RLock()
defer m.mu.RUnlock()
var providers []*ExtensionProviderWrapper
var providers []*extensionProviderWrapper
for _, ext := range m.extensions {
if ext.Enabled && ext.Manifest.IsMetadataProvider() && ext.Error == "" {
providers = append(providers, NewExtensionProviderWrapper(ext))
providers = append(providers, newExtensionProviderWrapper(ext))
}
}
return providers
}
func (m *ExtensionManager) GetDownloadProviders() []*ExtensionProviderWrapper {
func (m *extensionManager) GetDownloadProviders() []*extensionProviderWrapper {
m.mu.RLock()
defer m.mu.RUnlock()
var providers []*ExtensionProviderWrapper
var providers []*extensionProviderWrapper
for _, ext := range m.extensions {
if ext.Enabled && ext.Manifest.IsDownloadProvider() && ext.Error == "" {
providers = append(providers, NewExtensionProviderWrapper(ext))
providers = append(providers, newExtensionProviderWrapper(ext))
}
}
return providers
}
func (m *ExtensionManager) SearchTracksWithExtensions(query string, limit int) ([]ExtTrackMetadata, error) {
func (m *extensionManager) SearchTracksWithExtensions(query string, limit int) ([]ExtTrackMetadata, error) {
providers := m.GetMetadataProviders()
if len(providers) == 0 {
return nil, nil
}
providerByID := make(map[string]*ExtensionProviderWrapper, len(providers))
orderedProviders := make([]*ExtensionProviderWrapper, 0, len(providers))
providerByID := make(map[string]*extensionProviderWrapper, len(providers))
orderedProviders := make([]*extensionProviderWrapper, 0, len(providers))
for _, provider := range providers {
providerByID[provider.extension.ID] = provider
}
@ -830,13 +830,13 @@ func searchBuiltInMetadataTracks(providerID, query string, limit int) ([]ExtTrac
}
}
func (m *ExtensionManager) SearchTracksWithMetadataProviders(query string, limit int, includeExtensions bool) ([]ExtTrackMetadata, error) {
func (m *extensionManager) SearchTracksWithMetadataProviders(query string, limit int, includeExtensions bool) ([]ExtTrackMetadata, error) {
priority := GetMetadataProviderPriority()
if limit <= 0 {
limit = 20
}
extensionProviders := make(map[string]*ExtensionProviderWrapper)
extensionProviders := make(map[string]*extensionProviderWrapper)
if includeExtensions {
for _, provider := range m.GetMetadataProviders() {
extensionProviders[provider.extension.ID] = provider
@ -916,7 +916,7 @@ func (m *ExtensionManager) SearchTracksWithMetadataProviders(query string, limit
func DownloadWithExtensionFallback(req DownloadRequest) (*DownloadResponse, error) {
priority := GetProviderPriority()
extManager := GetExtensionManager()
extManager := getExtensionManager()
strictMode := !req.UseFallback
selectedProvider := strings.TrimSpace(req.Service)
@ -971,7 +971,7 @@ func DownloadWithExtensionFallback(req DownloadRequest) (*DownloadResponse, erro
if err == nil && ext.Enabled && ext.Error == "" && ext.Manifest.IsMetadataProvider() {
GoLog("[DownloadWithExtensionFallback] Enriching track from extension '%s'...\n", req.Source)
provider := NewExtensionProviderWrapper(ext)
provider := newExtensionProviderWrapper(ext)
trackMeta := &ExtTrackMetadata{
ID: req.SpotifyID,
Name: req.TrackName,
@ -1155,7 +1155,7 @@ func DownloadWithExtensionFallback(req DownloadRequest) (*DownloadResponse, erro
if err == nil && ext.Enabled && ext.Error == "" && ext.Manifest.IsDownloadProvider() {
skipBuiltIn = ext.Manifest.SkipBuiltInFallback
provider := NewExtensionProviderWrapper(ext)
provider := newExtensionProviderWrapper(ext)
trackID := req.SpotifyID
@ -1376,7 +1376,7 @@ func DownloadWithExtensionFallback(req DownloadRequest) (*DownloadResponse, erro
continue
}
provider := NewExtensionProviderWrapper(ext)
provider := newExtensionProviderWrapper(ext)
availability, err := provider.CheckAvailability(req.ISRC, req.TrackName, req.ArtistName, req.SpotifyID, req.DeezerID)
if err != nil || !availability.Available {
@ -1657,7 +1657,7 @@ func buildOutputPath(req DownloadRequest) string {
return filepath.Join(outputDir, filename+ext)
}
func buildOutputPathForExtension(req DownloadRequest, ext *LoadedExtension) string {
func buildOutputPathForExtension(req DownloadRequest, ext *loadedExtension) string {
if strings.TrimSpace(req.OutputPath) != "" {
return strings.TrimSpace(req.OutputPath)
}
@ -1703,7 +1703,7 @@ func buildOutputPathForExtension(req DownloadRequest, ext *LoadedExtension) stri
return filepath.Join(tempDir, filename+outputExt)
}
func (p *ExtensionProviderWrapper) CustomSearch(query string, options map[string]interface{}) ([]ExtTrackMetadata, error) {
func (p *extensionProviderWrapper) CustomSearch(query string, options map[string]interface{}) ([]ExtTrackMetadata, error) {
if !p.extension.Manifest.HasCustomSearch() {
return nil, fmt.Errorf("extension '%s' does not support custom search", p.extension.ID)
}
@ -1785,7 +1785,7 @@ type ExtURLHandleResult struct {
CoverURL string `json:"cover_url,omitempty"`
}
func (p *ExtensionProviderWrapper) HandleURL(url string) (*ExtURLHandleResult, error) {
func (p *extensionProviderWrapper) HandleURL(url string) (*ExtURLHandleResult, error) {
if !p.extension.Manifest.HasURLHandler() {
return nil, fmt.Errorf("extension '%s' does not support URL handling", p.extension.ID)
}
@ -1871,7 +1871,7 @@ type MatchTrackResult struct {
Reason string `json:"reason,omitempty"`
}
func (p *ExtensionProviderWrapper) MatchTrack(sourceTrack map[string]interface{}, candidates []map[string]interface{}) (*MatchTrackResult, error) {
func (p *extensionProviderWrapper) MatchTrack(sourceTrack map[string]interface{}, candidates []map[string]interface{}) (*MatchTrackResult, error) {
if !p.extension.Manifest.HasCustomMatching() {
return nil, fmt.Errorf("extension '%s' does not support custom matching", p.extension.ID)
}
@ -1942,7 +1942,7 @@ type PostProcessInput struct {
const PostProcessTimeout = 2 * time.Minute
func (p *ExtensionProviderWrapper) PostProcess(filePath string, metadata map[string]interface{}, hookID string) (*PostProcessResult, error) {
func (p *extensionProviderWrapper) PostProcess(filePath string, metadata map[string]interface{}, hookID string) (*PostProcessResult, error) {
if !p.extension.Manifest.HasPostProcessing() {
return nil, fmt.Errorf("extension '%s' does not support post-processing", p.extension.ID)
}
@ -2005,7 +2005,7 @@ func (p *ExtensionProviderWrapper) PostProcess(filePath string, metadata map[str
return &postResult, nil
}
func (p *ExtensionProviderWrapper) PostProcessV2(input PostProcessInput, metadata map[string]interface{}, hookID string) (*PostProcessResult, error) {
func (p *extensionProviderWrapper) PostProcessV2(input PostProcessInput, metadata map[string]interface{}, hookID string) (*PostProcessResult, error) {
if !p.extension.Manifest.HasPostProcessing() {
return nil, fmt.Errorf("extension '%s' does not support post-processing", p.extension.ID)
}
@ -2075,39 +2075,39 @@ func (p *ExtensionProviderWrapper) PostProcessV2(input PostProcessInput, metadat
return &postResult, nil
}
func (m *ExtensionManager) GetSearchProviders() []*ExtensionProviderWrapper {
func (m *extensionManager) GetSearchProviders() []*extensionProviderWrapper {
m.mu.RLock()
defer m.mu.RUnlock()
var providers []*ExtensionProviderWrapper
var providers []*extensionProviderWrapper
for _, ext := range m.extensions {
if ext.Enabled && ext.Manifest.HasCustomSearch() && ext.Error == "" {
providers = append(providers, NewExtensionProviderWrapper(ext))
providers = append(providers, newExtensionProviderWrapper(ext))
}
}
return providers
}
func (m *ExtensionManager) GetURLHandlers() []*ExtensionProviderWrapper {
func (m *extensionManager) GetURLHandlers() []*extensionProviderWrapper {
m.mu.RLock()
defer m.mu.RUnlock()
var providers []*ExtensionProviderWrapper
var providers []*extensionProviderWrapper
for _, ext := range m.extensions {
if ext.Enabled && ext.Manifest.HasURLHandler() && ext.Error == "" {
providers = append(providers, NewExtensionProviderWrapper(ext))
providers = append(providers, newExtensionProviderWrapper(ext))
}
}
return providers
}
func (m *ExtensionManager) FindURLHandler(url string) *ExtensionProviderWrapper {
func (m *extensionManager) FindURLHandler(url string) *extensionProviderWrapper {
m.mu.RLock()
defer m.mu.RUnlock()
for _, ext := range m.extensions {
if ext.Enabled && ext.Manifest.MatchesURL(url) && ext.Error == "" {
return NewExtensionProviderWrapper(ext)
return newExtensionProviderWrapper(ext)
}
}
return nil
@ -2118,7 +2118,7 @@ type ExtURLHandleResultWithExtID struct {
ExtensionID string
}
func (m *ExtensionManager) HandleURLWithExtension(url string) (*ExtURLHandleResultWithExtID, error) {
func (m *extensionManager) HandleURLWithExtension(url string) (*ExtURLHandleResultWithExtID, error) {
handler := m.FindURLHandler(url)
if handler == nil {
return nil, fmt.Errorf("no extension found to handle URL: %s", url)
@ -2138,20 +2138,20 @@ func (m *ExtensionManager) HandleURLWithExtension(url string) (*ExtURLHandleResu
}, nil
}
func (m *ExtensionManager) GetPostProcessingProviders() []*ExtensionProviderWrapper {
func (m *extensionManager) GetPostProcessingProviders() []*extensionProviderWrapper {
m.mu.RLock()
defer m.mu.RUnlock()
var providers []*ExtensionProviderWrapper
var providers []*extensionProviderWrapper
for _, ext := range m.extensions {
if ext.Enabled && ext.Manifest.HasPostProcessing() && ext.Error == "" {
providers = append(providers, NewExtensionProviderWrapper(ext))
providers = append(providers, newExtensionProviderWrapper(ext))
}
}
return providers
}
func (m *ExtensionManager) RunPostProcessing(filePath string, metadata map[string]interface{}) (*PostProcessResult, error) {
func (m *extensionManager) RunPostProcessing(filePath string, metadata map[string]interface{}) (*PostProcessResult, error) {
providers := m.GetPostProcessingProviders()
if len(providers) == 0 {
return &PostProcessResult{Success: true, NewFilePath: filePath}, nil
@ -2196,7 +2196,7 @@ func (m *ExtensionManager) RunPostProcessing(filePath string, metadata map[strin
return &PostProcessResult{Success: true, NewFilePath: currentPath}, nil
}
func (m *ExtensionManager) RunPostProcessingV2(input PostProcessInput, metadata map[string]interface{}) (*PostProcessResult, error) {
func (m *extensionManager) RunPostProcessingV2(input PostProcessInput, metadata map[string]interface{}) (*PostProcessResult, error) {
providers := m.GetPostProcessingProviders()
if len(providers) == 0 {
return &PostProcessResult{Success: true, NewFilePath: input.Path, NewFileURI: input.URI}, nil
@ -2264,7 +2264,7 @@ type ExtLyricsLine struct {
EndTimeMs int64 `json:"endTimeMs"`
}
func (p *ExtensionProviderWrapper) FetchLyrics(trackName, artistName, albumName string, durationSec float64) (*LyricsResponse, error) {
func (p *extensionProviderWrapper) FetchLyrics(trackName, artistName, albumName string, durationSec float64) (*LyricsResponse, error) {
if !p.extension.Manifest.IsLyricsProvider() {
return nil, fmt.Errorf("extension '%s' is not a lyrics provider", p.extension.ID)
}
@ -2362,14 +2362,14 @@ func (p *ExtensionProviderWrapper) FetchLyrics(trackName, artistName, albumName
return response, nil
}
func (m *ExtensionManager) GetLyricsProviders() []*ExtensionProviderWrapper {
func (m *extensionManager) GetLyricsProviders() []*extensionProviderWrapper {
m.mu.RLock()
defer m.mu.RUnlock()
var providers []*ExtensionProviderWrapper
var providers []*extensionProviderWrapper
for _, ext := range m.extensions {
if ext.Enabled && ext.Manifest.IsLyricsProvider() && ext.Error == "" {
providers = append(providers, NewExtensionProviderWrapper(ext))
providers = append(providers, newExtensionProviderWrapper(ext))
}
}

View file

@ -51,7 +51,7 @@ func TestSearchTracksWithMetadataProvidersUsesPriorityAndDedupes(t *testing.T) {
}
}
manager := GetExtensionManager()
manager := getExtensionManager()
tracks, err := manager.SearchTracksWithMetadataProviders("query", 3, false)
if err != nil {
t.Fatalf("SearchTracksWithMetadataProviders returned error: %v", err)

View file

@ -80,7 +80,7 @@ func SetExtensionTokens(extensionID string, accessToken, refreshToken string, ex
state.IsAuthenticated = accessToken != ""
}
type ExtensionRuntime struct {
type extensionRuntime struct {
extensionID string
manifest *ExtensionManifest
settings map[string]interface{}
@ -123,10 +123,10 @@ var (
privateIPCacheMu sync.RWMutex
)
func NewExtensionRuntime(ext *LoadedExtension) *ExtensionRuntime {
func newExtensionRuntime(ext *loadedExtension) *extensionRuntime {
jar, _ := newSimpleCookieJar()
runtime := &ExtensionRuntime{
runtime := &extensionRuntime{
extensionID: ext.ID,
manifest: ext.Manifest,
settings: make(map[string]interface{}),
@ -142,25 +142,25 @@ func NewExtensionRuntime(ext *LoadedExtension) *ExtensionRuntime {
return runtime
}
func (r *ExtensionRuntime) setActiveDownloadItemID(itemID string) {
func (r *extensionRuntime) setActiveDownloadItemID(itemID string) {
r.activeDownloadMu.Lock()
defer r.activeDownloadMu.Unlock()
r.activeDownloadItemID = strings.TrimSpace(itemID)
}
func (r *ExtensionRuntime) clearActiveDownloadItemID() {
func (r *extensionRuntime) clearActiveDownloadItemID() {
r.activeDownloadMu.Lock()
defer r.activeDownloadMu.Unlock()
r.activeDownloadItemID = ""
}
func (r *ExtensionRuntime) getActiveDownloadItemID() string {
func (r *extensionRuntime) getActiveDownloadItemID() string {
r.activeDownloadMu.RLock()
defer r.activeDownloadMu.RUnlock()
return r.activeDownloadItemID
}
func newExtensionHTTPClient(ext *LoadedExtension, jar http.CookieJar, timeout time.Duration) *http.Client {
func newExtensionHTTPClient(ext *loadedExtension, jar http.CookieJar, timeout time.Duration) *http.Client {
// Extension sandbox enforces HTTPS-only domains. Do not apply global
// allow_http scheme downgrade here, because some extension APIs (e.g.
// spotify-web) will redirect http -> https and can end up in 301 loops.
@ -329,11 +329,11 @@ func (j *simpleCookieJar) Cookies(u *url.URL) []*http.Cookie {
return j.cookies[u.Host]
}
func (r *ExtensionRuntime) SetSettings(settings map[string]interface{}) {
func (r *extensionRuntime) SetSettings(settings map[string]interface{}) {
r.settings = settings
}
func (r *ExtensionRuntime) RegisterAPIs(vm *goja.Runtime) {
func (r *extensionRuntime) RegisterAPIs(vm *goja.Runtime) {
r.vm = vm
httpObj := vm.NewObject()

View file

@ -52,7 +52,7 @@ func summarizeURLForLog(urlStr string) string {
return fmt.Sprintf("%s://%s%s", parsed.Scheme, parsed.Host, parsed.Path)
}
func (r *ExtensionRuntime) authOpenUrl(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authOpenUrl(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -99,7 +99,7 @@ func (r *ExtensionRuntime) authOpenUrl(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) authGetCode(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authGetCode(call goja.FunctionCall) goja.Value {
extensionAuthStateMu.RLock()
defer extensionAuthStateMu.RUnlock()
@ -111,7 +111,7 @@ func (r *ExtensionRuntime) authGetCode(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(state.AuthCode)
}
func (r *ExtensionRuntime) authSetCode(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authSetCode(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(false)
}
@ -149,7 +149,7 @@ func (r *ExtensionRuntime) authSetCode(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(true)
}
func (r *ExtensionRuntime) authClear(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authClear(call goja.FunctionCall) goja.Value {
extensionAuthStateMu.Lock()
delete(extensionAuthState, r.extensionID)
extensionAuthStateMu.Unlock()
@ -162,7 +162,7 @@ func (r *ExtensionRuntime) authClear(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(true)
}
func (r *ExtensionRuntime) authIsAuthenticated(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authIsAuthenticated(call goja.FunctionCall) goja.Value {
extensionAuthStateMu.RLock()
defer extensionAuthStateMu.RUnlock()
@ -178,7 +178,7 @@ func (r *ExtensionRuntime) authIsAuthenticated(call goja.FunctionCall) goja.Valu
return r.vm.ToValue(state.IsAuthenticated)
}
func (r *ExtensionRuntime) authGetTokens(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authGetTokens(call goja.FunctionCall) goja.Value {
extensionAuthStateMu.RLock()
defer extensionAuthStateMu.RUnlock()
@ -228,7 +228,7 @@ func generatePKCEChallenge(verifier string) string {
return base64.RawURLEncoding.EncodeToString(hash[:])
}
func (r *ExtensionRuntime) authGeneratePKCE(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authGeneratePKCE(call goja.FunctionCall) goja.Value {
length := 64
if len(call.Arguments) > 0 && !goja.IsUndefined(call.Arguments[0]) {
if l, ok := call.Arguments[0].Export().(float64); ok && l >= 43 && l <= 128 {
@ -265,7 +265,7 @@ func (r *ExtensionRuntime) authGeneratePKCE(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) authGetPKCE(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authGetPKCE(call goja.FunctionCall) goja.Value {
extensionAuthStateMu.RLock()
defer extensionAuthStateMu.RUnlock()
@ -281,7 +281,7 @@ func (r *ExtensionRuntime) authGetPKCE(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) authStartOAuthWithPKCE(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authStartOAuthWithPKCE(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -385,7 +385,7 @@ func (r *ExtensionRuntime) authStartOAuthWithPKCE(call goja.FunctionCall) goja.V
})
}
func (r *ExtensionRuntime) authExchangeCodeWithPKCE(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) authExchangeCodeWithPKCE(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,

View file

@ -50,7 +50,7 @@ func ClearFFmpegCommand(commandID string) {
delete(ffmpegCommands, commandID)
}
func (r *ExtensionRuntime) ffmpegExecute(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) ffmpegExecute(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -107,7 +107,7 @@ func (r *ExtensionRuntime) ffmpegExecute(call goja.FunctionCall) goja.Value {
}
}
func (r *ExtensionRuntime) ffmpegGetInfo(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) ffmpegGetInfo(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -134,7 +134,7 @@ func (r *ExtensionRuntime) ffmpegGetInfo(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) ffmpegConvert(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) ffmpegConvert(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,

View file

@ -71,7 +71,7 @@ func isPathWithinBase(baseDir, targetPath string) bool {
return true
}
func (r *ExtensionRuntime) validatePath(path string) (string, error) {
func (r *extensionRuntime) validatePath(path string) (string, error) {
if !r.manifest.Permissions.File {
return "", fmt.Errorf("file access denied: extension does not have 'file' permission")
}
@ -106,7 +106,7 @@ func (r *ExtensionRuntime) validatePath(path string) (string, error) {
return absPath, nil
}
func (r *ExtensionRuntime) fileDownload(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileDownload(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -271,7 +271,7 @@ func (r *ExtensionRuntime) fileDownload(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) fileExists(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileExists(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(false)
}
@ -286,7 +286,7 @@ func (r *ExtensionRuntime) fileExists(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(err == nil)
}
func (r *ExtensionRuntime) fileDelete(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileDelete(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -315,7 +315,7 @@ func (r *ExtensionRuntime) fileDelete(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) fileRead(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileRead(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -346,7 +346,7 @@ func (r *ExtensionRuntime) fileRead(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) fileWrite(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileWrite(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -386,7 +386,7 @@ func (r *ExtensionRuntime) fileWrite(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) fileCopy(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileCopy(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -459,7 +459,7 @@ func (r *ExtensionRuntime) fileCopy(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) fileMove(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileMove(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -507,7 +507,7 @@ func (r *ExtensionRuntime) fileMove(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) fileGetSize(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fileGetSize(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"success": false,

View file

@ -17,7 +17,7 @@ type HTTPResponse struct {
Headers map[string]string `json:"headers"`
}
func (r *ExtensionRuntime) validateDomain(urlStr string) error {
func (r *extensionRuntime) validateDomain(urlStr string) error {
parsed, err := url.Parse(urlStr)
if err != nil {
return fmt.Errorf("invalid URL: %w", err)
@ -49,7 +49,7 @@ func (r *ExtensionRuntime) validateDomain(urlStr string) error {
return nil
}
func (r *ExtensionRuntime) httpGet(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpGet(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"error": "URL is required",
@ -124,7 +124,7 @@ func (r *ExtensionRuntime) httpGet(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) httpPost(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpPost(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"error": "URL is required",
@ -221,7 +221,7 @@ func (r *ExtensionRuntime) httpPost(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) httpRequest(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpRequest(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"error": "URL is required",
@ -330,19 +330,19 @@ func (r *ExtensionRuntime) httpRequest(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) httpPut(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpPut(call goja.FunctionCall) goja.Value {
return r.httpMethodShortcut("PUT", call)
}
func (r *ExtensionRuntime) httpDelete(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpDelete(call goja.FunctionCall) goja.Value {
return r.httpMethodShortcut("DELETE", call)
}
func (r *ExtensionRuntime) httpPatch(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpPatch(call goja.FunctionCall) goja.Value {
return r.httpMethodShortcut("PATCH", call)
}
func (r *ExtensionRuntime) httpMethodShortcut(method string, call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpMethodShortcut(method string, call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(map[string]interface{}{
"error": "URL is required",
@ -455,7 +455,7 @@ func (r *ExtensionRuntime) httpMethodShortcut(method string, call goja.FunctionC
})
}
func (r *ExtensionRuntime) httpClearCookies(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) httpClearCookies(call goja.FunctionCall) goja.Value {
if jar, ok := r.cookieJar.(*simpleCookieJar); ok {
jar.mu.Lock()
jar.cookies = make(map[string][]*http.Cookie)

View file

@ -6,7 +6,7 @@ import (
"github.com/dop251/goja"
)
func (r *ExtensionRuntime) matchingCompareStrings(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) matchingCompareStrings(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(0.0)
}
@ -22,7 +22,7 @@ func (r *ExtensionRuntime) matchingCompareStrings(call goja.FunctionCall) goja.V
return r.vm.ToValue(similarity)
}
func (r *ExtensionRuntime) matchingCompareDuration(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) matchingCompareDuration(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(false)
}
@ -43,7 +43,7 @@ func (r *ExtensionRuntime) matchingCompareDuration(call goja.FunctionCall) goja.
return r.vm.ToValue(diff <= tolerance)
}
func (r *ExtensionRuntime) matchingNormalizeString(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) matchingNormalizeString(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}

View file

@ -12,7 +12,7 @@ import (
"github.com/dop251/goja"
)
func (r *ExtensionRuntime) fetchPolyfill(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) fetchPolyfill(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.createFetchError("URL is required")
}
@ -133,7 +133,7 @@ func (r *ExtensionRuntime) fetchPolyfill(call goja.FunctionCall) goja.Value {
return responseObj
}
func (r *ExtensionRuntime) createFetchError(message string) goja.Value {
func (r *extensionRuntime) createFetchError(message string) goja.Value {
errorObj := r.vm.NewObject()
errorObj.Set("ok", false)
errorObj.Set("status", 0)
@ -148,7 +148,7 @@ func (r *ExtensionRuntime) createFetchError(message string) goja.Value {
return errorObj
}
func (r *ExtensionRuntime) atobPolyfill(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) atobPolyfill(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -164,7 +164,7 @@ func (r *ExtensionRuntime) atobPolyfill(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(string(decoded))
}
func (r *ExtensionRuntime) btoaPolyfill(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) btoaPolyfill(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -172,7 +172,7 @@ func (r *ExtensionRuntime) btoaPolyfill(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(base64.StdEncoding.EncodeToString([]byte(input)))
}
func (r *ExtensionRuntime) registerTextEncoderDecoder(vm *goja.Runtime) {
func (r *extensionRuntime) registerTextEncoderDecoder(vm *goja.Runtime) {
vm.Set("TextEncoder", func(call goja.ConstructorCall) *goja.Object {
encoder := call.This
encoder.Set("encoding", "utf-8")
@ -252,7 +252,7 @@ func (r *ExtensionRuntime) registerTextEncoderDecoder(vm *goja.Runtime) {
})
}
func (r *ExtensionRuntime) registerURLClass(vm *goja.Runtime) {
func (r *extensionRuntime) registerURLClass(vm *goja.Runtime) {
vm.Set("URL", func(call goja.ConstructorCall) *goja.Object {
urlObj := call.This
@ -416,7 +416,7 @@ func (r *ExtensionRuntime) registerURLClass(vm *goja.Runtime) {
})
}
func (r *ExtensionRuntime) registerJSONGlobal(vm *goja.Runtime) {
func (r *extensionRuntime) registerJSONGlobal(vm *goja.Runtime) {
jsonScript := `
if (typeof JSON === 'undefined') {
var JSON = {

View file

@ -21,7 +21,7 @@ const (
storageFlushRetryDelay = 2 * time.Second
)
func (r *ExtensionRuntime) getStoragePath() string {
func (r *extensionRuntime) getStoragePath() string {
return filepath.Join(r.dataDir, "storage.json")
}
@ -36,7 +36,7 @@ func cloneInterfaceMap(src map[string]interface{}) map[string]interface{} {
return dst
}
func (r *ExtensionRuntime) ensureStorageLoaded() error {
func (r *extensionRuntime) ensureStorageLoaded() error {
r.storageMu.RLock()
if r.storageLoaded {
r.storageMu.RUnlock()
@ -74,7 +74,7 @@ func (r *ExtensionRuntime) ensureStorageLoaded() error {
return nil
}
func (r *ExtensionRuntime) loadStorage() (map[string]interface{}, error) {
func (r *extensionRuntime) loadStorage() (map[string]interface{}, error) {
if err := r.ensureStorageLoaded(); err != nil {
return nil, err
}
@ -84,7 +84,7 @@ func (r *ExtensionRuntime) loadStorage() (map[string]interface{}, error) {
return cloneInterfaceMap(r.storageCache), nil
}
func (r *ExtensionRuntime) queueStorageFlushLocked(delay time.Duration) {
func (r *extensionRuntime) queueStorageFlushLocked(delay time.Duration) {
if r.storageClosed {
return
}
@ -94,7 +94,7 @@ func (r *ExtensionRuntime) queueStorageFlushLocked(delay time.Duration) {
r.storageTimer = time.AfterFunc(delay, r.flushStorageDirtyAsync)
}
func (r *ExtensionRuntime) persistStorageSnapshot(storage map[string]interface{}) error {
func (r *extensionRuntime) persistStorageSnapshot(storage map[string]interface{}) error {
data, err := json.Marshal(storage)
if err != nil {
return err
@ -106,13 +106,13 @@ func (r *ExtensionRuntime) persistStorageSnapshot(storage map[string]interface{}
return os.WriteFile(r.getStoragePath(), data, 0600)
}
func (r *ExtensionRuntime) flushStorageDirtyAsync() {
func (r *extensionRuntime) flushStorageDirtyAsync() {
if err := r.flushStorageDirty(); err != nil {
GoLog("[Extension:%s] Storage flush error: %v\n", r.extensionID, err)
}
}
func (r *ExtensionRuntime) flushStorageDirty() error {
func (r *extensionRuntime) flushStorageDirty() error {
r.storageMu.Lock()
if r.storageClosed {
r.storageTimer = nil
@ -140,7 +140,7 @@ func (r *ExtensionRuntime) flushStorageDirty() error {
return nil
}
func (r *ExtensionRuntime) flushStorageNow() error {
func (r *extensionRuntime) flushStorageNow() error {
r.storageMu.Lock()
if r.storageTimer != nil {
r.storageTimer.Stop()
@ -157,7 +157,7 @@ func (r *ExtensionRuntime) flushStorageNow() error {
return r.persistStorageSnapshot(snapshot)
}
func (r *ExtensionRuntime) closeStorageFlusher() {
func (r *extensionRuntime) closeStorageFlusher() {
r.storageMu.Lock()
r.storageClosed = true
r.storageDirty = false
@ -168,7 +168,7 @@ func (r *ExtensionRuntime) closeStorageFlusher() {
r.storageMu.Unlock()
}
func (r *ExtensionRuntime) storageGet(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) storageGet(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return goja.Undefined()
}
@ -193,7 +193,7 @@ func (r *ExtensionRuntime) storageGet(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(value)
}
func (r *ExtensionRuntime) storageSet(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) storageSet(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(false)
}
@ -225,7 +225,7 @@ func (r *ExtensionRuntime) storageSet(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(true)
}
func (r *ExtensionRuntime) storageRemove(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) storageRemove(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(false)
}
@ -254,15 +254,15 @@ func (r *ExtensionRuntime) storageRemove(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(true)
}
func (r *ExtensionRuntime) getCredentialsPath() string {
func (r *extensionRuntime) getCredentialsPath() string {
return filepath.Join(r.dataDir, ".credentials.enc")
}
func (r *ExtensionRuntime) getSaltPath() string {
func (r *extensionRuntime) getSaltPath() string {
return filepath.Join(r.dataDir, ".cred_salt")
}
func (r *ExtensionRuntime) getOrCreateSalt() ([]byte, error) {
func (r *extensionRuntime) getOrCreateSalt() ([]byte, error) {
saltPath := r.getSaltPath()
salt, err := os.ReadFile(saltPath)
@ -282,7 +282,7 @@ func (r *ExtensionRuntime) getOrCreateSalt() ([]byte, error) {
return salt, nil
}
func (r *ExtensionRuntime) getEncryptionKey() ([]byte, error) {
func (r *extensionRuntime) getEncryptionKey() ([]byte, error) {
salt, err := r.getOrCreateSalt()
if err != nil {
return nil, err
@ -293,7 +293,7 @@ func (r *ExtensionRuntime) getEncryptionKey() ([]byte, error) {
return hash[:], nil
}
func (r *ExtensionRuntime) ensureCredentialsLoaded() error {
func (r *extensionRuntime) ensureCredentialsLoaded() error {
r.credentialsMu.RLock()
if r.credentialsLoaded {
r.credentialsMu.RUnlock()
@ -340,7 +340,7 @@ func (r *ExtensionRuntime) ensureCredentialsLoaded() error {
return nil
}
func (r *ExtensionRuntime) loadCredentials() (map[string]interface{}, error) {
func (r *extensionRuntime) loadCredentials() (map[string]interface{}, error) {
if err := r.ensureCredentialsLoaded(); err != nil {
return nil, err
}
@ -350,7 +350,7 @@ func (r *ExtensionRuntime) loadCredentials() (map[string]interface{}, error) {
return cloneInterfaceMap(r.credentialsCache), nil
}
func (r *ExtensionRuntime) saveCredentials(creds map[string]interface{}) error {
func (r *extensionRuntime) saveCredentials(creds map[string]interface{}) error {
data, err := json.Marshal(creds)
if err != nil {
return err
@ -377,7 +377,7 @@ func (r *ExtensionRuntime) saveCredentials(creds map[string]interface{}) error {
return nil
}
func (r *ExtensionRuntime) credentialsStore(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) credentialsStore(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -414,7 +414,7 @@ func (r *ExtensionRuntime) credentialsStore(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) credentialsGet(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) credentialsGet(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return goja.Undefined()
}
@ -439,7 +439,7 @@ func (r *ExtensionRuntime) credentialsGet(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(value)
}
func (r *ExtensionRuntime) credentialsRemove(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) credentialsRemove(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(false)
}
@ -464,7 +464,7 @@ func (r *ExtensionRuntime) credentialsRemove(call goja.FunctionCall) goja.Value
return r.vm.ToValue(true)
}
func (r *ExtensionRuntime) credentialsHas(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) credentialsHas(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue(false)
}

View file

@ -11,7 +11,7 @@ import (
"github.com/dop251/goja"
)
func setStorageValue(t *testing.T, runtime *ExtensionRuntime, key string, value interface{}) {
func setStorageValue(t *testing.T, runtime *extensionRuntime, key string, value interface{}) {
t.Helper()
result := runtime.storageSet(goja.FunctionCall{
Arguments: []goja.Value{
@ -39,7 +39,7 @@ func readStorageMap(t *testing.T, storagePath string) map[string]interface{} {
}
func TestExtensionRuntimeStorage_DebouncedWriteCompactJSON(t *testing.T) {
ext := &LoadedExtension{
ext := &loadedExtension{
ID: "storage-test",
Manifest: &ExtensionManifest{
Name: "storage-test",
@ -47,7 +47,7 @@ func TestExtensionRuntimeStorage_DebouncedWriteCompactJSON(t *testing.T) {
DataDir: t.TempDir(),
}
runtime := NewExtensionRuntime(ext)
runtime := newExtensionRuntime(ext)
runtime.storageFlushDelay = 25 * time.Millisecond
runtime.RegisterAPIs(goja.New())
@ -86,7 +86,7 @@ func TestExtensionRuntimeStorage_DebouncedWriteCompactJSON(t *testing.T) {
}
func TestUnloadExtension_FlushesPendingStorage(t *testing.T) {
ext := &LoadedExtension{
ext := &loadedExtension{
ID: "unload-storage-test",
Manifest: &ExtensionManifest{
Name: "unload-storage-test",
@ -95,13 +95,13 @@ func TestUnloadExtension_FlushesPendingStorage(t *testing.T) {
VM: goja.New(),
}
runtime := NewExtensionRuntime(ext)
runtime := newExtensionRuntime(ext)
runtime.storageFlushDelay = time.Hour
runtime.RegisterAPIs(ext.VM)
ext.runtime = runtime
manager := &ExtensionManager{
extensions: map[string]*LoadedExtension{
manager := &extensionManager{
extensions: map[string]*loadedExtension{
ext.ID: ext,
},
}

View file

@ -16,7 +16,7 @@ import (
"github.com/dop251/goja"
)
func (r *ExtensionRuntime) base64Encode(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) base64Encode(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -24,7 +24,7 @@ func (r *ExtensionRuntime) base64Encode(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(base64.StdEncoding.EncodeToString([]byte(input)))
}
func (r *ExtensionRuntime) base64Decode(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) base64Decode(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -36,7 +36,7 @@ func (r *ExtensionRuntime) base64Decode(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(string(decoded))
}
func (r *ExtensionRuntime) md5Hash(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) md5Hash(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -45,7 +45,7 @@ func (r *ExtensionRuntime) md5Hash(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(hex.EncodeToString(hash[:]))
}
func (r *ExtensionRuntime) sha256Hash(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) sha256Hash(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -54,7 +54,7 @@ func (r *ExtensionRuntime) sha256Hash(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(hex.EncodeToString(hash[:]))
}
func (r *ExtensionRuntime) hmacSHA256(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) hmacSHA256(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue("")
}
@ -66,7 +66,7 @@ func (r *ExtensionRuntime) hmacSHA256(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(hex.EncodeToString(mac.Sum(nil)))
}
func (r *ExtensionRuntime) hmacSHA256Base64(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) hmacSHA256Base64(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue("")
}
@ -78,7 +78,7 @@ func (r *ExtensionRuntime) hmacSHA256Base64(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(base64.StdEncoding.EncodeToString(mac.Sum(nil)))
}
func (r *ExtensionRuntime) hmacSHA1(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) hmacSHA1(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue([]byte{})
}
@ -130,7 +130,7 @@ func (r *ExtensionRuntime) hmacSHA1(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(jsArray)
}
func (r *ExtensionRuntime) parseJSON(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) parseJSON(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return goja.Undefined()
}
@ -145,7 +145,7 @@ func (r *ExtensionRuntime) parseJSON(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(result)
}
func (r *ExtensionRuntime) stringifyJSON(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) stringifyJSON(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -160,7 +160,7 @@ func (r *ExtensionRuntime) stringifyJSON(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(string(data))
}
func (r *ExtensionRuntime) cryptoEncrypt(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) cryptoEncrypt(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -187,7 +187,7 @@ func (r *ExtensionRuntime) cryptoEncrypt(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) cryptoDecrypt(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) cryptoDecrypt(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 2 {
return r.vm.ToValue(map[string]interface{}{
"success": false,
@ -222,7 +222,7 @@ func (r *ExtensionRuntime) cryptoDecrypt(call goja.FunctionCall) goja.Value {
})
}
func (r *ExtensionRuntime) cryptoGenerateKey(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) cryptoGenerateKey(call goja.FunctionCall) goja.Value {
length := 32
if len(call.Arguments) > 0 && !goja.IsUndefined(call.Arguments[0]) {
if l, ok := call.Arguments[0].Export().(float64); ok {
@ -245,35 +245,35 @@ func (r *ExtensionRuntime) cryptoGenerateKey(call goja.FunctionCall) goja.Value
})
}
func (r *ExtensionRuntime) randomUserAgent(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) randomUserAgent(call goja.FunctionCall) goja.Value {
return r.vm.ToValue(getRandomUserAgent())
}
func (r *ExtensionRuntime) logDebug(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) logDebug(call goja.FunctionCall) goja.Value {
msg := r.formatLogArgs(call.Arguments)
GoLog("[Extension:%s:DEBUG] %s\n", r.extensionID, msg)
return goja.Undefined()
}
func (r *ExtensionRuntime) logInfo(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) logInfo(call goja.FunctionCall) goja.Value {
msg := r.formatLogArgs(call.Arguments)
GoLog("[Extension:%s:INFO] %s\n", r.extensionID, msg)
return goja.Undefined()
}
func (r *ExtensionRuntime) logWarn(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) logWarn(call goja.FunctionCall) goja.Value {
msg := r.formatLogArgs(call.Arguments)
GoLog("[Extension:%s:WARN] %s\n", r.extensionID, msg)
return goja.Undefined()
}
func (r *ExtensionRuntime) logError(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) logError(call goja.FunctionCall) goja.Value {
msg := r.formatLogArgs(call.Arguments)
GoLog("[Extension:%s:ERROR] %s\n", r.extensionID, msg)
return goja.Undefined()
}
func (r *ExtensionRuntime) formatLogArgs(args []goja.Value) string {
func (r *extensionRuntime) formatLogArgs(args []goja.Value) string {
parts := make([]string, len(args))
for i, arg := range args {
parts[i] = fmt.Sprintf("%v", arg.Export())
@ -281,7 +281,7 @@ func (r *ExtensionRuntime) formatLogArgs(args []goja.Value) string {
return strings.Join(parts, " ")
}
func (r *ExtensionRuntime) sanitizeFilenameWrapper(call goja.FunctionCall) goja.Value {
func (r *extensionRuntime) sanitizeFilenameWrapper(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return r.vm.ToValue("")
}
@ -289,7 +289,7 @@ func (r *ExtensionRuntime) sanitizeFilenameWrapper(call goja.FunctionCall) goja.
return r.vm.ToValue(sanitizeFilename(input))
}
func (r *ExtensionRuntime) RegisterGoBackendAPIs(vm *goja.Runtime) {
func (r *extensionRuntime) RegisterGoBackendAPIs(vm *goja.Runtime) {
gobackendObj := vm.Get("gobackend")
if gobackendObj == nil || goja.IsUndefined(gobackendObj) {
gobackendObj = vm.NewObject()

View file

@ -305,7 +305,7 @@ func (s *extensionStore) getExtensionsWithStatus(forceRefresh bool) ([]storeExte
return nil, err
}
manager := GetExtensionManager()
manager := getExtensionManager()
installed := make(map[string]string) // id -> version
if manager != nil {

View file

@ -99,7 +99,7 @@ func TestIsDomainAllowed(t *testing.T) {
func TestExtensionRuntime_NetworkSandbox(t *testing.T) {
// Create a mock extension with limited network permissions
ext := &LoadedExtension{
ext := &loadedExtension{
ID: "test-ext",
Manifest: &ExtensionManifest{
Name: "test-ext",
@ -110,7 +110,7 @@ func TestExtensionRuntime_NetworkSandbox(t *testing.T) {
DataDir: t.TempDir(),
}
runtime := NewExtensionRuntime(ext)
runtime := newExtensionRuntime(ext)
if err := runtime.validateDomain("https://api.allowed.com/path"); err != nil {
t.Errorf("Expected api.allowed.com to be allowed, got error: %v", err)
@ -132,7 +132,7 @@ func TestExtensionRuntime_NetworkSandbox(t *testing.T) {
func TestExtensionRuntime_FileSandbox(t *testing.T) {
tempDir := t.TempDir()
ext := &LoadedExtension{
ext := &loadedExtension{
ID: "test-ext",
Manifest: &ExtensionManifest{
Name: "test-ext",
@ -143,7 +143,7 @@ func TestExtensionRuntime_FileSandbox(t *testing.T) {
DataDir: tempDir,
}
runtime := NewExtensionRuntime(ext)
runtime := newExtensionRuntime(ext)
validPath, err := runtime.validatePath("test.txt")
if err != nil {
@ -177,7 +177,7 @@ func TestExtensionRuntime_FileSandbox(t *testing.T) {
t.Error("Expected absolute path to be blocked")
}
extNoFile := &LoadedExtension{
extNoFile := &loadedExtension{
ID: "test-ext-no-file",
Manifest: &ExtensionManifest{
Name: "test-ext-no-file",
@ -187,7 +187,7 @@ func TestExtensionRuntime_FileSandbox(t *testing.T) {
},
DataDir: tempDir,
}
runtimeNoFile := NewExtensionRuntime(extNoFile)
runtimeNoFile := newExtensionRuntime(extNoFile)
_, err = runtimeNoFile.validatePath("test.txt")
if err == nil {
t.Error("Expected file access to be denied without file permission")
@ -195,7 +195,7 @@ func TestExtensionRuntime_FileSandbox(t *testing.T) {
}
func TestExtensionRuntime_UtilityFunctions(t *testing.T) {
ext := &LoadedExtension{
ext := &loadedExtension{
ID: "test-ext",
Manifest: &ExtensionManifest{
Name: "test-ext",
@ -203,7 +203,7 @@ func TestExtensionRuntime_UtilityFunctions(t *testing.T) {
DataDir: t.TempDir(),
}
runtime := NewExtensionRuntime(ext)
runtime := newExtensionRuntime(ext)
vm := goja.New()
runtime.RegisterAPIs(vm)
@ -243,7 +243,7 @@ func TestExtensionRuntime_UtilityFunctions(t *testing.T) {
func TestExtensionRuntime_SSRFProtection(t *testing.T) {
// Create extension with limited network permissions
ext := &LoadedExtension{
ext := &loadedExtension{
ID: "test-ext",
Manifest: &ExtensionManifest{
Name: "test-ext",
@ -254,7 +254,7 @@ func TestExtensionRuntime_SSRFProtection(t *testing.T) {
DataDir: t.TempDir(),
}
runtime := NewExtensionRuntime(ext)
runtime := newExtensionRuntime(ext)
privateIPs := []string{
"http://localhost/admin",

View file

@ -53,7 +53,7 @@ func RunWithTimeout(vm *goja.Runtime, script string, timeout time.Duration) (goj
IsTimeout: true,
}}
} else {
GoLog("[ExtensionRuntime] panic during JS execution: %v\n%s\n", r, string(debug.Stack()))
GoLog("[extensionRuntime] panic during JS execution: %v\n%s\n", r, string(debug.Stack()))
resultCh <- result{nil, fmt.Errorf("panic during execution: %v", r)}
}
}
@ -90,7 +90,7 @@ func RunWithTimeout(vm *goja.Runtime, script string, timeout time.Duration) (goj
case <-time.After(60 * time.Second):
// Goroutine is truly stuck (e.g. HTTP read with no timeout).
// Log a warning — the VM should NOT be reused after this.
GoLog("[ExtensionRuntime] WARNING: JS goroutine did not exit within 60s after interrupt, VM may be unsafe\n")
GoLog("[extensionRuntime] WARNING: JS goroutine did not exit within 60s after interrupt, VM may be unsafe\n")
return nil, &JSExecutionError{
Message: "execution timeout exceeded (force)",
IsTimeout: true,

View file

@ -385,8 +385,8 @@ func (c *LyricsClient) FetchLyricsAllSources(spotifyID, trackName, artistName st
primaryArtist := normalizeArtistName(artistName)
fetchOptions := GetLyricsFetchOptions()
extManager := GetExtensionManager()
var extensionProviders []*ExtensionProviderWrapper
extManager := getExtensionManager()
var extensionProviders []*extensionProviderWrapper
if extManager != nil {
extensionProviders = extManager.GetLyricsProviders()
}

View file

@ -1311,28 +1311,38 @@ class FFmpegService {
cmdBuffer.write('-v error -hide_banner ');
cmdBuffer.write('-i "$m4aPath" ');
final hasCover = coverPath != null && await File(coverPath).exists();
final normalizedCoverPath = coverPath?.trim();
final hasCover =
normalizedCoverPath != null &&
normalizedCoverPath.isNotEmpty &&
await File(normalizedCoverPath).exists();
if (hasCover) {
cmdBuffer.write('-i "$coverPath" ');
cmdBuffer.write('-i "$normalizedCoverPath" ');
}
cmdBuffer.write('-map 0:a ');
final preserveExistingStreams = preserveMetadata && !hasCover;
if (preserveExistingStreams) {
// When no replacement cover is provided, preserve all input streams so
// the existing attached artwork is not dropped during the metadata rewrite.
cmdBuffer.write('-map 0 -c copy ');
} else {
cmdBuffer.write('-map 0:a -c:a copy ');
}
cmdBuffer.write(
preserveMetadata ? '-map_metadata 0 ' : '-map_metadata -1 ',
);
// For M4A/MP4, cover art is mapped as a video stream and stored in the
// 'covr' atom automatically by FFmpeg. The '-disposition attached_pic'
// flag is only valid for Matroska/WebM containers and must NOT be used here.
// Force the mp4 muxer when cover art is present because the default ipod
// muxer (auto-selected for .m4a) does not register a codec tag for mjpeg,
// causing "codec not currently supported in container" on FFmpeg 8.0+.
// For M4A cover replacements, mark the image as an attached picture so the
// mp4 muxer writes a proper covr atom instead of a generic MJPEG video track.
// Force the mp4 muxer because the default ipod muxer (auto-selected for .m4a)
// does not register a codec tag for mjpeg on FFmpeg 8.0+.
if (hasCover) {
cmdBuffer.write('-map 1:v -c:v copy -f mp4 ');
cmdBuffer.write('-map 1:v -c:v copy -disposition:v:0 attached_pic ');
cmdBuffer.write('-metadata:s:v title="Album cover" ');
cmdBuffer.write('-metadata:s:v comment="Cover (front)" ');
cmdBuffer.write('-f mp4 ');
}
cmdBuffer.write('-c:a copy ');
if (metadata != null) {
final m4aMetadata = _convertToM4aTags(metadata);
for (final entry in m4aMetadata.entries) {