MetaTube / engine /engine.go
henry99a's picture
Clean commit for Hugging Face Spaces without binary files
ca7217f
package engine
import (
"fmt"
"log"
gomaps "maps"
"net/http"
"net/url"
"strings"
"time"
"gorm.io/gorm"
"github.com/metatube-community/metatube-sdk-go/collection/maps"
"github.com/metatube-community/metatube-sdk-go/common/fetch"
"github.com/metatube-community/metatube-sdk-go/database"
mt "github.com/metatube-community/metatube-sdk-go/provider"
)
const (
DefaultEngineName = "metatube"
DefaultRequestTimeout = time.Minute
)
type Engine struct {
db *gorm.DB
name string
timeout time.Duration
fetcher *fetch.Fetcher
// Engine Logger
logger *log.Logger
// Name:Config Case-Insensitive Map
actorProviderConfigs *maps.CaseInsensitiveMap[mt.Config]
movieProviderConfigs *maps.CaseInsensitiveMap[mt.Config]
// Name:Provider Case-Insensitive Map
actorProviders *maps.CaseInsensitiveMap[mt.ActorProvider]
movieProviders *maps.CaseInsensitiveMap[mt.MovieProvider]
// Host:[]Provider Case-Insensitive Map
// We need a []mt.ActorProvider here because sometimes providers
// can share the same host, but they're two different providers.
// However, in most cases, a host is mapped to only one provider.
// E.g., github.com -> [Gfriends, ...]
actorHostProviders *maps.CaseInsensitiveMap[[]mt.ActorProvider]
movieHostProviders *maps.CaseInsensitiveMap[[]mt.MovieProvider]
}
func New(db *gorm.DB, opts ...Option) *Engine {
engine := &Engine{
db: db,
name: DefaultEngineName,
timeout: DefaultRequestTimeout,
// pre-initialize case-insensitive maps.
actorProviderConfigs: maps.NewCaseInsensitiveMap[mt.Config](),
movieProviderConfigs: maps.NewCaseInsensitiveMap[mt.Config](),
actorProviders: maps.NewCaseInsensitiveMap[mt.ActorProvider](),
movieProviders: maps.NewCaseInsensitiveMap[mt.MovieProvider](),
actorHostProviders: maps.NewCaseInsensitiveMap[[]mt.ActorProvider](),
movieHostProviders: maps.NewCaseInsensitiveMap[[]mt.MovieProvider](),
}
// apply options.
for _, opt := range opts {
opt(engine)
}
return engine.init()
}
func Default() *Engine {
db, _ := database.Open(&database.Config{
DSN: "",
DisableAutomaticPing: true,
})
engine := New(db)
defer engine.DBAutoMigrate(true)
return engine
}
func (e *Engine) IsActorProvider(name string) bool {
return e.actorProviders.Has(name)
}
func (e *Engine) GetActorProviders() map[string]mt.ActorProvider {
return gomaps.Collect(e.actorProviders.Iterator())
}
func (e *Engine) GetActorProviderByURL(rawURL string) (mt.ActorProvider, error) {
u, err := url.Parse(rawURL)
if err != nil {
return nil, err
}
for _, p := range e.actorHostProviders.GetOrDefault(u.Hostname(), nil) {
if strings.HasPrefix(u.Path, p.URL().Path) {
return p, nil
}
}
return nil, mt.ErrProviderNotFound
}
func (e *Engine) GetActorProviderByName(name string) (mt.ActorProvider, error) {
provider, ok := e.actorProviders.Get(name)
if !ok {
return nil, mt.ErrProviderNotFound
}
return provider, nil
}
func (e *Engine) MustGetActorProviderByName(name string) mt.ActorProvider {
provider, err := e.GetActorProviderByName(name)
if err != nil {
panic(err)
}
return provider
}
func (e *Engine) IsMovieProvider(name string) bool {
return e.movieProviders.Has(name)
}
func (e *Engine) GetMovieProviders() map[string]mt.MovieProvider {
return gomaps.Collect(e.movieProviders.Iterator())
}
func (e *Engine) GetMovieProviderByURL(rawURL string) (mt.MovieProvider, error) {
u, err := url.Parse(rawURL)
if err != nil {
return nil, err
}
for _, p := range e.movieHostProviders.GetOrDefault(u.Hostname(), nil) {
if strings.HasPrefix(u.Path, p.URL().Path) {
return p, nil
}
}
return nil, mt.ErrProviderNotFound
}
func (e *Engine) GetMovieProviderByName(name string) (mt.MovieProvider, error) {
provider, ok := e.movieProviders.Get(name)
if !ok {
return nil, mt.ErrProviderNotFound
}
return provider, nil
}
func (e *Engine) MustGetMovieProviderByName(name string) mt.MovieProvider {
provider, err := e.GetMovieProviderByName(name)
if err != nil {
panic(err)
}
return provider
}
// Fetch fetches content from url. If the provider
// is nil, the default fetcher will be used.
func (e *Engine) Fetch(url string, provider mt.Provider) (*http.Response, error) {
// Provider which implements Fetcher interface should be
// used to fetch all its corresponding resources.
if fetcher, ok := provider.(mt.Fetcher); ok {
return fetcher.Fetch(url)
}
return e.fetcher.Fetch(url)
}
// String returns the name of the Engine instance.
func (e *Engine) String() string { return e.name }
var (
_ = New
_ = Default
)
var _ fmt.Stringer = (*Engine)(nil)