mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-06 08:30:29 +00:00
101 lines
2.5 KiB
Go
101 lines
2.5 KiB
Go
package hls
|
|
|
|
import (
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/GopeedLab/gopeed/internal/fetcher"
|
|
)
|
|
|
|
type FetcherManager struct {
|
|
}
|
|
|
|
func (fm *FetcherManager) Name() string {
|
|
return "hls"
|
|
}
|
|
|
|
// Filters registers the .m3u8 extension. The manager must be registered BEFORE
|
|
// the http manager in the downloader config: protocol routing picks the first
|
|
// manager whose filter matches, and the http filter claims every http(s) URL.
|
|
func (fm *FetcherManager) Filters() []*fetcher.SchemeFilter {
|
|
return []*fetcher.SchemeFilter{
|
|
{
|
|
Type: fetcher.FilterTypeFile,
|
|
Pattern: "M3U8",
|
|
},
|
|
}
|
|
}
|
|
|
|
func (fm *FetcherManager) Build() fetcher.Fetcher {
|
|
return &Fetcher{}
|
|
}
|
|
|
|
func (fm *FetcherManager) ParseName(u string) string {
|
|
parsed, err := url.Parse(u)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
name := path.Base(parsed.Path)
|
|
name = strings.TrimSuffix(name, path.Ext(name))
|
|
if name == "" || name == "/" || name == "." {
|
|
name = parsed.Hostname()
|
|
}
|
|
return name
|
|
}
|
|
|
|
func (fm *FetcherManager) AutoRename() bool {
|
|
return true
|
|
}
|
|
|
|
func (fm *FetcherManager) DefaultConfig() any {
|
|
return &config{
|
|
SegmentConnections: defaultSegmentConnections,
|
|
MaxRetries: defaultMaxRetries,
|
|
TimeoutSeconds: defaultTimeoutSeconds,
|
|
PrefetchContentLength: true,
|
|
}
|
|
}
|
|
|
|
func (fm *FetcherManager) Store(f fetcher.Fetcher) (data any, err error) {
|
|
_f := f.(*Fetcher)
|
|
_f.mu.Lock()
|
|
defer _f.mu.Unlock()
|
|
if _f.state == nil {
|
|
return nil, nil
|
|
}
|
|
// The engine's periodic checkpoint calls Store without holding the task
|
|
// lock and serializes the returned value afterwards, while Resolve/Patch/
|
|
// Start mutate the live state under the fetcher mutex: return a snapshot
|
|
// copy taken under that mutex, mirroring the HTTP protocol's Store.
|
|
return cloneState(_f.state), nil
|
|
}
|
|
|
|
func cloneState(state *fetcherState) *fetcherState {
|
|
cloned := *state
|
|
cloned.Segments = make([]*Segment, len(state.Segments))
|
|
for i, seg := range state.Segments {
|
|
s := *seg
|
|
cloned.Segments[i] = &s
|
|
}
|
|
return &cloned
|
|
}
|
|
|
|
func (fm *FetcherManager) Restore() (v any, f func(meta *fetcher.FetcherMeta, v any) fetcher.Fetcher) {
|
|
return &fetcherState{}, func(meta *fetcher.FetcherMeta, v any) fetcher.Fetcher {
|
|
fb := &FetcherManager{}
|
|
restored := fb.Build().(*Fetcher)
|
|
restored.meta = meta
|
|
if state, ok := v.(*fetcherState); ok && state.MediaURL != "" {
|
|
restored.state = state
|
|
}
|
|
// Setup is invoked by the engine (restoreFetcher) after this builder
|
|
// returns; ReqExtra parsing happens there as well.
|
|
return restored
|
|
}
|
|
}
|
|
|
|
func (fm *FetcherManager) Close() error {
|
|
return nil
|
|
}
|