node build fixed
This commit is contained in:
24
seanime-2.9.10/internal/onlinestream/sources/common.go
Normal file
24
seanime-2.9.10/internal/onlinestream/sources/common.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package onlinestream_sources
|
||||
|
||||
import (
|
||||
"errors"
|
||||
hibikeonlinestream "seanime/internal/extension/hibike/onlinestream"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoVideoSourceFound = errors.New("no episode source found")
|
||||
ErrVideoSourceExtraction = errors.New("error while extracting video sources")
|
||||
)
|
||||
|
||||
type VideoExtractor interface {
|
||||
Extract(uri string) ([]*hibikeonlinestream.VideoSource, error)
|
||||
}
|
||||
|
||||
const (
|
||||
QualityDefault = "default"
|
||||
QualityAuto = "auto"
|
||||
Quality360 = "360"
|
||||
Quality480 = "480"
|
||||
Quality720 = "720"
|
||||
Quality1080 = "1080"
|
||||
)
|
||||
277
seanime-2.9.10/internal/onlinestream/sources/gogocdn.go
Normal file
277
seanime-2.9.10/internal/onlinestream/sources/gogocdn.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package onlinestream_sources
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gocolly/colly"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"seanime/internal/util"
|
||||
"strings"
|
||||
|
||||
hibikeonlinestream "seanime/internal/extension/hibike/onlinestream"
|
||||
)
|
||||
|
||||
type cdnKeys struct {
|
||||
key []byte
|
||||
secondKey []byte
|
||||
iv []byte
|
||||
}
|
||||
|
||||
type GogoCDN struct {
|
||||
client *http.Client
|
||||
serverName string
|
||||
keys cdnKeys
|
||||
referrer string
|
||||
}
|
||||
|
||||
func NewGogoCDN() *GogoCDN {
|
||||
return &GogoCDN{
|
||||
client: &http.Client{},
|
||||
serverName: "goload",
|
||||
keys: cdnKeys{
|
||||
key: []byte("37911490979715163134003223491201"),
|
||||
secondKey: []byte("54674138327930866480207815084989"),
|
||||
iv: []byte("3134003223491201"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Extract fetches and extracts video sources from the provided URI.
|
||||
func (g *GogoCDN) Extract(uri string) (vs []*hibikeonlinestream.VideoSource, err error) {
|
||||
|
||||
defer util.HandlePanicInModuleThen("onlinestream/sources/gogocdn/Extract", func() {
|
||||
err = ErrVideoSourceExtraction
|
||||
})
|
||||
|
||||
// Instantiate a new collector
|
||||
c := colly.NewCollector(
|
||||
// Allow visiting the same page multiple times
|
||||
colly.AllowURLRevisit(),
|
||||
)
|
||||
ur, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Variables to hold extracted values
|
||||
var scriptValue, id string
|
||||
|
||||
id = ur.Query().Get("id")
|
||||
|
||||
// Find and extract the script value and id
|
||||
c.OnHTML("script[data-name='episode']", func(e *colly.HTMLElement) {
|
||||
scriptValue = e.Attr("data-value")
|
||||
|
||||
})
|
||||
|
||||
// Start scraping
|
||||
err = c.Visit(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if scriptValue and id are found
|
||||
if scriptValue == "" || id == "" {
|
||||
return nil, errors.New("script value or id not found")
|
||||
}
|
||||
|
||||
// Extract video sources
|
||||
ajaxUrl := fmt.Sprintf("%s://%s/encrypt-ajax.php?%s", ur.Scheme, ur.Host, g.generateEncryptedAjaxParams(id, scriptValue))
|
||||
|
||||
req, err := http.NewRequest("GET", ajaxUrl, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36")
|
||||
req.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01")
|
||||
|
||||
encryptedData, err := g.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer encryptedData.Body.Close()
|
||||
|
||||
encryptedDataBytesRes, err := io.ReadAll(encryptedData.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var encryptedDataBytes map[string]string
|
||||
err = json.Unmarshal(encryptedDataBytesRes, &encryptedDataBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := g.decryptAjaxData(encryptedDataBytes["data"])
|
||||
|
||||
source, ok := data["source"].([]interface{})
|
||||
|
||||
// Check if source is found
|
||||
if !ok {
|
||||
return nil, ErrNoVideoSourceFound
|
||||
}
|
||||
|
||||
var results []*hibikeonlinestream.VideoSource
|
||||
|
||||
urls := make([]string, 0)
|
||||
for _, src := range source {
|
||||
s := src.(map[string]interface{})
|
||||
urls = append(urls, s["file"].(string))
|
||||
}
|
||||
|
||||
sourceBK, ok := data["source_bk"].([]interface{})
|
||||
if ok {
|
||||
for _, src := range sourceBK {
|
||||
s := src.(map[string]interface{})
|
||||
urls = append(urls, s["file"].(string))
|
||||
}
|
||||
}
|
||||
|
||||
for _, url := range urls {
|
||||
|
||||
vs, ok := g.urlToVideoSource(url, source, sourceBK)
|
||||
if ok {
|
||||
results = append(results, vs...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (g *GogoCDN) urlToVideoSource(url string, source []interface{}, sourceBK []interface{}) (vs []*hibikeonlinestream.VideoSource, ok bool) {
|
||||
defer util.HandlePanicInModuleThen("onlinestream/sources/gogocdn/urlToVideoSource", func() {
|
||||
ok = false
|
||||
})
|
||||
ret := make([]*hibikeonlinestream.VideoSource, 0)
|
||||
if strings.Contains(url, ".m3u8") {
|
||||
resResult, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
defer resResult.Body.Close()
|
||||
|
||||
bodyBytes, err := io.ReadAll(resResult.Body)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
resolutions := regexp.MustCompile(`(RESOLUTION=)(.*)(\s*?)(\s.*)`).FindAllStringSubmatch(bodyString, -1)
|
||||
baseURL := url[:strings.LastIndex(url, "/")]
|
||||
|
||||
for _, res := range resolutions {
|
||||
quality := strings.Split(strings.Split(res[2], "x")[1], ",")[0]
|
||||
url := fmt.Sprintf("%s/%s", baseURL, strings.TrimSpace(res[4]))
|
||||
ret = append(ret, &hibikeonlinestream.VideoSource{URL: url, Type: hibikeonlinestream.VideoSourceM3U8, Quality: quality + "p"})
|
||||
}
|
||||
|
||||
ret = append(ret, &hibikeonlinestream.VideoSource{URL: url, Type: hibikeonlinestream.VideoSourceM3U8, Quality: "default"})
|
||||
} else {
|
||||
for _, src := range source {
|
||||
s := src.(map[string]interface{})
|
||||
if s["file"].(string) == url {
|
||||
quality := strings.Split(s["label"].(string), " ")[0] + "p"
|
||||
ret = append(ret, &hibikeonlinestream.VideoSource{URL: url, Type: hibikeonlinestream.VideoSourceMP4, Quality: quality})
|
||||
}
|
||||
}
|
||||
if sourceBK != nil {
|
||||
for _, src := range sourceBK {
|
||||
s := src.(map[string]interface{})
|
||||
if s["file"].(string) == url {
|
||||
ret = append(ret, &hibikeonlinestream.VideoSource{URL: url, Type: hibikeonlinestream.VideoSourceMP4, Quality: "backup"})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret, true
|
||||
}
|
||||
|
||||
// generateEncryptedAjaxParams generates encrypted AJAX parameters.
|
||||
func (g *GogoCDN) generateEncryptedAjaxParams(id, scriptValue string) string {
|
||||
encryptedKey := g.encrypt(id, g.keys.iv, g.keys.key)
|
||||
decryptedToken := g.decrypt(scriptValue, g.keys.iv, g.keys.key)
|
||||
return fmt.Sprintf("id=%s&alias=%s", encryptedKey, decryptedToken)
|
||||
}
|
||||
|
||||
// encrypt encrypts the given text using AES CBC mode.
|
||||
func (g *GogoCDN) encrypt(text string, iv []byte, key []byte) string {
|
||||
block, _ := aes.NewCipher(key)
|
||||
textBytes := []byte(text)
|
||||
textBytes = pkcs7Padding(textBytes, aes.BlockSize)
|
||||
cipherText := make([]byte, len(textBytes))
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(cipherText, textBytes)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(cipherText)
|
||||
}
|
||||
|
||||
// decrypt decrypts the given text using AES CBC mode.
|
||||
func (g *GogoCDN) decrypt(text string, iv []byte, key []byte) string {
|
||||
block, _ := aes.NewCipher(key)
|
||||
cipherText, _ := base64.StdEncoding.DecodeString(text)
|
||||
plainText := make([]byte, len(cipherText))
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(plainText, cipherText)
|
||||
plainText = pkcs7Trimming(plainText)
|
||||
|
||||
return string(plainText)
|
||||
}
|
||||
|
||||
func (g *GogoCDN) decryptAjaxData(encryptedData string) (map[string]interface{}, error) {
|
||||
decodedData, err := base64.StdEncoding.DecodeString(encryptedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(g.keys.secondKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(decodedData) < aes.BlockSize {
|
||||
return nil, fmt.Errorf("cipher text too short")
|
||||
}
|
||||
|
||||
iv := g.keys.iv
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(decodedData, decodedData)
|
||||
|
||||
// Remove padding
|
||||
decodedData = pkcs7Trimming(decodedData)
|
||||
|
||||
var data map[string]interface{}
|
||||
err = json.Unmarshal(decodedData, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// pkcs7Padding pads the text to be a multiple of blockSize using Pkcs7 padding.
|
||||
func pkcs7Padding(text []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(text)%blockSize
|
||||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(text, padText...)
|
||||
}
|
||||
|
||||
// pkcs7Trimming removes Pkcs7 padding from the text.
|
||||
func pkcs7Trimming(text []byte) []byte {
|
||||
length := len(text)
|
||||
unpadding := int(text[length-1])
|
||||
return text[:(length - unpadding)]
|
||||
}
|
||||
16
seanime-2.9.10/internal/onlinestream/sources/gogocdn_test.go
Normal file
16
seanime-2.9.10/internal/onlinestream/sources/gogocdn_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package onlinestream_sources
|
||||
|
||||
import (
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGogoCDN_Extract(t *testing.T) {
|
||||
gogo := NewGogoCDN()
|
||||
|
||||
ret, err := gogo.Extract("https://embtaku.pro/streaming.php?id=MjExNjU5&title=One+Piece+Episode+1075")
|
||||
assert.NoError(t, err)
|
||||
|
||||
spew.Dump(ret)
|
||||
}
|
||||
350
seanime-2.9.10/internal/onlinestream/sources/megacloud.go
Normal file
350
seanime-2.9.10/internal/onlinestream/sources/megacloud.go
Normal file
@@ -0,0 +1,350 @@
|
||||
package onlinestream_sources
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
hibikeonlinestream "seanime/internal/extension/hibike/onlinestream"
|
||||
"seanime/internal/util"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MegaCloud struct {
|
||||
Script string
|
||||
Sources string
|
||||
UserAgent string
|
||||
}
|
||||
|
||||
func NewMegaCloud() *MegaCloud {
|
||||
return &MegaCloud{
|
||||
Script: "https://megacloud.tv/js/player/a/prod/e1-player.min.js",
|
||||
Sources: "https://megacloud.tv/embed-2/ajax/e-1/getSources?id=",
|
||||
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MegaCloud) Extract(uri string) (vs []*hibikeonlinestream.VideoSource, err error) {
|
||||
defer util.HandlePanicInModuleThen("onlinestream/sources/megacloud/Extract", func() {
|
||||
err = ErrVideoSourceExtraction
|
||||
})
|
||||
|
||||
videoIdParts := strings.Split(uri, "/")
|
||||
videoId := videoIdParts[len(videoIdParts)-1]
|
||||
videoId = strings.Split(videoId, "?")[0]
|
||||
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("GET", m.Sources+videoId, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "*/*")
|
||||
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||||
req.Header.Set("User-Agent", m.UserAgent)
|
||||
req.Header.Set("Referer", uri)
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var srcData map[string]interface{}
|
||||
err = json.NewDecoder(res.Body).Decode(&srcData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subtitles := make([]*hibikeonlinestream.VideoSubtitle, 0)
|
||||
for idx, s := range srcData["tracks"].([]interface{}) {
|
||||
sub := s.(map[string]interface{})
|
||||
label, ok := sub["label"].(string)
|
||||
if ok {
|
||||
subtitle := &hibikeonlinestream.VideoSubtitle{
|
||||
URL: sub["file"].(string),
|
||||
ID: label,
|
||||
Language: label,
|
||||
IsDefault: idx == 0,
|
||||
}
|
||||
subtitles = append(subtitles, subtitle)
|
||||
}
|
||||
}
|
||||
if encryptedString, ok := srcData["sources"]; ok {
|
||||
|
||||
switch encryptedString.(type) {
|
||||
case []interface{}:
|
||||
if len(encryptedString.([]interface{})) == 0 {
|
||||
return nil, ErrNoVideoSourceFound
|
||||
}
|
||||
videoSources := make([]*hibikeonlinestream.VideoSource, 0)
|
||||
if e, ok := encryptedString.([]interface{})[0].(map[string]interface{}); ok {
|
||||
file, ok := e["file"].(string)
|
||||
if ok {
|
||||
videoSources = append(videoSources, &hibikeonlinestream.VideoSource{
|
||||
URL: file,
|
||||
Type: map[bool]hibikeonlinestream.VideoSourceType{true: hibikeonlinestream.VideoSourceM3U8, false: hibikeonlinestream.VideoSourceMP4}[strings.Contains(file, ".m3u8")],
|
||||
Subtitles: subtitles,
|
||||
Quality: QualityAuto,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(videoSources) == 0 {
|
||||
return nil, ErrNoVideoSourceFound
|
||||
}
|
||||
|
||||
return videoSources, nil
|
||||
|
||||
case []map[string]interface{}:
|
||||
if srcData["encrypted"].(bool) && ok {
|
||||
videoSources := make([]*hibikeonlinestream.VideoSource, 0)
|
||||
for _, e := range encryptedString.([]map[string]interface{}) {
|
||||
videoSources = append(videoSources, &hibikeonlinestream.VideoSource{
|
||||
URL: e["file"].(string),
|
||||
Type: map[bool]hibikeonlinestream.VideoSourceType{true: hibikeonlinestream.VideoSourceM3U8, false: hibikeonlinestream.VideoSourceMP4}[strings.Contains(e["file"].(string), ".m3u8")],
|
||||
Subtitles: subtitles,
|
||||
Quality: QualityAuto,
|
||||
})
|
||||
}
|
||||
if len(videoSources) == 0 {
|
||||
return nil, ErrNoVideoSourceFound
|
||||
}
|
||||
return videoSources, nil
|
||||
}
|
||||
case string:
|
||||
res, err = client.Get(m.Script)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
text, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("couldn't fetch script to decrypt resource")
|
||||
}
|
||||
|
||||
values, err := m.extractVariables(string(text))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secret, encryptedSource := m.getSecret(encryptedString.(string), values)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
decrypted, err := m.decrypt(encryptedSource, secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var decryptedData []map[string]interface{}
|
||||
err = json.Unmarshal([]byte(decrypted), &decryptedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sources := make([]*hibikeonlinestream.VideoSource, 0)
|
||||
for _, e := range decryptedData {
|
||||
sources = append(sources, &hibikeonlinestream.VideoSource{
|
||||
URL: e["file"].(string),
|
||||
Type: map[bool]hibikeonlinestream.VideoSourceType{true: hibikeonlinestream.VideoSourceM3U8, false: hibikeonlinestream.VideoSourceMP4}[strings.Contains(e["file"].(string), ".m3u8")],
|
||||
Subtitles: subtitles,
|
||||
Quality: QualityAuto,
|
||||
})
|
||||
}
|
||||
|
||||
if len(sources) == 0 {
|
||||
return nil, ErrNoVideoSourceFound
|
||||
}
|
||||
|
||||
return sources, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil, ErrNoVideoSourceFound
|
||||
}
|
||||
|
||||
func (m *MegaCloud) extractVariables(text string) ([][]int, error) {
|
||||
re := regexp.MustCompile(`case\s*0x[0-9a-f]+:\s*\w+\s*=\s*(\w+)\s*,\s*\w+\s*=\s*(\w+);`)
|
||||
matches := re.FindAllStringSubmatch(text, -1)
|
||||
|
||||
var vars [][]int
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
caseLine := match[0]
|
||||
if strings.Contains(caseLine, "partKey") {
|
||||
continue
|
||||
}
|
||||
|
||||
matchKey1, err1 := m.matchingKey(match[1], text)
|
||||
matchKey2, err2 := m.matchingKey(match[2], text)
|
||||
|
||||
if err1 != nil || err2 != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
key1, err1 := strconv.ParseInt(matchKey1, 16, 64)
|
||||
key2, err2 := strconv.ParseInt(matchKey2, 16, 64)
|
||||
|
||||
if err1 != nil || err2 != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
vars = append(vars, []int{int(key1), int(key2)})
|
||||
}
|
||||
|
||||
return vars, nil
|
||||
}
|
||||
|
||||
func (m *MegaCloud) matchingKey(value, script string) (string, error) {
|
||||
regexPattern := `,` + regexp.QuoteMeta(value) + `=((?:0x)?([0-9a-fA-F]+))`
|
||||
re := regexp.MustCompile(regexPattern)
|
||||
|
||||
match := re.FindStringSubmatch(script)
|
||||
if len(match) > 1 {
|
||||
return strings.TrimPrefix(match[1], "0x"), nil
|
||||
}
|
||||
|
||||
return "", errors.New("failed to match the key")
|
||||
}
|
||||
|
||||
func (m *MegaCloud) getSecret(encryptedString string, values [][]int) (string, string) {
|
||||
secret := ""
|
||||
encryptedSourceArray := strings.Split(encryptedString, "")
|
||||
currentIndex := 0
|
||||
|
||||
for _, index := range values {
|
||||
start := index[0] + currentIndex
|
||||
end := start + index[1]
|
||||
|
||||
for i := start; i < end; i++ {
|
||||
secret += string(encryptedString[i])
|
||||
encryptedSourceArray[i] = ""
|
||||
}
|
||||
|
||||
currentIndex += index[1]
|
||||
}
|
||||
|
||||
encryptedSource := strings.Join(encryptedSourceArray, "")
|
||||
|
||||
return secret, encryptedSource
|
||||
}
|
||||
|
||||
//func (m *MegaCloud) getSecret(encryptedString string, values []int) (string, string, error) {
|
||||
// var secret string
|
||||
// var encryptedSource = encryptedString
|
||||
// var totalInc int
|
||||
//
|
||||
// for i := 0; i < values[0]; i++ {
|
||||
// var start, inc int
|
||||
//
|
||||
// switch i {
|
||||
// case 0:
|
||||
// start = values[2]
|
||||
// inc = values[1]
|
||||
// case 1:
|
||||
// start = values[4]
|
||||
// inc = values[3]
|
||||
// case 2:
|
||||
// start = values[6]
|
||||
// inc = values[5]
|
||||
// case 3:
|
||||
// start = values[8]
|
||||
// inc = values[7]
|
||||
// case 4:
|
||||
// start = values[10]
|
||||
// inc = values[9]
|
||||
// case 5:
|
||||
// start = values[12]
|
||||
// inc = values[11]
|
||||
// case 6:
|
||||
// start = values[14]
|
||||
// inc = values[13]
|
||||
// case 7:
|
||||
// start = values[16]
|
||||
// inc = values[15]
|
||||
// case 8:
|
||||
// start = values[18]
|
||||
// inc = values[17]
|
||||
// default:
|
||||
// return "", "", errors.New("invalid index")
|
||||
// }
|
||||
//
|
||||
// from := start + totalInc
|
||||
// to := from + inc
|
||||
//
|
||||
// secret += encryptedString[from:to]
|
||||
// encryptedSource = strings.Replace(encryptedSource, encryptedString[from:to], "", 1)
|
||||
// totalInc += inc
|
||||
// }
|
||||
//
|
||||
// return secret, encryptedSource, nil
|
||||
//}
|
||||
|
||||
func (m *MegaCloud) decrypt(encrypted, keyOrSecret string) (string, error) {
|
||||
cypher, err := base64.StdEncoding.DecodeString(encrypted)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
salt := cypher[8:16]
|
||||
password := append([]byte(keyOrSecret), salt...)
|
||||
|
||||
md5Hashes := make([][]byte, 3)
|
||||
digest := password
|
||||
for i := 0; i < 3; i++ {
|
||||
hash := md5.Sum(digest)
|
||||
md5Hashes[i] = hash[:]
|
||||
digest = append(hash[:], password...)
|
||||
}
|
||||
|
||||
key := append(md5Hashes[0], md5Hashes[1]...)
|
||||
iv := md5Hashes[2]
|
||||
contents := cypher[16:]
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(contents, contents)
|
||||
|
||||
contents, err = pkcs7Unpad(contents, block.BlockSize())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(contents), nil
|
||||
}
|
||||
|
||||
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("invalid blocksize")
|
||||
}
|
||||
if len(data)%blockSize != 0 || len(data) == 0 {
|
||||
return nil, errors.New("invalid PKCS7 data (block size must be a multiple of input length)")
|
||||
}
|
||||
padLen := int(data[len(data)-1])
|
||||
if padLen > blockSize || padLen == 0 {
|
||||
return nil, errors.New("invalid PKCS7 padding")
|
||||
}
|
||||
for i := 0; i < padLen; i++ {
|
||||
if data[len(data)-1-i] != byte(padLen) {
|
||||
return nil, errors.New("invalid PKCS7 padding")
|
||||
}
|
||||
}
|
||||
return data[:len(data)-padLen], nil
|
||||
}
|
||||
111
seanime-2.9.10/internal/onlinestream/sources/streamsb.go
Normal file
111
seanime-2.9.10/internal/onlinestream/sources/streamsb.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package onlinestream_sources
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"seanime/internal/util"
|
||||
"strings"
|
||||
|
||||
hibikeonlinestream "seanime/internal/extension/hibike/onlinestream"
|
||||
)
|
||||
|
||||
type StreamSB struct {
|
||||
Host string
|
||||
Host2 string
|
||||
UserAgent string
|
||||
}
|
||||
|
||||
func NewStreamSB() *StreamSB {
|
||||
return &StreamSB{
|
||||
Host: "https://streamsss.net/sources50",
|
||||
Host2: "https://watchsb.com/sources50",
|
||||
UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StreamSB) Payload(hex string) string {
|
||||
return "566d337678566f743674494a7c7c" + hex + "7c7c346b6767586d6934774855537c7c73747265616d7362/6565417268755339773461447c7c346133383438333436313335376136323337373433383634376337633465366534393338373136643732373736343735373237613763376334363733353737303533366236333463353333363534366137633763373337343732363536313664373336327c7c6b586c3163614468645a47617c7c73747265616d7362"
|
||||
}
|
||||
|
||||
func (s *StreamSB) Extract(uri string) (vs []*hibikeonlinestream.VideoSource, err error) {
|
||||
|
||||
defer util.HandlePanicInModuleThen("onlinestream/sources/streamsb/Extract", func() {
|
||||
err = ErrVideoSourceExtraction
|
||||
})
|
||||
|
||||
var ret []*hibikeonlinestream.VideoSource
|
||||
|
||||
id := strings.Split(uri, "/e/")[1]
|
||||
if strings.Contains(id, "html") {
|
||||
id = strings.Split(id, ".html")[0]
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
return nil, errors.New("cannot find ID")
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", fmt.Sprintf("%s/%s", s.Host, s.Payload(hex.EncodeToString([]byte(id)))), nil)
|
||||
req.Header.Add("watchsb", "sbstream")
|
||||
req.Header.Add("User-Agent", s.UserAgent)
|
||||
req.Header.Add("Referer", uri)
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
|
||||
var jsonResponse map[string]interface{}
|
||||
err = json.Unmarshal(body, &jsonResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
streamData, ok := jsonResponse["stream_data"].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, ErrNoVideoSourceFound
|
||||
}
|
||||
|
||||
m3u8Urls, err := client.Get(streamData["file"].(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer m3u8Urls.Body.Close()
|
||||
|
||||
m3u8Body, err := io.ReadAll(m3u8Urls.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
videoList := strings.Split(string(m3u8Body), "#EXT-X-STREAM-INF:")
|
||||
|
||||
for _, video := range videoList {
|
||||
if !strings.Contains(video, "m3u8") {
|
||||
continue
|
||||
}
|
||||
|
||||
url := strings.Split(video, "\n")[1]
|
||||
quality := strings.Split(strings.Split(video, "RESOLUTION=")[1], ",")[0]
|
||||
quality = strings.Split(quality, "x")[1]
|
||||
|
||||
ret = append(ret, &hibikeonlinestream.VideoSource{
|
||||
URL: url,
|
||||
Quality: quality + "p",
|
||||
Type: hibikeonlinestream.VideoSourceM3U8,
|
||||
})
|
||||
}
|
||||
|
||||
ret = append(ret, &hibikeonlinestream.VideoSource{
|
||||
URL: streamData["file"].(string),
|
||||
Quality: "auto",
|
||||
Type: map[bool]hibikeonlinestream.VideoSourceType{true: hibikeonlinestream.VideoSourceM3U8, false: hibikeonlinestream.VideoSourceMP4}[strings.Contains(streamData["file"].(string), ".m3u8")],
|
||||
})
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
64
seanime-2.9.10/internal/onlinestream/sources/streamtape.go
Normal file
64
seanime-2.9.10/internal/onlinestream/sources/streamtape.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package onlinestream_sources
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"seanime/internal/util"
|
||||
"strings"
|
||||
|
||||
hibikeonlinestream "seanime/internal/extension/hibike/onlinestream"
|
||||
)
|
||||
|
||||
type (
|
||||
Streamtape struct {
|
||||
Client *http.Client
|
||||
}
|
||||
)
|
||||
|
||||
func NewStreamtape() *Streamtape {
|
||||
return &Streamtape{
|
||||
Client: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Streamtape) Extract(uri string) (vs []*hibikeonlinestream.VideoSource, err error) {
|
||||
defer util.HandlePanicInModuleThen("onlinestream/sources/streamtape/Extract", func() {
|
||||
err = ErrVideoSourceExtraction
|
||||
})
|
||||
|
||||
var ret []*hibikeonlinestream.VideoSource
|
||||
|
||||
resp, err := s.Client.Get(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`robotlink'\).innerHTML = (.*)'`)
|
||||
match := re.FindStringSubmatch(string(body))
|
||||
if len(match) == 0 {
|
||||
return nil, errors.New("could not find robotlink")
|
||||
}
|
||||
|
||||
fhsh := strings.Split(match[1], "+ ('")
|
||||
fh := fhsh[0]
|
||||
sh := fhsh[1][3:]
|
||||
|
||||
fh = strings.ReplaceAll(fh, "'", "")
|
||||
|
||||
url := "https:" + fh + sh
|
||||
|
||||
ret = append(ret, &hibikeonlinestream.VideoSource{
|
||||
URL: url,
|
||||
Type: map[bool]hibikeonlinestream.VideoSourceType{true: hibikeonlinestream.VideoSourceM3U8, false: hibikeonlinestream.VideoSourceMP4}[strings.Contains(url, ".m3u8")],
|
||||
Quality: QualityAuto,
|
||||
})
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
Reference in New Issue
Block a user