This commit is contained in:
2018-11-04 15:58:15 +01:00
commit f956bcee28
1178 changed files with 584552 additions and 0 deletions

58
ytsclient/genre.go Normal file
View File

@ -0,0 +1,58 @@
package ytsclient
type Genre string
const (
// Genre Enum
Action Genre = "Action"
Adventure Genre = "Adventure"
Animation Genre = "Animation"
Biography Genre = "Biography"
Comedy Genre = "Comedy"
Crime Genre = "Crime"
Documentary Genre = "Documentary"
Drama Genre = "Drama"
Family Genre = "Family"
Fantasy Genre = "Fantasy"
FilmNoir Genre = "Film Noir"
History Genre = "History"
Horror Genre = "Horror"
Music Genre = "Music"
Musical Genre = "Musical"
Mystery Genre = "Mystery"
Romance Genre = "Romance"
SciFi Genre = "Sci-Fi"
Short Genre = "Short"
Sport Genre = "Sport"
Superhero Genre = "Superhero"
Thriller Genre = "Thriller"
War Genre = "War"
Western Genre = "Western"
)
var Genres = []Genre{
Action,
Adventure,
Animation,
Biography,
Comedy,
Crime,
Documentary,
Drama,
Family,
Fantasy,
FilmNoir,
History,
Horror,
Music,
Musical,
Mystery,
Romance,
SciFi,
Short,
Sport,
Superhero,
Thriller,
War,
Western,
}

91
ytsclient/movie.go Normal file
View File

@ -0,0 +1,91 @@
package ytsclient
import (
"fmt"
"github.com/pkg/errors"
"net/url"
)
type Movie struct {
ID int `json:"id"`
URL string `json:"url"`
ImdbCode string `json:"imdb_code"`
Title string `json:"title"`
TitleEnglish string `json:"title_english"`
TitleLong string `json:"title_long"`
Slug string `json:"slug"`
Year int `json:"year"`
Rating float64 `json:"rating"`
Runtime int `json:"runtime"`
Genres []string `json:"genres"`
DownloadCount int `json:"download_count"`
LikeCount int `json:"like_count"`
State string `json:"state"`
Summary string `json:"summary"`
Synopsis string `json:"synopsis"`
DescriptionIntro string `json:"description_intro"`
DescriptionFull string `json:"description_full"`
YtTrailerCode string `json:"yt_trailer_code"`
Language string `json:"language"`
MpaRating string `json:"mpa_rating"`
BackgroundImage string `json:"background_image"`
BackgroundImageOriginal string `json:"background_image_original"`
SmallCoverImage string `json:"small_cover_image"`
MediumCoverImage string `json:"medium_cover_image"`
LargeCoverImage string `json:"large_cover_image"`
MediumScreenshotImage1 string `json:"medium_screenshot_image1"`
MediumScreenshotImage2 string `json:"medium_screenshot_image2"`
MediumScreenshotImage3 string `json:"medium_screenshot_image3"`
LargeScreenshotImage1 string `json:"large_screenshot_image1"`
LargeScreenshotImage2 string `json:"large_screenshot_image2"`
LargeScreenshotImage3 string `json:"large_screenshot_image3"`
Cast []Cast `json:"cast"`
Torrents []Torrent `json:"torrents"`
DateUploaded string `json:"date_uploaded"`
DateUploadedUnix int `json:"date_uploaded_unix"`
}
type Torrent struct {
URL string `json:"url"`
Hash string `json:"hash"`
Quality string `json:"quality"`
Seeds int `json:"seeds"`
Peers int `json:"peers"`
Size string `json:"size"`
SizeBytes int `json:"size_bytes"`
DateUploaded string `json:"date_uploaded"`
DateUploadedUnix int `json:"date_uploaded_unix"`
}
func (m *Movie) Magnet(t Torrent) (string, error) {
if !contains(m.Torrents, t) {
return "", errors.New("invalid torrent")
}
b := "magnet:?xt=urn:btih:%s&" +
"dn=%s&" +
"tr=udp://glotorrents.pw:6969/announce&" +
"re=udp://tracker.opentrackr.org:1337/announce&" +
"tr=udp://torrent.gresille.org:80/announce&" +
"tr=udp://tracker.openbittorrent.com:80&" +
"tr=udp://tracker.coppersurfer.tk:6969&" +
"tr=udp://tracker.leechers-paradise.org:6969&" +
"tr=udp://tracker.internetwarriors.net:1337"
title := fmt.Sprintf("%s (%d) [%s]", m.Title, m.Year, t.Quality)
return fmt.Sprintf(b, url.QueryEscape(t.Hash), url.QueryEscape(title)), nil
}
type Cast struct {
Name string `json:"name"`
CharacterName string `json:"character_name"`
URLSmallImage string `json:"url_small_image"`
ImdbCode string `json:"imdb_code"`
}
func contains(slice []Torrent, item Torrent) bool {
for _, i := range slice {
if i == item {
return true
}
}
return false
}

20
ytsclient/order.go Normal file
View File

@ -0,0 +1,20 @@
package ytsclient
type SortBy string
type Order string
const (
// OrderBy Enum
Title SortBy = "title"
Year SortBy = "year"
Rating SortBy = "rating"
Peers SortBy = "peers"
Seeds SortBy = "seeds"
DownloadCount SortBy = "downloadCount"
LikeCount SortBy = "likeCount"
DateAdded SortBy = "dateAdded"
// Order Enum
Asc Order = "asc"
Desc Order = "desc"
)

10
ytsclient/quality.go Normal file
View File

@ -0,0 +1,10 @@
package ytsclient
type Quality string
const (
// Quality Enum
Quality720p Quality = "720p"
Quality1080p Quality = "1080p"
Quality3D Quality = "3D"
)

32
ytsclient/reponses.go Normal file
View File

@ -0,0 +1,32 @@
package ytsclient
type ListResponse struct {
Status string `json:"status"`
StatusMessage string `json:"status_message"`
Data struct {
MovieCount int `json:"movie_count"`
Limit int `json:"limit"`
PageNumber int `json:"page_number"`
Movies []Movie `json:"movies"`
} `json:"data"`
Meta struct {
ServerTime int `json:"server_time"`
ServerTimezone string `json:"server_timezone"`
APIVersion int `json:"api_version"`
ExecutionTime string `json:"execution_time"`
} `json:"@meta"`
}
type MovieResponse struct {
Status string `json:"status"`
StatusMessage string `json:"status_message"`
Data struct {
Movie Movie `json:"movie"`
} `json:"data"`
Meta struct {
ServerTime int `json:"server_time"`
ServerTimezone string `json:"server_timezone"`
APIVersion int `json:"api_version"`
ExecutionTime string `json:"execution_time"`
} `json:"@meta"`
}

61
ytsclient/requests.go Normal file
View File

@ -0,0 +1,61 @@
package ytsclient
import (
r "github.com/levigross/grequests"
"strconv"
)
type ListParams struct {
Limit int
Page int
Quality Quality
MinimumRating int
QueryTerm string
Genre Genre
SortBy SortBy
OrderBy Order
WithRottenTomatoesRating bool
}
func (l *ListParams) format() *r.RequestOptions {
p := map[string]string{}
if l.Limit != 0 {
p["limit"] = strconv.Itoa(l.Limit)
}
if l.Page != 0 {
p["page"] = strconv.Itoa(l.Page)
}
if l.Quality != "" {
p["quality"] = string(l.Quality)
}
if l.MinimumRating != 0 {
p["minimum_rating"] = strconv.Itoa(l.MinimumRating)
}
if l.QueryTerm != "" {
p["query_term"] = l.QueryTerm
}
if l.Genre != "" {
p["genre"] = string(l.Genre)
}
if l.SortBy != "" {
p["sort_by"] = string(l.SortBy)
}
if l.Genre != "" {
p["order_by"] = string(l.Genre)
}
p["with_rt_ratings"] = strconv.FormatBool(l.WithRottenTomatoesRating)
return &r.RequestOptions{Params: p}
}
type MovieParams struct {
WithImages bool
WithCast bool
}
func (m *MovieParams) format() *r.RequestOptions {
p := map[string]string{
"with_images": strconv.FormatBool(m.WithImages),
"with_cast": strconv.FormatBool(m.WithCast),
}
return &r.RequestOptions{Params: p}
}

89
ytsclient/ytsclient.go Normal file
View File

@ -0,0 +1,89 @@
package ytsclient
import (
r "github.com/levigross/grequests"
"strconv"
)
type Client struct {
url string
list string
get string
suggestions string
}
const (
list = "/list_movies.json"
get = "/movie_details.json"
suggestions = "/movie_suggestions.json"
)
func NewClient(url string) *Client {
if url == "" {
url = "https://yts.am/api/v2"
}
return &Client{url, url + list, url + get, url + suggestions}
}
func (y *Client) List(params *ListParams) ([]Movie, error) {
if params == nil {
params = &ListParams{}
}
res, err := r.Get(y.list, params.format())
if err != nil {
return nil, err
}
var resp ListResponse
if err := res.JSON(&resp); err != nil {
return nil, err
}
return resp.Data.Movies, nil
}
func (y *Client) Search(search string, params *ListParams) ([]Movie, error) {
if params == nil {
params = &ListParams{}
}
params.QueryTerm = search
res, err := r.Get(y.list, params.format())
if err != nil {
return nil, err
}
var resp ListResponse
if err := res.JSON(&resp); err != nil {
return nil, err
}
return resp.Data.Movies, nil
}
func (y *Client) Get(id string, params *MovieParams) (Movie, error) {
if params == nil {
params = &MovieParams{}
}
p := params.format()
p.Params["movie_id"] = id
res, err := r.Get(y.get, p)
if err != nil {
return Movie{}, err
}
var resp MovieResponse
if err := res.JSON(&resp); err != nil {
return Movie{}, err
}
return resp.Data.Movie, nil
}
func (y *Client) Suggestions(id int) ([]Movie, error) {
ro := &r.RequestOptions{
Params: map[string]string{"movie_id": strconv.Itoa(id)},
}
res, err := r.Get(y.suggestions, ro)
if err != nil {
return nil, err
}
var resp ListResponse
if err := res.JSON(&resp); err != nil {
return nil, err
}
return resp.Data.Movies, nil
}

View File

@ -0,0 +1,91 @@
package ytsclient
import (
"github.com/stretchr/testify/assert"
"strconv"
"strings"
"testing"
)
var (
southParkID = "2806"
southParkMovie Movie
// Removed [YTS.AG] as it may change and does not matter
southParkMagnet = "magnet:?xt=urn:btih:04AC27DCF18C68470F35969517B477003C325301&" +
"dn=South+Park%3A+Bigger%2C+Longer+%26+Uncut+%281999%29+%5B1080p%5D&" +
"tr=udp%3A%2F%2Fglotorrents.pw%3A6969%2Fannounce&" +
"tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&" +
"tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&" +
"tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&" +
"tr=udp%3A%2F%2Fp4p.arenabg.ch%3A1337&" +
"tr=udp%3A%2F%2Ftracker.internetwarriors.net%3A1337"
tests = []struct {
name string
test func(t *testing.T)
}{{
name: "default url",
test: func(t *testing.T) {
c := NewClient("")
assert.Equal(t, "https://yts.am/api/v2", c.url)
},
}, {
name: "list",
test: func(t *testing.T) {
c := NewClient("")
_, err := c.List(nil)
assert.NoError(t, err)
},
}, {
name: "search",
test: func(t *testing.T) {
c := NewClient("")
l, err := c.Search("South Park", nil)
assert.NoError(t, err)
southParkMovie = l[0]
assert.Equal(t, southParkID, strconv.Itoa(southParkMovie.ID))
},
}, {
name: "list 50",
test: func(t *testing.T) {
c := NewClient("")
p := &ListParams{Limit: 50}
l, err := c.List(p)
assert.NoError(t, err)
assert.Equal(t, 50, len(l))
},
}, {
name: "get",
test: func(t *testing.T) {
c := NewClient("")
m, err := c.Get(southParkID, nil)
assert.NoError(t, err)
assert.NotEmpty(t, m)
assert.Equal(t, southParkMovie.Torrents, m.Torrents)
},
}, {
name: "magnet",
test: func(t *testing.T) {
m, err := southParkMovie.Magnet(southParkMovie.Torrents[1])
assert.NoError(t, err)
// Don't test trackers list nor YTS extension
e := strings.Split(southParkMagnet, "tr=")
a := strings.Split(m, "tr=")
assert.Equal(t, e[0], a[0])
},
}, {
name: "suggestions",
test: func(t *testing.T) {
c := NewClient("")
s, err := c.Suggestions(10)
assert.NoError(t, err)
assert.NotEmpty(t, s)
},
}}
)
func TestYTSClient(t *testing.T) {
for _, test := range tests {
t.Run(test.name, test.test)
}
}