103 lines
2.3 KiB
Go
103 lines
2.3 KiB
Go
|
package youtube
|
||
|
|
||
|
import (
|
||
|
"git.nobrain.org/r4/dischord/extractor"
|
||
|
|
||
|
"errors"
|
||
|
"net/url"
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
extractor.AddExtractor("youtube", &Extractor{})
|
||
|
extractor.AddSearcher("youtube-search", &Searcher{})
|
||
|
extractor.AddSuggestor("youtube-search-suggestions", &Suggestor{})
|
||
|
}
|
||
|
|
||
|
type matchType int
|
||
|
|
||
|
const (
|
||
|
matchTypeNone matchType = iota
|
||
|
matchTypeVideo
|
||
|
matchTypePlaylist
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
ErrInvalidInput = errors.New("invalid input")
|
||
|
)
|
||
|
|
||
|
func matches(requireDirectPlaylistUrl bool, input string) matchType {
|
||
|
u, err := url.Parse(input)
|
||
|
if err != nil {
|
||
|
return matchTypeNone
|
||
|
}
|
||
|
if u.Scheme != "http" && u.Scheme != "https" {
|
||
|
return matchTypeNone
|
||
|
}
|
||
|
q, err := url.ParseQuery(u.RawQuery)
|
||
|
if err != nil {
|
||
|
return matchTypeNone
|
||
|
}
|
||
|
switch u.Host {
|
||
|
case "www.youtube.com", "youtube.com":
|
||
|
if u.Path != "/watch" && u.Path != "/playlist" {
|
||
|
return matchTypeNone
|
||
|
}
|
||
|
if q.Has("list") && (!requireDirectPlaylistUrl || u.Path == "/playlist") {
|
||
|
return matchTypePlaylist
|
||
|
}
|
||
|
return matchTypeVideo
|
||
|
case "youtu.be":
|
||
|
return matchTypeVideo
|
||
|
default:
|
||
|
return matchTypeNone
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Extractor struct {
|
||
|
decryptor decryptor
|
||
|
}
|
||
|
|
||
|
func (e *Extractor) DefaultConfig() extractor.ProviderConfig {
|
||
|
return extractor.ProviderConfig{
|
||
|
"require-direct-playlist-url": false,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (e *Extractor) Matches(cfg extractor.ProviderConfig, input string) bool {
|
||
|
return matches(cfg["require-direct-playlist-url"].(bool), input) != matchTypeNone
|
||
|
}
|
||
|
|
||
|
func (e *Extractor) Extract(cfg extractor.ProviderConfig, input string) ([]extractor.Data, error) {
|
||
|
switch matches(cfg["require-direct-playlist-url"].(bool), input) {
|
||
|
case matchTypeVideo:
|
||
|
d, err := getVideo(&e.decryptor, input)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return []extractor.Data{d}, nil
|
||
|
case matchTypePlaylist:
|
||
|
return getPlaylist(input)
|
||
|
}
|
||
|
return nil, ErrInvalidInput
|
||
|
}
|
||
|
|
||
|
type Searcher struct{}
|
||
|
|
||
|
func (s *Searcher) DefaultConfig() extractor.ProviderConfig {
|
||
|
return extractor.ProviderConfig{}
|
||
|
}
|
||
|
|
||
|
func (s *Searcher) Search(cfg extractor.ProviderConfig, input string) ([]extractor.Data, error) {
|
||
|
return getSearch(input)
|
||
|
}
|
||
|
|
||
|
type Suggestor struct{}
|
||
|
|
||
|
func (s *Suggestor) DefaultConfig() extractor.ProviderConfig {
|
||
|
return extractor.ProviderConfig{}
|
||
|
}
|
||
|
|
||
|
func (s *Suggestor) Suggest(cfg extractor.ProviderConfig, input string) ([]string, error) {
|
||
|
return getSearchSuggestions(input)
|
||
|
}
|