chg: feat: add better magnet processing
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"github.com/felipemarinho97/torrent-indexer/magnet"
|
||||||
"github.com/felipemarinho97/torrent-indexer/schema"
|
"github.com/felipemarinho97/torrent-indexer/schema"
|
||||||
goscrape "github.com/felipemarinho97/torrent-indexer/scrape"
|
goscrape "github.com/felipemarinho97/torrent-indexer/scrape"
|
||||||
)
|
)
|
||||||
@@ -47,6 +48,7 @@ type IndexedTorrent struct {
|
|||||||
Date time.Time `json:"date"`
|
Date time.Time `json:"date"`
|
||||||
InfoHash string `json:"info_hash"`
|
InfoHash string `json:"info_hash"`
|
||||||
Trackers []string `json:"trackers"`
|
Trackers []string `json:"trackers"`
|
||||||
|
Size string `json:"size"`
|
||||||
LeechCount int `json:"leech_count"`
|
LeechCount int `json:"leech_count"`
|
||||||
SeedCount int `json:"seed_count"`
|
SeedCount int `json:"seed_count"`
|
||||||
}
|
}
|
||||||
@@ -148,6 +150,7 @@ func getTorrents(ctx context.Context, i *Indexer, link string) ([]IndexedTorrent
|
|||||||
|
|
||||||
var audio []schema.Audio
|
var audio []schema.Audio
|
||||||
var year string
|
var year string
|
||||||
|
var size []string
|
||||||
article.Find("div.entry-content > p").Each(func(i int, s *goquery.Selection) {
|
article.Find("div.entry-content > p").Each(func(i int, s *goquery.Selection) {
|
||||||
// pattern:
|
// pattern:
|
||||||
// Título Traduzido: Fundação
|
// Título Traduzido: Fundação
|
||||||
@@ -167,8 +170,131 @@ func getTorrents(ctx context.Context, i *Indexer, link string) ([]IndexedTorrent
|
|||||||
// Servidor: Torrent
|
// Servidor: Torrent
|
||||||
text := s.Text()
|
text := s.Text()
|
||||||
|
|
||||||
//re := regexp.MustCompile(`Áudio: (.*)`)
|
audio = append(audio, findAudioFromText(text)...)
|
||||||
re := regexp.MustCompile(`(Áudio|Idioma): (.*)`)
|
year = findYearFromText(text, title)
|
||||||
|
size = append(size, findSizesFromText(text)...)
|
||||||
|
})
|
||||||
|
|
||||||
|
size = stableUniq(size)
|
||||||
|
|
||||||
|
var chanIndexedTorrent = make(chan IndexedTorrent)
|
||||||
|
|
||||||
|
// for each magnet link, create a new indexed torrent
|
||||||
|
for it, magnetLink := range magnetLinks {
|
||||||
|
it := it
|
||||||
|
go func(it int, magnetLink string) {
|
||||||
|
magnet, err := magnet.ParseMagnetUri(magnetLink)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
releaseTitle := magnet.DisplayName
|
||||||
|
infoHash := magnet.InfoHash.String()
|
||||||
|
trackers := magnet.Trackers
|
||||||
|
magnetAudio := []schema.Audio{}
|
||||||
|
if strings.Contains(strings.ToLower(releaseTitle), "dual") {
|
||||||
|
magnetAudio = append(magnetAudio, audio...)
|
||||||
|
} else if len(audio) > 1 {
|
||||||
|
// remove portuguese audio, and append to magnetAudio
|
||||||
|
for _, a := range audio {
|
||||||
|
if a != schema.AudioPortuguese {
|
||||||
|
magnetAudio = append(magnetAudio, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
magnetAudio = append(magnetAudio, audio...)
|
||||||
|
}
|
||||||
|
|
||||||
|
peer, seed, err := goscrape.GetLeechsAndSeeds(ctx, i.redis, infoHash, trackers)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
title := processTitle(title, magnetAudio)
|
||||||
|
|
||||||
|
// if the number of sizes is equal to the number of magnets, then assign the size to each indexed torrent in order
|
||||||
|
var mySize string
|
||||||
|
if len(size) == len(magnetLinks) {
|
||||||
|
mySize = size[it]
|
||||||
|
}
|
||||||
|
|
||||||
|
ixt := IndexedTorrent{
|
||||||
|
Title: appendAudioISO639_2Code(releaseTitle, magnetAudio),
|
||||||
|
OriginalTitle: title,
|
||||||
|
Details: link,
|
||||||
|
Year: year,
|
||||||
|
Audio: magnetAudio,
|
||||||
|
MagnetLink: magnetLink,
|
||||||
|
Date: date,
|
||||||
|
InfoHash: infoHash,
|
||||||
|
Trackers: trackers,
|
||||||
|
LeechCount: peer,
|
||||||
|
SeedCount: seed,
|
||||||
|
Size: mySize,
|
||||||
|
}
|
||||||
|
chanIndexedTorrent <- ixt
|
||||||
|
}(it, magnetLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(magnetLinks); i++ {
|
||||||
|
it := <-chanIndexedTorrent
|
||||||
|
indexedTorrents = append(indexedTorrents, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
return indexedTorrents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stableUniq(s []string) []string {
|
||||||
|
var uniq []map[string]interface{}
|
||||||
|
m := make(map[string]map[string]interface{})
|
||||||
|
for i, v := range s {
|
||||||
|
m[v] = map[string]interface{}{
|
||||||
|
"v": v,
|
||||||
|
"i": i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// to order by index
|
||||||
|
for _, v := range m {
|
||||||
|
uniq = append(uniq, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort by index
|
||||||
|
for i := 0; i < len(uniq); i++ {
|
||||||
|
for j := i + 1; j < len(uniq); j++ {
|
||||||
|
if uniq[i]["i"].(int) > uniq[j]["i"].(int) {
|
||||||
|
uniq[i], uniq[j] = uniq[j], uniq[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get only values
|
||||||
|
var uniqValues []string
|
||||||
|
for _, v := range uniq {
|
||||||
|
uniqValues = append(uniqValues, v["v"].(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniqValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func findYearFromText(text string, title string) (year string) {
|
||||||
|
re := regexp.MustCompile(`Lançamento: (.*)`)
|
||||||
|
yearMatch := re.FindStringSubmatch(text)
|
||||||
|
if len(yearMatch) > 0 {
|
||||||
|
year = yearMatch[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if year == "" {
|
||||||
|
re = regexp.MustCompile(`\((\d{4})\)`)
|
||||||
|
yearMatch := re.FindStringSubmatch(title)
|
||||||
|
if len(yearMatch) > 0 {
|
||||||
|
year = yearMatch[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return year
|
||||||
|
}
|
||||||
|
|
||||||
|
func findAudioFromText(text string) []schema.Audio {
|
||||||
|
var audio []schema.Audio
|
||||||
|
re := regexp.MustCompile(`(.udio|Idioma):.?(.*)`)
|
||||||
audioMatch := re.FindStringSubmatch(text)
|
audioMatch := re.FindStringSubmatch(text)
|
||||||
if len(audioMatch) > 0 {
|
if len(audioMatch) > 0 {
|
||||||
sep := getSeparator(audioMatch[2])
|
sep := getSeparator(audioMatch[2])
|
||||||
@@ -183,87 +309,36 @@ func getTorrents(ctx context.Context, i *Indexer, link string) ([]IndexedTorrent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return audio
|
||||||
re = regexp.MustCompile(`Lançamento: (.*)`)
|
|
||||||
yearMatch := re.FindStringSubmatch(text)
|
|
||||||
if len(yearMatch) > 0 {
|
|
||||||
year = yearMatch[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if year is empty, try to get it from title
|
func findSizesFromText(text string) []string {
|
||||||
if year == "" {
|
var sizes []string
|
||||||
re = regexp.MustCompile(`\((\d{4})\)`)
|
// everything that ends with GB or MB, using ',' or '.' as decimal separator
|
||||||
yearMatch := re.FindStringSubmatch(title)
|
re := regexp.MustCompile(`(\d+[\.,]?\d+) ?(GB|MB)`)
|
||||||
if len(yearMatch) > 0 {
|
sizesMatch := re.FindAllStringSubmatch(text, -1)
|
||||||
year = yearMatch[1]
|
if len(sizesMatch) > 0 {
|
||||||
|
for _, size := range sizesMatch {
|
||||||
|
sizes = append(sizes, size[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
return sizes
|
||||||
|
|
||||||
var chanIndexedTorrent = make(chan IndexedTorrent)
|
|
||||||
|
|
||||||
// for each magnet link, create a new indexed torrent
|
|
||||||
for _, magnetLink := range magnetLinks {
|
|
||||||
go func(magnetLink string) {
|
|
||||||
releaseTitle := extractReleaseName(magnetLink)
|
|
||||||
magnetAudio := []schema.Audio{}
|
|
||||||
if strings.Contains(strings.ToLower(releaseTitle), "dual") {
|
|
||||||
magnetAudio = append(magnetAudio, audio...)
|
|
||||||
} else if len(audio) > 1 {
|
|
||||||
// remove portuguese audio, and append to magnetAudio
|
|
||||||
for _, a := range audio {
|
|
||||||
if a != schema.AudioPortuguese {
|
|
||||||
magnetAudio = append(magnetAudio, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
magnetAudio = append(magnetAudio, audio...)
|
|
||||||
}
|
|
||||||
// decode url encoded title
|
|
||||||
releaseTitle, _ = url.QueryUnescape(releaseTitle)
|
|
||||||
|
|
||||||
infoHash := extractInfoHash(magnetLink)
|
|
||||||
trackers := extractTrackers(magnetLink)
|
|
||||||
peer, seed, err := goscrape.GetLeechsAndSeeds(ctx, i.redis, infoHash, trackers)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
title := processTitle(title, magnetAudio)
|
|
||||||
|
|
||||||
it := IndexedTorrent{
|
|
||||||
Title: releaseTitle,
|
|
||||||
OriginalTitle: title,
|
|
||||||
Details: link,
|
|
||||||
Year: year,
|
|
||||||
Audio: magnetAudio,
|
|
||||||
MagnetLink: magnetLink,
|
|
||||||
Date: date,
|
|
||||||
InfoHash: infoHash,
|
|
||||||
Trackers: trackers,
|
|
||||||
LeechCount: peer,
|
|
||||||
SeedCount: seed,
|
|
||||||
}
|
|
||||||
chanIndexedTorrent <- it
|
|
||||||
}(magnetLink)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(magnetLinks); i++ {
|
|
||||||
it := <-chanIndexedTorrent
|
|
||||||
indexedTorrents = append(indexedTorrents, it)
|
|
||||||
}
|
|
||||||
|
|
||||||
return indexedTorrents, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processTitle(title string, a []schema.Audio) string {
|
func processTitle(title string, a []schema.Audio) string {
|
||||||
// remove ' - Donwload' from title
|
// remove ' - Donwload' from title
|
||||||
title = strings.Replace(title, " - Download", "", -1)
|
title = strings.Replace(title, " – Download", "", -1)
|
||||||
|
|
||||||
// remove 'comando.la' from title
|
// remove 'comando.la' from title
|
||||||
title = strings.Replace(title, "comando.la", "", -1)
|
title = strings.Replace(title, "comando.la", "", -1)
|
||||||
|
|
||||||
// add audio ISO 639-2 code to title between ()
|
// add audio ISO 639-2 code to title between ()
|
||||||
|
title = appendAudioISO639_2Code(title, a)
|
||||||
|
|
||||||
|
return title
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendAudioISO639_2Code(title string, a []schema.Audio) string {
|
||||||
if len(a) > 0 {
|
if len(a) > 0 {
|
||||||
audio := []string{}
|
audio := []string{}
|
||||||
for _, lang := range a {
|
for _, lang := range a {
|
||||||
@@ -271,7 +346,6 @@ func processTitle(title string, a []schema.Audio) string {
|
|||||||
}
|
}
|
||||||
title = fmt.Sprintf("%s (%s)", title, strings.Join(audio, ", "))
|
title = fmt.Sprintf("%s (%s)", title, strings.Join(audio, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,33 +389,3 @@ func getDocument(ctx context.Context, i *Indexer, link string) (*goquery.Documen
|
|||||||
|
|
||||||
return doc, nil
|
return doc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractReleaseName(magnetLink string) string {
|
|
||||||
re := regexp.MustCompile(`dn=(.*?)&`)
|
|
||||||
matches := re.FindStringSubmatch(magnetLink)
|
|
||||||
if len(matches) > 0 {
|
|
||||||
return matches[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractInfoHash(magnetLink string) string {
|
|
||||||
re := regexp.MustCompile(`btih:(.*?)&`)
|
|
||||||
matches := re.FindStringSubmatch(magnetLink)
|
|
||||||
if len(matches) > 0 {
|
|
||||||
return matches[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractTrackers(magnetLink string) []string {
|
|
||||||
re := regexp.MustCompile(`tr=(.*?)&`)
|
|
||||||
matches := re.FindAllStringSubmatch(magnetLink, -1)
|
|
||||||
var trackers []string
|
|
||||||
for _, match := range matches {
|
|
||||||
// url decode
|
|
||||||
tracker, _ := url.QueryUnescape(match[1])
|
|
||||||
trackers = append(trackers, tracker)
|
|
||||||
}
|
|
||||||
return trackers
|
|
||||||
}
|
|
||||||
|
|||||||
79
api/comando_torrents_test.go
Normal file
79
api/comando_torrents_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/felipemarinho97/torrent-indexer/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_findAudioFromText(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want []schema.Audio
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should return audio in portuguese",
|
||||||
|
args: args{
|
||||||
|
text: "Áudio: Português",
|
||||||
|
},
|
||||||
|
want: []schema.Audio{
|
||||||
|
schema.AudioPortuguese,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should return audio in portuguese",
|
||||||
|
args: args{
|
||||||
|
text: "Idioma: Português",
|
||||||
|
},
|
||||||
|
want: []schema.Audio{
|
||||||
|
schema.AudioPortuguese,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should return audio in portuguese",
|
||||||
|
args: args{
|
||||||
|
text: "Audio: Português",
|
||||||
|
},
|
||||||
|
want: []schema.Audio{
|
||||||
|
schema.AudioPortuguese,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should return audio in portuguese",
|
||||||
|
args: args{
|
||||||
|
text: `
|
||||||
|
»INFORMAÇÕES«
|
||||||
|
Título Traduzido: O Cangaceiro do Futuro
|
||||||
|
Título Original: O Cangaceiro do Futuro
|
||||||
|
IMDb: 7,1
|
||||||
|
Gênero:Comédia
|
||||||
|
Lançamento: 2022
|
||||||
|
Qualidade: WEB-DL
|
||||||
|
Áudio: Português
|
||||||
|
Legenda: S/L
|
||||||
|
Formato: MKV
|
||||||
|
Tamanho: 5.77 GB | 9.60 GB
|
||||||
|
Duração: 30 Min./Ep.
|
||||||
|
Qualidade de Áudio: 10
|
||||||
|
Qualidade de Vídeo: 10
|
||||||
|
Servidor Via: Torrent
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
want: []schema.Audio{
|
||||||
|
schema.AudioPortuguese,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := findAudioFromText(tt.args.text); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("findAudioFromText() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
80
magnet/infohash.go
Normal file
80
magnet/infohash.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package magnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Size = 20
|
||||||
|
|
||||||
|
// 20-byte SHA1 hash used for info and pieces.
|
||||||
|
type T [Size]byte
|
||||||
|
|
||||||
|
var _ fmt.Formatter = (*T)(nil)
|
||||||
|
|
||||||
|
func (t T) Format(f fmt.State, c rune) {
|
||||||
|
// TODO: I can't figure out a nice way to just override the 'x' rune, since it's meaningless
|
||||||
|
// with the "default" 'v', or .String() already returning the hex.
|
||||||
|
f.Write([]byte(t.HexString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t T) Bytes() []byte {
|
||||||
|
return t[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t T) AsString() string {
|
||||||
|
return string(t[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t T) String() string {
|
||||||
|
return t.HexString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t T) HexString() string {
|
||||||
|
return fmt.Sprintf("%x", t[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *T) FromHexString(s string) (err error) {
|
||||||
|
if len(s) != 2*Size {
|
||||||
|
err = fmt.Errorf("hash hex string has bad length: %d", len(s))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err := hex.Decode(t[:], []byte(s))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n != Size {
|
||||||
|
panic(n)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ encoding.TextUnmarshaler = (*T)(nil)
|
||||||
|
_ encoding.TextMarshaler = T{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *T) UnmarshalText(b []byte) error {
|
||||||
|
return t.FromHexString(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t T) MarshalText() (text []byte, err error) {
|
||||||
|
return []byte(t.HexString()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromHexString(s string) (h T) {
|
||||||
|
err := h.FromHexString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func HashBytes(b []byte) (ret T) {
|
||||||
|
hasher := sha1.New()
|
||||||
|
hasher.Write(b)
|
||||||
|
copy(ret[:], hasher.Sum(nil))
|
||||||
|
return
|
||||||
|
}
|
||||||
93
magnet/magnet.go
Normal file
93
magnet/magnet.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package magnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Magnet link components.
|
||||||
|
type Magnet struct {
|
||||||
|
InfoHash T // Expected in this implementation
|
||||||
|
Trackers []string // "tr" values
|
||||||
|
DisplayName string // "dn" value, if not empty
|
||||||
|
Params url.Values // All other values, such as "x.pe", "as", "xs" etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
const xtPrefix = "urn:btih:"
|
||||||
|
|
||||||
|
// Deprecated: Use ParseMagnetUri.
|
||||||
|
var ParseMagnetURI = ParseMagnetUri
|
||||||
|
|
||||||
|
// ParseMagnetUri parses Magnet-formatted URIs into a Magnet instance
|
||||||
|
func ParseMagnetUri(uri string) (m Magnet, err error) {
|
||||||
|
u, err := url.Parse(uri)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error parsing uri: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if u.Scheme != "magnet" {
|
||||||
|
err = fmt.Errorf("unexpected scheme %q", u.Scheme)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
q := u.Query()
|
||||||
|
xt := q.Get("xt")
|
||||||
|
m.InfoHash, err = parseInfohash(q.Get("xt"))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error parsing infohash %q: %w", xt, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dropFirst(q, "xt")
|
||||||
|
m.DisplayName = q.Get("dn")
|
||||||
|
dropFirst(q, "dn")
|
||||||
|
m.Trackers = q["tr"]
|
||||||
|
delete(q, "tr")
|
||||||
|
if len(q) == 0 {
|
||||||
|
q = nil
|
||||||
|
}
|
||||||
|
m.Params = q
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInfohash(xt string) (ih T, err error) {
|
||||||
|
if !strings.HasPrefix(xt, xtPrefix) {
|
||||||
|
err = errors.New("bad xt parameter prefix")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
encoded := xt[len(xtPrefix):]
|
||||||
|
decode := func() func(dst, src []byte) (int, error) {
|
||||||
|
switch len(encoded) {
|
||||||
|
case 40:
|
||||||
|
return hex.Decode
|
||||||
|
case 32:
|
||||||
|
return base32.StdEncoding.Decode
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if decode == nil {
|
||||||
|
err = fmt.Errorf("unhandled xt parameter encoding (encoded length %d)", len(encoded))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err := decode(ih[:], []byte(encoded))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error decoding xt: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n != 20 {
|
||||||
|
panic(n)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func dropFirst(vs url.Values, key string) {
|
||||||
|
sl := vs[key]
|
||||||
|
switch len(sl) {
|
||||||
|
case 0, 1:
|
||||||
|
vs.Del(key)
|
||||||
|
default:
|
||||||
|
vs[key] = sl[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,41 +4,57 @@ type Audio string
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
AudioPortuguese = "Português"
|
AudioPortuguese = "Português"
|
||||||
|
AudioPortuguese2 = "Portugues"
|
||||||
AudioEnglish = "Inglês"
|
AudioEnglish = "Inglês"
|
||||||
|
AudioEnglish2 = "Ingles"
|
||||||
AudioSpanish = "Espanhol"
|
AudioSpanish = "Espanhol"
|
||||||
AudioFrench = "Francês"
|
AudioFrench = "Francês"
|
||||||
|
AudioFrench2 = "Frances"
|
||||||
AudioGerman = "Alemão"
|
AudioGerman = "Alemão"
|
||||||
|
AudioGerman2 = "Alemao"
|
||||||
AudioItalian = "Italiano"
|
AudioItalian = "Italiano"
|
||||||
AudioJapanese = "Japonês"
|
AudioJapanese = "Japonês"
|
||||||
|
AudioJapanese2 = "Japones"
|
||||||
AudioKorean = "Coreano"
|
AudioKorean = "Coreano"
|
||||||
AudioMandarin = "Mandarim"
|
AudioMandarin = "Mandarim"
|
||||||
AudioMandarin2 = "Chinês"
|
AudioMandarin2 = "Chinês"
|
||||||
|
AudioMandarin3 = "Chines"
|
||||||
AudioRussian = "Russo"
|
AudioRussian = "Russo"
|
||||||
AudioSwedish = "Sueco"
|
AudioSwedish = "Sueco"
|
||||||
AudioUkrainian = "Ucraniano"
|
AudioUkrainian = "Ucraniano"
|
||||||
AudioPolish = "Polaco"
|
AudioPolish = "Polaco"
|
||||||
AudioPolish2 = "Polonês"
|
AudioPolish2 = "Polonês"
|
||||||
|
AudioPolish3 = "Polones"
|
||||||
AudioThai = "Tailandês"
|
AudioThai = "Tailandês"
|
||||||
|
AudioThai2 = "Tailandes"
|
||||||
AudioTurkish = "Turco"
|
AudioTurkish = "Turco"
|
||||||
)
|
)
|
||||||
|
|
||||||
var AudioList = []Audio{
|
var AudioList = []Audio{
|
||||||
AudioPortuguese,
|
AudioPortuguese,
|
||||||
|
AudioPortuguese2,
|
||||||
AudioEnglish,
|
AudioEnglish,
|
||||||
|
AudioEnglish2,
|
||||||
AudioSpanish,
|
AudioSpanish,
|
||||||
AudioFrench,
|
AudioFrench,
|
||||||
|
AudioFrench2,
|
||||||
AudioGerman,
|
AudioGerman,
|
||||||
|
AudioGerman2,
|
||||||
AudioItalian,
|
AudioItalian,
|
||||||
AudioJapanese,
|
AudioJapanese,
|
||||||
|
AudioJapanese2,
|
||||||
AudioKorean,
|
AudioKorean,
|
||||||
AudioMandarin,
|
AudioMandarin,
|
||||||
AudioMandarin2,
|
AudioMandarin2,
|
||||||
|
AudioMandarin3,
|
||||||
AudioRussian,
|
AudioRussian,
|
||||||
AudioSwedish,
|
AudioSwedish,
|
||||||
AudioUkrainian,
|
AudioUkrainian,
|
||||||
AudioPolish,
|
AudioPolish,
|
||||||
AudioPolish2,
|
AudioPolish2,
|
||||||
|
AudioPolish3,
|
||||||
AudioThai,
|
AudioThai,
|
||||||
|
AudioThai2,
|
||||||
AudioTurkish,
|
AudioTurkish,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,24 +75,36 @@ func (a Audio) toISO639_2() string {
|
|||||||
switch a {
|
switch a {
|
||||||
case AudioPortuguese:
|
case AudioPortuguese:
|
||||||
return "por"
|
return "por"
|
||||||
|
case AudioPortuguese2:
|
||||||
|
return "por"
|
||||||
case AudioEnglish:
|
case AudioEnglish:
|
||||||
return "eng"
|
return "eng"
|
||||||
|
case AudioEnglish2:
|
||||||
|
return "eng"
|
||||||
case AudioSpanish:
|
case AudioSpanish:
|
||||||
return "spa"
|
return "spa"
|
||||||
case AudioFrench:
|
case AudioFrench:
|
||||||
return "fra"
|
return "fra"
|
||||||
|
case AudioFrench2:
|
||||||
|
return "fra"
|
||||||
case AudioGerman:
|
case AudioGerman:
|
||||||
return "deu"
|
return "deu"
|
||||||
|
case AudioGerman2:
|
||||||
|
return "deu"
|
||||||
case AudioItalian:
|
case AudioItalian:
|
||||||
return "ita"
|
return "ita"
|
||||||
case AudioJapanese:
|
case AudioJapanese:
|
||||||
return "jpn"
|
return "jpn"
|
||||||
|
case AudioJapanese2:
|
||||||
|
return "jpn"
|
||||||
case AudioKorean:
|
case AudioKorean:
|
||||||
return "kor"
|
return "kor"
|
||||||
case AudioMandarin:
|
case AudioMandarin:
|
||||||
return "chi"
|
return "chi"
|
||||||
case AudioMandarin2:
|
case AudioMandarin2:
|
||||||
return "chi"
|
return "chi"
|
||||||
|
case AudioMandarin3:
|
||||||
|
return "chi"
|
||||||
case AudioRussian:
|
case AudioRussian:
|
||||||
return "rus"
|
return "rus"
|
||||||
case AudioSwedish:
|
case AudioSwedish:
|
||||||
@@ -87,8 +115,12 @@ func (a Audio) toISO639_2() string {
|
|||||||
return "pol"
|
return "pol"
|
||||||
case AudioPolish2:
|
case AudioPolish2:
|
||||||
return "pol"
|
return "pol"
|
||||||
|
case AudioPolish3:
|
||||||
|
return "pol"
|
||||||
case AudioThai:
|
case AudioThai:
|
||||||
return "tha"
|
return "tha"
|
||||||
|
case AudioThai2:
|
||||||
|
return "tha"
|
||||||
case AudioTurkish:
|
case AudioTurkish:
|
||||||
return "tur"
|
return "tur"
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user