-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
314e0ce
commit e43c8b5
Showing
5 changed files
with
204 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package hentaihd | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"html" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/gan-of-culture/get-sauce/request" | ||
"github.com/gan-of-culture/get-sauce/static" | ||
"github.com/gan-of-culture/get-sauce/utils" | ||
) | ||
|
||
type videoInfo struct { | ||
Success bool `json:"success"` | ||
Data []struct { | ||
File string `json:"file"` | ||
Label string `json:"label"` | ||
Type string `json:"type"` | ||
} `json:"data"` | ||
Captions []interface{} `json:"captions"` | ||
IsVr bool `json:"is_vr"` | ||
} | ||
|
||
const site = "https://v2.hentaihd.net/" | ||
const videoProvider = "https://amhentai.com/" | ||
const videoProviderAPI = videoProvider + "api/source/" | ||
|
||
var reEpisodeURL = regexp.MustCompile(site + `\d+/.+/`) | ||
var reParseURLShow = regexp.MustCompile(site + `anime/[\w-%]+/`) | ||
var reVideoURL = regexp.MustCompile(videoProvider + `v/([^"]+)`) | ||
|
||
type extractor struct{} | ||
|
||
// New returns a hentaihd extractor. | ||
func New() static.Extractor { | ||
return &extractor{} | ||
} | ||
|
||
// Extract data from URL | ||
func (e *extractor) Extract(URL string) ([]*static.Data, error) { | ||
URLs := parseURL(URL) | ||
if len(URLs) == 0 { | ||
return nil, static.ErrURLParseFailed | ||
} | ||
|
||
data := []*static.Data{} | ||
for _, u := range URLs { | ||
d, err := extractData(u) | ||
if err != nil { | ||
return nil, utils.Wrap(err, u) | ||
} | ||
data = append(data, d) | ||
} | ||
|
||
return data, nil | ||
} | ||
|
||
func parseURL(URL string) []string { | ||
if ok := reEpisodeURL.MatchString(URL); ok { | ||
return []string{URL} | ||
} | ||
|
||
htmlString, err := request.Get(URL) | ||
if err != nil { | ||
return nil | ||
} | ||
|
||
if strings.Contains(URL, "/anime/") { | ||
htmlString = strings.Split(htmlString, `<div class="bixbox"`)[0] | ||
return utils.RemoveAdjDuplicates(reEpisodeURL.FindAllString(htmlString, -1)) | ||
} | ||
|
||
// contains list of show that need to be derefenced to episode level | ||
htmlString = strings.Split(htmlString, `<div id="sidebar">`)[0] | ||
|
||
out := []string{} | ||
for _, anime := range reParseURLShow.FindAllString(htmlString, -1) { | ||
out = append(out, parseURL(anime)...) | ||
} | ||
return out | ||
|
||
} | ||
|
||
func extractData(URL string) (*static.Data, error) { | ||
htmlString, err := request.Get(URL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
matchedVideo := reVideoURL.FindStringSubmatch(htmlString) // 0=URL 1=ID | ||
if len(matchedVideo) < 2 { | ||
return nil, static.ErrDataSourceParseFailed | ||
} | ||
|
||
videoJSON, err := request.PostAsBytesWithHeaders(videoProviderAPI+matchedVideo[1], map[string]string{"Referer": matchedVideo[0]}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
video := videoInfo{} | ||
err = json.Unmarshal(videoJSON, &video) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
streams := map[string]*static.Stream{} | ||
for i, s := range video.Data { | ||
size, _ := request.Size(s.File, site) | ||
streams[fmt.Sprint(len(video.Data)-i-1)] = &static.Stream{ | ||
Type: static.DataTypeVideo, | ||
URLs: []*static.URL{ | ||
{ | ||
URL: s.File, | ||
Ext: s.Type, | ||
}, | ||
}, | ||
Quality: s.Label, | ||
Size: size, | ||
} | ||
} | ||
|
||
return &static.Data{ | ||
Site: site, | ||
Title: html.UnescapeString(utils.GetH1(&htmlString, -1)), | ||
Type: static.DataTypeVideo, | ||
Streams: streams, | ||
URL: URL, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package hentaihd | ||
|
||
import "testing" | ||
|
||
func TestParseURL(t *testing.T) { | ||
tests := []struct { | ||
Name string | ||
URL string | ||
Want int | ||
}{ | ||
{ | ||
Name: "Single Episode Raw", | ||
URL: "https://v2.hentaihd.net/74888/shuumatsu-no-harem-episode-1-raw/", | ||
Want: 1, | ||
}, { | ||
Name: "Single Episode Eng Sub", | ||
URL: "https://v2.hentaihd.net/74923/shuumatsu-no-harem-episode-1-english-subbed/", | ||
Want: 1, | ||
}, { | ||
Name: "Single Episode Preview", | ||
URL: "https://v2.hentaihd.net/74886/shuumatsu-no-harem-previews/", | ||
Want: 1, | ||
}, { | ||
Name: "Series", | ||
URL: "https://v2.hentaihd.net/anime/accelerando-datenshi-tachi-no-sasayaki/", | ||
Want: 11, | ||
}, { | ||
// this is the same logic for all extensions that group shows e.g. /genres/ | ||
// its hard to make a test for the other groups since the number of episodes always changes | ||
Name: "Studio", | ||
URL: "https://v2.hentaihd.net/studio/flavors-soft/", | ||
Want: 11, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.Name, func(t *testing.T) { | ||
URLs := parseURL(tt.URL) | ||
if len(URLs) > tt.Want || len(URLs) == 0 { | ||
t.Errorf("Got: %v - Want: %v", len(URLs), tt.Want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestExtract(t *testing.T) { | ||
tests := []struct { | ||
Name string | ||
URL string | ||
Want int | ||
}{ | ||
{ | ||
Name: "Single Episode", | ||
URL: "https://v2.hentaihd.net/77536/chizuru-chan-kaihatsu-nikki-episodio-6-en-espanol/", | ||
Want: 1, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.Name, func(t *testing.T) { | ||
data, err := New().Extract(tt.URL) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
if len(data) > tt.Want || len(data) == 0 { | ||
t.Errorf("Got: %v - Want: %v", len(data), tt.Want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters