get all fields from apple podcast
This commit is contained in:
8
go.mod
8
go.mod
@@ -4,6 +4,11 @@ go 1.23
|
|||||||
|
|
||||||
require github.com/mmcdole/gofeed v1.3.0
|
require github.com/mmcdole/gofeed v1.3.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.8.0 // indirect
|
github.com/PuerkitoBio/goquery v1.8.0 // indirect
|
||||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||||
@@ -13,5 +18,6 @@ require (
|
|||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/stretchr/testify v1.8.4 // indirect
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
golang.org/x/net v0.4.0 // indirect
|
golang.org/x/net v0.4.0 // indirect
|
||||||
golang.org/x/text v0.5.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
gorm.io/gorm v1.25.12
|
||||||
)
|
)
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -6,6 +6,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
|
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
|
||||||
@@ -32,6 +36,10 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
|
|||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -10,7 +10,8 @@ func main() {
|
|||||||
|
|
||||||
provider := search.NewITunesProvider()
|
provider := search.NewITunesProvider()
|
||||||
|
|
||||||
podcast, err := provider.SearchPodcasts("golang", &search.SearchOptions{Country: "us", Language: "en-us"})
|
// podcast, err := provider.SearchPodcasts("golang", &search.SearchOptions{Country: "us", Language: "en-us"})
|
||||||
|
podcast, err := provider.Charts(&search.ChartsOptions{Country: "us", Language: "en-us", Limit: 200})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
126
model/chart_result.go
Normal file
126
model/chart_result.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Package model 提供播客和剧集的数据模型
|
||||||
|
package model
|
||||||
|
|
||||||
|
// ChartResult 表示Apple Podcasts排行榜结果
|
||||||
|
type ChartResult struct {
|
||||||
|
Feed Feed `json:"feed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed 表示排行榜的Feed信息
|
||||||
|
type Feed struct {
|
||||||
|
Author Author `json:"author"`
|
||||||
|
Entry []Entry `json:"entry"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author 表示Feed的作者信息
|
||||||
|
type Author struct {
|
||||||
|
Name NameLabel `json:"name"`
|
||||||
|
URI URILabel `json:"uri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameLabel 表示带标签的名称
|
||||||
|
type NameLabel struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// URILabel 表示带标签的URI
|
||||||
|
type URILabel struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry 表示排行榜中的一个播客条目
|
||||||
|
type Entry struct {
|
||||||
|
Name NameLabel `json:"im:name"`
|
||||||
|
Image []Image `json:"im:image"`
|
||||||
|
Summary NameLabel `json:"summary"`
|
||||||
|
Price Price `json:"im:price"`
|
||||||
|
ContentType ContentType `json:"im:contentType"`
|
||||||
|
Rights NameLabel `json:"rights"`
|
||||||
|
Title NameLabel `json:"title"`
|
||||||
|
Link Link `json:"link"`
|
||||||
|
ID ID `json:"id"`
|
||||||
|
Artist NameLabel `json:"im:artist"`
|
||||||
|
Category Category `json:"category"`
|
||||||
|
ReleaseDate ReleaseDate `json:"im:releaseDate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image 表示播客的图片信息
|
||||||
|
type Image struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes ImageAttribute `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageAttribute 表示图片的属性
|
||||||
|
type ImageAttribute struct {
|
||||||
|
Height string `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Price 表示播客的价格信息
|
||||||
|
type Price struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes PriceAttributes `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PriceAttributes 表示价格的属性
|
||||||
|
type PriceAttributes struct {
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentType 表示内容类型
|
||||||
|
type ContentType struct {
|
||||||
|
Attributes ContentTypeAttributes `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentTypeAttributes 表示内容类型的属性
|
||||||
|
type ContentTypeAttributes struct {
|
||||||
|
Term string `json:"term"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link 表示链接信息
|
||||||
|
type Link struct {
|
||||||
|
Attributes LinkAttributes `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkAttributes 表示链接的属性
|
||||||
|
type LinkAttributes struct {
|
||||||
|
Rel string `json:"rel"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Href string `json:"href"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID 表示播客的ID信息
|
||||||
|
type ID struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes IDAttributes `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDAttributes 表示ID的属性
|
||||||
|
type IDAttributes struct {
|
||||||
|
ID string `json:"im:id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category 表示播客的分类信息
|
||||||
|
type Category struct {
|
||||||
|
Attributes CategoryAttributes `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CategoryAttributes 表示分类的属性
|
||||||
|
type CategoryAttributes struct {
|
||||||
|
ID string `json:"im:id"`
|
||||||
|
Term string `json:"term"`
|
||||||
|
Scheme string `json:"scheme"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseDate 表示发布日期信息
|
||||||
|
type ReleaseDate struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes ReleaseDateAttributes `json:"attributes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseDateAttributes 表示发布日期的属性
|
||||||
|
type ReleaseDateAttributes struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
74
model/feed_entry.go
Normal file
74
model/feed_entry.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Package model 提供播客和剧集的数据模型
|
||||||
|
package model
|
||||||
|
|
||||||
|
// FeedEntry 表示 Apple Podcasts RSS Feed 中的一个条目
|
||||||
|
type FeedEntry struct {
|
||||||
|
Name struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"im:name"`
|
||||||
|
Image []struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes struct {
|
||||||
|
Height string `json:"height"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
} `json:"im:image"`
|
||||||
|
Summary struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"summary"`
|
||||||
|
Price struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes struct {
|
||||||
|
Amount string `json:"amount"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
} `json:"im:price"`
|
||||||
|
ContentType struct {
|
||||||
|
Attributes struct {
|
||||||
|
Term string `json:"term"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
} `json:"im:contentType"`
|
||||||
|
Rights struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"rights"`
|
||||||
|
Title struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"title"`
|
||||||
|
Link struct {
|
||||||
|
Attributes struct {
|
||||||
|
Rel string `json:"rel"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Href string `json:"href"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
} `json:"link"`
|
||||||
|
ID struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes struct {
|
||||||
|
ID string `json:"im:id"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
} `json:"id"`
|
||||||
|
Artist struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"im:artist"`
|
||||||
|
Category struct {
|
||||||
|
Attributes struct {
|
||||||
|
ID string `json:"im:id"`
|
||||||
|
Term string `json:"term"`
|
||||||
|
Scheme string `json:"scheme"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
} `json:"category"`
|
||||||
|
ReleaseDate struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Attributes struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
} `json:"attributes"`
|
||||||
|
} `json:"im:releaseDate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeedResponse 表示 Apple Podcasts RSS Feed 的响应
|
||||||
|
type FeedResponse struct {
|
||||||
|
Feed struct {
|
||||||
|
Entry []FeedEntry `json:"entry"`
|
||||||
|
} `json:"feed"`
|
||||||
|
}
|
||||||
21
model/lookup_result.go
Normal file
21
model/lookup_result.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// Package model 提供播客和剧集的数据模型
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SearchResult2 表示iTunes API搜索或查询的结果
|
||||||
|
type LookupResult struct {
|
||||||
|
// 结果数量
|
||||||
|
ResultCount int `json:"resultCount"`
|
||||||
|
|
||||||
|
// 搜索是否成功
|
||||||
|
Successful bool `json:"successful,omitempty"`
|
||||||
|
|
||||||
|
// 搜索结果项目列表
|
||||||
|
Items []ApplePodcast `json:"results"`
|
||||||
|
|
||||||
|
// 处理时间
|
||||||
|
ProcessedTime time.Time `json:"processedTime,omitempty"`
|
||||||
|
}
|
||||||
@@ -43,3 +43,18 @@ type SearchResult struct {
|
|||||||
// 处理时间
|
// 处理时间
|
||||||
ProcessedTime time.Time `json:"processedTime,omitempty"`
|
ProcessedTime time.Time `json:"processedTime,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchResult 表示播客搜索的结果
|
||||||
|
type SearchResult2 struct {
|
||||||
|
// 结果数量
|
||||||
|
ResultCount int `json:"resultCount,omitempty"`
|
||||||
|
|
||||||
|
// 搜索是否成功
|
||||||
|
Successful bool `json:"successful,omitempty"`
|
||||||
|
|
||||||
|
// 搜索结果项目列表
|
||||||
|
Items []ApplePodcast `json:"items,omitempty"`
|
||||||
|
|
||||||
|
// 处理时间
|
||||||
|
ProcessedTime time.Time `json:"processedTime,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ type ChartsOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SearchPodcasts 搜索播客
|
// SearchPodcasts 搜索播客
|
||||||
func (p *ITunesProvider) SearchPodcasts(term string, options *SearchOptions) (*model.SearchResult, error) {
|
func (p *ITunesProvider) SearchPodcasts(term string, options *SearchOptions) (*model.SearchResult2, error) {
|
||||||
if term == "" {
|
if term == "" {
|
||||||
return nil, fmt.Errorf("搜索词不能为空")
|
return nil, fmt.Errorf("搜索词不能为空")
|
||||||
}
|
}
|
||||||
@@ -178,76 +178,22 @@ func (p *ITunesProvider) SearchPodcasts(term string, options *SearchOptions) (*m
|
|||||||
fmt.Printf("body: %s\n", string(body))
|
fmt.Printf("body: %s\n", string(body))
|
||||||
|
|
||||||
// 解析JSON响应
|
// 解析JSON响应
|
||||||
var response struct {
|
// var response struct {
|
||||||
ResultCount int `json:"resultCount"`
|
// ResultCount int `json:"resultCount"`
|
||||||
Results []map[string]interface{} `json:"results"`
|
// Results []model.ApplePodcast `json:"results"`
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := json.Unmarshal(body, &response); err != nil {
|
|
||||||
return nil, fmt.Errorf("解析响应失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建搜索结果
|
// 创建搜索结果
|
||||||
result := &model.SearchResult{
|
result := &model.SearchResult2{
|
||||||
ResultCount: response.ResultCount,
|
Items: []model.ApplePodcast{},
|
||||||
Successful: true,
|
|
||||||
Items: make([]model.Item, 0, len(response.Results)),
|
|
||||||
ProcessedTime: time.Now(),
|
ProcessedTime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析结果
|
if err := json.Unmarshal(body, &result); err != nil {
|
||||||
for _, itemData := range response.Results {
|
return nil, fmt.Errorf("解析响应失败: %w", err)
|
||||||
item := model.Item{}
|
|
||||||
|
|
||||||
// 提取基本信息
|
|
||||||
if v, ok := itemData["collectionId"]; ok {
|
|
||||||
if id, ok := v.(float64); ok {
|
|
||||||
item.CollectionID = int(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["collectionName"]; ok {
|
|
||||||
if name, ok := v.(string); ok {
|
|
||||||
item.Title = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["artistName"]; ok {
|
|
||||||
if name, ok := v.(string); ok {
|
|
||||||
item.Author = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["feedUrl"]; ok {
|
|
||||||
if url, ok := v.(string); ok {
|
|
||||||
item.FeedURL = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["artworkUrl100"]; ok {
|
|
||||||
if url, ok := v.(string); ok {
|
|
||||||
item.ArtworkURL = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["artworkUrl600"]; ok {
|
|
||||||
if url, ok := v.(string); ok {
|
|
||||||
item.ArtworkURL600 = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["genres"]; ok {
|
|
||||||
if genres, ok := v.([]interface{}); ok {
|
|
||||||
for _, g := range genres {
|
|
||||||
if genre, ok := g.(string); ok {
|
|
||||||
item.Genres = append(item.Genres, genre)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Items = append(result.Items, item)
|
|
||||||
}
|
}
|
||||||
|
result.ResultCount = len(result.Items)
|
||||||
|
result.Successful = true
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
@@ -256,9 +202,10 @@ func (p *ITunesProvider) SearchPodcasts(term string, options *SearchOptions) (*m
|
|||||||
// 可选参数包括国家、语言、结果数量限制、是否包含限制内容和流派
|
// 可选参数包括国家、语言、结果数量限制、是否包含限制内容和流派
|
||||||
// 由于热门播客列表以feed形式返回,为了与SearchResult兼容,需要解析feed并获取每个项目的底层结果
|
// 由于热门播客列表以feed形式返回,为了与SearchResult兼容,需要解析feed并获取每个项目的底层结果
|
||||||
// 这会导致每个结果都有一个HTTP调用,鉴于热门播客列表更新不频繁,建议客户端缓存结果
|
// 这会导致每个结果都有一个HTTP调用,鉴于热门播客列表更新不频繁,建议客户端缓存结果
|
||||||
func (p *ITunesProvider) Charts(options *ChartsOptions) (*model.SearchResult, error) {
|
func (p *ITunesProvider) Charts(options *ChartsOptions) (*model.SearchResult2, error) {
|
||||||
// 构建URL
|
// 构建URL
|
||||||
url := p.buildChartsURL(options)
|
url := p.buildChartsURL(options)
|
||||||
|
fmt.Printf("url: %s\n", url)
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
@@ -286,55 +233,29 @@ func (p *ITunesProvider) Charts(options *ChartsOptions) (*model.SearchResult, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("读取响应失败: %w", err)
|
return nil, fmt.Errorf("读取响应失败: %w", err)
|
||||||
}
|
}
|
||||||
|
// fmt.Printf("body: %s\n", string(body)[:3000])
|
||||||
|
// fmt.Printf("body: %s\n", string(body))
|
||||||
|
|
||||||
// 解析JSON响应
|
var chartResult model.ChartResult
|
||||||
var response struct {
|
if err := json.Unmarshal(body, &chartResult); err != nil {
|
||||||
Feed struct {
|
|
||||||
Entry []map[string]interface{} `json:"entry"`
|
|
||||||
} `json:"feed"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(body, &response); err != nil {
|
|
||||||
return nil, fmt.Errorf("解析响应失败: %w", err)
|
return nil, fmt.Errorf("解析响应失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建搜索结果
|
// 创建搜索结果
|
||||||
result := &model.SearchResult{
|
result := &model.SearchResult2{
|
||||||
Successful: true,
|
Items: make([]model.ApplePodcast, 0),
|
||||||
Items: make([]model.Item, 0),
|
|
||||||
ProcessedTime: time.Now(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析结果
|
// 解析结果
|
||||||
if response.Feed.Entry != nil {
|
if chartResult.Feed.Entry != nil {
|
||||||
for _, entry := range response.Feed.Entry {
|
for _, entry := range chartResult.Feed.Entry {
|
||||||
// 获取播客ID
|
// 获取播客ID
|
||||||
var id string
|
id := entry.ID.Attributes.ID
|
||||||
if idObj, ok := entry["id"]; ok {
|
|
||||||
if idMap, ok := idObj.(map[string]interface{}); ok {
|
|
||||||
if attrs, ok := idMap["attributes"]; ok {
|
|
||||||
if attrsMap, ok := attrs.(map[string]interface{}); ok {
|
|
||||||
if imID, ok := attrsMap["im:id"]; ok {
|
|
||||||
id = fmt.Sprintf("%v", imID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if id == "" {
|
if id == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取播客标题
|
|
||||||
// 不再使用title变量,直接在需要时获取
|
|
||||||
if titleObj, ok := entry["title"]; ok {
|
|
||||||
// 移除未使用的titleMap变量
|
|
||||||
if _, ok := titleObj.(map[string]interface{}); ok {
|
|
||||||
// 处理标题信息,如果需要使用
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 为每个播客获取详细信息
|
// 为每个播客获取详细信息
|
||||||
lookupURL := fmt.Sprintf("%s/lookup?id=%s", p.FeedAPIEndpoint, id)
|
lookupURL := fmt.Sprintf("%s/lookup?id=%s", p.FeedAPIEndpoint, id)
|
||||||
lookupReq, err := http.NewRequest("GET", lookupURL, nil)
|
lookupReq, err := http.NewRequest("GET", lookupURL, nil)
|
||||||
@@ -358,73 +279,21 @@ func (p *ITunesProvider) Charts(options *ChartsOptions) (*model.SearchResult, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
fmt.Printf("lookupBody: %s\n", string(lookupBody))
|
||||||
|
|
||||||
var lookupResponse struct {
|
var tempResult model.LookupResult
|
||||||
ResultCount int `json:"resultCount"`
|
if err := json.Unmarshal(lookupBody, &tempResult); err != nil {
|
||||||
Results []map[string]interface{} `json:"results"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(lookupBody, &lookupResponse); err != nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if lookupResponse.ResultCount > 0 && len(lookupResponse.Results) > 0 {
|
if tempResult.ResultCount > 0 && len(tempResult.Items) > 0 {
|
||||||
itemData := lookupResponse.Results[0]
|
result.Items = append(result.Items, tempResult.Items[0])
|
||||||
item := model.Item{}
|
|
||||||
|
|
||||||
// 提取基本信息
|
|
||||||
if v, ok := itemData["collectionId"]; ok {
|
|
||||||
if id, ok := v.(float64); ok {
|
|
||||||
item.CollectionID = int(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["collectionName"]; ok {
|
|
||||||
if name, ok := v.(string); ok {
|
|
||||||
item.Title = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["artistName"]; ok {
|
|
||||||
if name, ok := v.(string); ok {
|
|
||||||
item.Author = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["feedUrl"]; ok {
|
|
||||||
if url, ok := v.(string); ok {
|
|
||||||
item.FeedURL = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["artworkUrl100"]; ok {
|
|
||||||
if url, ok := v.(string); ok {
|
|
||||||
item.ArtworkURL = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["artworkUrl600"]; ok {
|
|
||||||
if url, ok := v.(string); ok {
|
|
||||||
item.ArtworkURL600 = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := itemData["genres"]; ok {
|
|
||||||
if genres, ok := v.([]interface{}); ok {
|
|
||||||
for _, g := range genres {
|
|
||||||
if genre, ok := g.(string); ok {
|
|
||||||
item.Genres = append(item.Genres, genre)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Items = append(result.Items, item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result.ResultCount = len(result.Items)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.ResultCount = len(result.Items)
|
// result.ResultCount = len(result.Items)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,7 +324,7 @@ func (p *ITunesProvider) buildChartsURL(options *ChartsOptions) string {
|
|||||||
if options.Limit > 0 {
|
if options.Limit > 0 {
|
||||||
buf += strconv.Itoa(options.Limit)
|
buf += strconv.Itoa(options.Limit)
|
||||||
} else {
|
} else {
|
||||||
buf += "20"
|
buf += "200"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加流派
|
// 添加流派
|
||||||
@@ -468,9 +337,9 @@ func (p *ITunesProvider) buildChartsURL(options *ChartsOptions) string {
|
|||||||
// 添加限制内容设置
|
// 添加限制内容设置
|
||||||
buf += "/explicit="
|
buf += "/explicit="
|
||||||
if options.Explicit {
|
if options.Explicit {
|
||||||
buf += "Yes"
|
buf += "true"
|
||||||
} else {
|
} else {
|
||||||
buf += "No"
|
buf += "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加JSON格式
|
// 添加JSON格式
|
||||||
|
|||||||
@@ -1,331 +0,0 @@
|
|||||||
package search
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSearchPodcasts_EmptyTerm(t *testing.T) {
|
|
||||||
// 创建iTunes搜索提供者
|
|
||||||
provider := NewITunesProvider()
|
|
||||||
|
|
||||||
// 使用空搜索词调用SearchPodcasts
|
|
||||||
result, err := provider.SearchPodcasts("", nil)
|
|
||||||
|
|
||||||
// 验证返回错误
|
|
||||||
if err == nil {
|
|
||||||
t.Error("期望返回错误,但没有")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证错误消息
|
|
||||||
expectedErrMsg := "搜索词不能为空"
|
|
||||||
if err.Error() != expectedErrMsg {
|
|
||||||
t.Errorf("期望错误消息为 %q,但得到 %q", expectedErrMsg, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果为nil
|
|
||||||
if result != nil {
|
|
||||||
t.Errorf("期望结果为nil,但得到 %+v", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchPodcasts_SuccessfulSearch(t *testing.T) {
|
|
||||||
// 创建测试服务器
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// 验证请求方法
|
|
||||||
if r.Method != http.MethodGet {
|
|
||||||
t.Errorf("期望请求方法为GET,但得到 %s", r.Method)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证请求路径
|
|
||||||
if r.URL.Path != "/search" {
|
|
||||||
t.Errorf("期望请求路径为/search,但得到 %s", r.URL.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证查询参数
|
|
||||||
query := r.URL.Query()
|
|
||||||
if query.Get("term") != "golang" {
|
|
||||||
t.Errorf("期望term参数为golang,但得到 %s", query.Get("term"))
|
|
||||||
}
|
|
||||||
if query.Get("entity") != "podcast" {
|
|
||||||
t.Errorf("期望entity参数为podcast,但得到 %s", query.Get("entity"))
|
|
||||||
}
|
|
||||||
if query.Get("media") != "podcast" {
|
|
||||||
t.Errorf("期望media参数为podcast,但得到 %s", query.Get("media"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回模拟响应
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
|
|
||||||
// 创建模拟响应数据
|
|
||||||
response := map[string]interface{}{
|
|
||||||
"resultCount": 2,
|
|
||||||
"results": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"collectionId": 1234567890,
|
|
||||||
"collectionName": "Go编程语言",
|
|
||||||
"artistName": "Go开发者",
|
|
||||||
"feedUrl": "https://example.com/feed1",
|
|
||||||
"artworkUrl100": "https://example.com/artwork1_100.jpg",
|
|
||||||
"artworkUrl600": "https://example.com/artwork1_600.jpg",
|
|
||||||
"genres": []string{"Technology", "Education"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"collectionId": 9876543210,
|
|
||||||
"collectionName": "Golang讨论",
|
|
||||||
"artistName": "Golang爱好者",
|
|
||||||
"feedUrl": "https://example.com/feed2",
|
|
||||||
"artworkUrl100": "https://example.com/artwork2_100.jpg",
|
|
||||||
"artworkUrl600": "https://example.com/artwork2_600.jpg",
|
|
||||||
"genres": []string{"Technology"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将响应数据编码为JSON并写入响应
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
// 创建iTunes搜索提供者,并设置测试服务器URL
|
|
||||||
provider := &ITunesProvider{
|
|
||||||
SearchAPIEndpoint: server.URL,
|
|
||||||
FeedAPIEndpoint: "https://itunes.apple.com",
|
|
||||||
GenresMap: initITunesGenres(),
|
|
||||||
Client: &http.Client{Timeout: 20 * time.Second},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用SearchPodcasts
|
|
||||||
result, err := provider.SearchPodcasts("golang", nil)
|
|
||||||
|
|
||||||
// 验证没有错误
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("期望没有错误,但得到 %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果
|
|
||||||
if result == nil {
|
|
||||||
t.Fatal("期望结果不为nil,但得到nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果计数
|
|
||||||
if result.ResultCount != 2 {
|
|
||||||
t.Errorf("期望结果计数为2,但得到 %d", result.ResultCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果成功标志
|
|
||||||
if !result.Successful {
|
|
||||||
t.Error("期望结果成功标志为true,但得到false")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果项目数量
|
|
||||||
if len(result.Items) != 2 {
|
|
||||||
t.Errorf("期望结果项目数量为2,但得到 %d", len(result.Items))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证第一个结果项目
|
|
||||||
item1 := result.Items[0]
|
|
||||||
if item1.CollectionID != 1234567890 {
|
|
||||||
t.Errorf("期望第一个项目的CollectionID为1234567890,但得到 %d", item1.CollectionID)
|
|
||||||
}
|
|
||||||
if item1.Title != "Go编程语言" {
|
|
||||||
t.Errorf("期望第一个项目的Title为'Go编程语言',但得到 %s", item1.Title)
|
|
||||||
}
|
|
||||||
if item1.Author != "Go开发者" {
|
|
||||||
t.Errorf("期望第一个项目的Author为'Go开发者',但得到 %s", item1.Author)
|
|
||||||
}
|
|
||||||
if item1.FeedURL != "https://example.com/feed1" {
|
|
||||||
t.Errorf("期望第一个项目的FeedURL为'https://example.com/feed1',但得到 %s", item1.FeedURL)
|
|
||||||
}
|
|
||||||
if item1.ArtworkURL != "https://example.com/artwork1_100.jpg" {
|
|
||||||
t.Errorf("期望第一个项目的ArtworkURL为'https://example.com/artwork1_100.jpg',但得到 %s", item1.ArtworkURL)
|
|
||||||
}
|
|
||||||
if item1.ArtworkURL600 != "https://example.com/artwork1_600.jpg" {
|
|
||||||
t.Errorf("期望第一个项目的ArtworkURL600为'https://example.com/artwork1_600.jpg',但得到 %s", item1.ArtworkURL600)
|
|
||||||
}
|
|
||||||
if len(item1.Genres) != 2 || item1.Genres[0] != "Technology" || item1.Genres[1] != "Education" {
|
|
||||||
t.Errorf("期望第一个项目的Genres为[Technology, Education],但得到 %v", item1.Genres)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证第二个结果项目
|
|
||||||
item2 := result.Items[1]
|
|
||||||
if item2.CollectionID != 9876543210 {
|
|
||||||
t.Errorf("期望第二个项目的CollectionID为9876543210,但得到 %d", item2.CollectionID)
|
|
||||||
}
|
|
||||||
if item2.Title != "Golang讨论" {
|
|
||||||
t.Errorf("期望第二个项目的Title为'Golang讨论',但得到 %s", item2.Title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchPodcasts_WithOptions(t *testing.T) {
|
|
||||||
// 创建测试服务器
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// 验证查询参数
|
|
||||||
query := r.URL.Query()
|
|
||||||
|
|
||||||
// 验证基本参数
|
|
||||||
if query.Get("term") != "golang" {
|
|
||||||
t.Errorf("期望term参数为golang,但得到 %s", query.Get("term"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证选项参数
|
|
||||||
if query.Get("country") != "cn" {
|
|
||||||
t.Errorf("期望country参数为cn,但得到 %s", query.Get("country"))
|
|
||||||
}
|
|
||||||
if query.Get("genreId") != "1318" { // Technology的ID
|
|
||||||
t.Errorf("期望genreId参数为1318,但得到 %s", query.Get("genreId"))
|
|
||||||
}
|
|
||||||
if query.Get("attribute") != "titleTerm" {
|
|
||||||
t.Errorf("期望attribute参数为titleTerm,但得到 %s", query.Get("attribute"))
|
|
||||||
}
|
|
||||||
if query.Get("limit") != "10" {
|
|
||||||
t.Errorf("期望limit参数为10,但得到 %s", query.Get("limit"))
|
|
||||||
}
|
|
||||||
if query.Get("language") != "zh-cn" {
|
|
||||||
t.Errorf("期望language参数为zh-cn,但得到 %s", query.Get("language"))
|
|
||||||
}
|
|
||||||
if query.Get("explicit") != "No" {
|
|
||||||
t.Errorf("期望explicit参数为No,但得到 %s", query.Get("explicit"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 返回模拟响应
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
|
|
||||||
// 创建模拟响应数据
|
|
||||||
response := map[string]interface{}{
|
|
||||||
"resultCount": 1,
|
|
||||||
"results": []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"collectionId": 1234567890,
|
|
||||||
"collectionName": "Go编程语言",
|
|
||||||
"artistName": "Go开发者",
|
|
||||||
"feedUrl": "https://example.com/feed1",
|
|
||||||
"artworkUrl100": "https://example.com/artwork1_100.jpg",
|
|
||||||
"artworkUrl600": "https://example.com/artwork1_600.jpg",
|
|
||||||
"genres": []string{"Technology"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将响应数据编码为JSON并写入响应
|
|
||||||
json.NewEncoder(w).Encode(response)
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
// 创建iTunes搜索提供者,并设置测试服务器URL
|
|
||||||
provider := &ITunesProvider{
|
|
||||||
SearchAPIEndpoint: server.URL,
|
|
||||||
FeedAPIEndpoint: "https://itunes.apple.com",
|
|
||||||
GenresMap: initITunesGenres(),
|
|
||||||
Client: &http.Client{Timeout: 20 * time.Second},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建搜索选项
|
|
||||||
options := &SearchOptions{
|
|
||||||
Country: "cn",
|
|
||||||
Genre: "Technology",
|
|
||||||
Attribute: "titleTerm",
|
|
||||||
Limit: 10,
|
|
||||||
Language: "zh-cn",
|
|
||||||
Explicit: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用SearchPodcasts
|
|
||||||
result, err := provider.SearchPodcasts("golang", options)
|
|
||||||
|
|
||||||
// 验证没有错误
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("期望没有错误,但得到 %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果
|
|
||||||
if result == nil {
|
|
||||||
t.Fatal("期望结果不为nil,但得到nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果计数
|
|
||||||
if result.ResultCount != 1 {
|
|
||||||
t.Errorf("期望结果计数为1,但得到 %d", result.ResultCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果项目数量
|
|
||||||
if len(result.Items) != 1 {
|
|
||||||
t.Errorf("期望结果项目数量为1,但得到 %d", len(result.Items))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchPodcasts_APIError(t *testing.T) {
|
|
||||||
// 创建测试服务器,返回错误状态码
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
// 创建iTunes搜索提供者,并设置测试服务器URL
|
|
||||||
provider := &ITunesProvider{
|
|
||||||
SearchAPIEndpoint: server.URL,
|
|
||||||
FeedAPIEndpoint: "https://itunes.apple.com",
|
|
||||||
GenresMap: initITunesGenres(),
|
|
||||||
Client: &http.Client{Timeout: 20 * time.Second},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用SearchPodcasts
|
|
||||||
result, err := provider.SearchPodcasts("golang", nil)
|
|
||||||
|
|
||||||
// 验证返回错误
|
|
||||||
if err == nil {
|
|
||||||
t.Error("期望返回错误,但没有")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证错误消息包含状态码
|
|
||||||
expectedErrMsg := "API请求失败,状态码: 500"
|
|
||||||
if err.Error() != expectedErrMsg {
|
|
||||||
t.Errorf("期望错误消息包含 %q,但得到 %q", expectedErrMsg, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果为nil
|
|
||||||
if result != nil {
|
|
||||||
t.Errorf("期望结果为nil,但得到 %+v", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSearchPodcasts_InvalidJSON(t *testing.T) {
|
|
||||||
// 创建测试服务器,返回无效的JSON
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte(`{"resultCount": 1, "results": [invalid json]`))
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
// 创建iTunes搜索提供者,并设置测试服务器URL
|
|
||||||
provider := &ITunesProvider{
|
|
||||||
SearchAPIEndpoint: server.URL,
|
|
||||||
FeedAPIEndpoint: "https://itunes.apple.com",
|
|
||||||
GenresMap: initITunesGenres(),
|
|
||||||
Client: &http.Client{Timeout: 20 * time.Second},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用SearchPodcasts
|
|
||||||
result, err := provider.SearchPodcasts("golang", nil)
|
|
||||||
|
|
||||||
// 验证返回错误
|
|
||||||
if err == nil {
|
|
||||||
t.Error("期望返回错误,但没有")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证错误消息包含解析失败
|
|
||||||
if err != nil && err.Error() != "解析响应失败: invalid character 'i' looking for beginning of value" {
|
|
||||||
t.Errorf("期望错误消息包含解析失败,但得到 %q", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证结果为nil
|
|
||||||
if result != nil {
|
|
||||||
t.Errorf("期望结果为nil,但得到 %+v", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user