/
thetvdb.go
201 lines (185 loc) · 7.59 KB
/
thetvdb.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package apiexternal
import (
"errors"
"slices"
"strconv"
"time"
"github.com/Kellerman81/go_media_downloader/pkg/main/config"
"github.com/Kellerman81/go_media_downloader/pkg/main/database"
"github.com/Kellerman81/go_media_downloader/pkg/main/logger"
"github.com/Kellerman81/go_media_downloader/pkg/main/slidingwindow"
)
type theTVDBSeries struct {
Data theTVDBSeriesData `json:"data"`
}
type theTVDBSeriesData struct {
ID int `json:"id"`
SeriesName string `json:"seriesName"`
Aliases []string `json:"aliases"`
Season string `json:"season"`
Status string `json:"status"`
FirstAired string `json:"firstAired"`
Network string `json:"network"`
Runtime string `json:"runtime"`
Language string `json:"language"`
Genre []string `json:"genre"`
Overview string `json:"overview"`
Rating string `json:"rating"`
ImdbID string `json:"imdbId"`
SiteRating float32 `json:"siteRating"`
SiteRatingCount int `json:"siteRatingCount"`
Slug string `json:"slug"`
Banner string `json:"banner"`
Poster string `json:"poster"`
Fanart string `json:"fanart"`
//SeriesID any `json:"seriesId"`
//NetworkID string `json:"networkId"`
}
type theTVDBEpisodes struct {
Links theTVDBEpisodesLinks `json:"links"`
Data []theTVDBEpisode `json:"data"`
}
type theTVDBEpisodesLinks struct {
First int `json:"first"`
Last int `json:"last"`
}
type theTVDBEpisode struct {
AiredSeason int `json:"airedSeason"`
AiredEpisodeNumber int `json:"airedEpisodeNumber"`
EpisodeName string `json:"episodeName"`
FirstAired string `json:"firstAired"`
Overview string `json:"overview"`
Poster string `json:"filename"`
//ID int `json:"id"`
//Language TheTVDBEpisodeLanguage `json:"language"`
//ProductionCode string `json:"productionCode"`
//ShowURL string `json:"showUrl"`
//SeriesID int `json:"seriesId"`
//ImdbID string `json:"imdbId"`
//ContentRating string `json:"contentRating"`
//SiteRating float32 `json:"siteRating"`
//SiteRatingCount int `json:"siteRatingCount"`
//IsMovie int `json:"isMovie"`
}
// type theTVDBEpisodeLanguage struct {
// EpisodeName string `json:"episodeName"`
// Overview string `json:"overview"`
// }
// tvdbClient is a struct for interacting with TheTVDB API.
// It contains a field Client which is a pointer to a rate limited HTTP client.
type tvdbClient struct {
// Client is a pointer to a rate limited HTTP client for making requests.
Client rlHTTPClient
}
// Close cleans up the theTVDBSeries object by setting all fields to their
// zero values. This is done to avoid keeping large objects in memory
// unnecessarily when they are no longer needed. The cleanup is skipped
// if the DisableVariableCleanup setting is true or if t is nil.
func (t *theTVDBSeries) Close() {
if config.SettingsGeneral.DisableVariableCleanup || t == nil {
return
}
*t = theTVDBSeries{}
}
// Close cleans up the theTVDBEpisodes struct by zeroing it out.
// This is done to avoid keeping large structs in memory when no longer needed.
func (t *theTVDBEpisodes) Close() {
if config.SettingsGeneral.DisableVariableCleanup || t == nil {
return
}
clear(t.Data)
*t = theTVDBEpisodes{}
}
// NewTvdbClient creates a new tvdbClient instance for making requests to
// the TheTVDB API. It configures rate limiting and TLS based on the
// provided parameters.
func NewTvdbClient(seconds int, calls int, disabletls bool, timeoutseconds int) {
if seconds == 0 {
seconds = 1
}
if calls == 0 {
calls = 1
}
tvdbAPI = tvdbClient{
Client: NewClient(
"tvdb",
disabletls,
true,
slidingwindow.NewLimiter(time.Duration(seconds)*time.Second, int64(calls)),
false, slidingwindow.NewLimiter(10*time.Second, 10), timeoutseconds)}
}
// GetTvdbSeries retrieves TV series data from the TheTVDB API for the given series ID.
// If a non-empty language is provided, it will be set in the API request headers.
// Returns the TV series data, or an error if one occurs.
func GetTvdbSeries(id int, language string) (theTVDBSeries, error) {
if id == 0 || tvdbAPI.Client.checklimiterwithdaily() {
return theTVDBSeries{}, logger.ErrNotFound
}
if language != "" {
return DoJSONType[theTVDBSeries](&tvdbAPI.Client, logger.JoinStrings("https://api.thetvdb.com/series/", strconv.Itoa(id)), keyval{"Accept-Language", language})
}
return DoJSONType[theTVDBSeries](&tvdbAPI.Client, logger.JoinStrings("https://api.thetvdb.com/series/", strconv.Itoa(id)))
}
// GetTvdbSeriesEpisodes retrieves all episodes for the given TV series ID from
// TheTVDB API. It accepts the series ID, preferred language, and database series
// ID. It retrieves the episode data, checks for existing episodes to avoid
// duplicates, and inserts any missing episodes into the database. If there are
// multiple pages of results, it fetches additional pages.
func UpdateTvdbSeriesEpisodes(id int, language string, dbid uint) {
if id == 0 || tvdbAPI.Client.checklimiterwithdaily() {
return
}
urlv := logger.URLJoinPath("https://api.thetvdb.com/series/", strconv.Itoa(id), "episodes")
var result theTVDBEpisodes
var err error
var lang keyval
if language != "" {
lang = keyval{"Accept-Language", language}
result, err = DoJSONType[theTVDBEpisodes](&tvdbAPI.Client, urlv, lang)
} else {
result, err = DoJSONType[theTVDBEpisodes](&tvdbAPI.Client, urlv)
}
if err != nil {
if !errors.Is(err, logger.ErrToWait) {
logger.LogDynamic("error", "Error calling", logger.NewLogFieldValue(err), logger.NewLogField(logger.StrURL, urlv))
}
return
}
tbl := database.Getrows1size[database.DbstaticTwoString](false, database.QueryDbserieEpisodesCountByDBID, database.QueryDbserieEpisodesGetSeasonEpisodeByDBID, &dbid)
addthetvdbepisodes(&result, &dbid, tbl)
urlv += "?page="
if result.Links.Last >= 2 {
result.Data = slices.Grow(result.Data, len(result.Data)*result.Links.Last)
var resultadd theTVDBEpisodes
for k := 2; k <= result.Links.Last; k++ {
if language != "" {
resultadd, err = DoJSONType[theTVDBEpisodes](&tvdbAPI.Client, urlv+strconv.Itoa(k), lang)
} else {
resultadd, err = DoJSONType[theTVDBEpisodes](&tvdbAPI.Client, urlv+strconv.Itoa(k))
}
if err == nil {
addthetvdbepisodes(&resultadd, &dbid, tbl)
}
resultadd.Close()
}
}
result.Close()
clear(tbl)
}
// addthetvdbepisodes iterates through the episodes in the given TheTVDBEpisodes
// result and inserts any missing episodes into the dbserie_episodes table for
// the series matching the given dbid. It returns false if no error occurs.
func addthetvdbepisodes(resultadd *theTVDBEpisodes, dbid *uint, tbl []database.DbstaticTwoString) bool {
for idx := range resultadd.Data {
strepisode := strconv.Itoa(resultadd.Data[idx].AiredEpisodeNumber)
strseason := strconv.Itoa(resultadd.Data[idx].AiredSeason)
if checkdbtwostrings(tbl, strseason, strepisode) {
continue
}
dt := database.ParseDateTime(resultadd.Data[idx].FirstAired)
stridentifier := GenerateIdentifierStringFromInt(resultadd.Data[idx].AiredSeason, resultadd.Data[idx].AiredEpisodeNumber)
database.ExecN("insert into dbserie_episodes (episode, season, identifier, title, first_aired, overview, poster, dbserie_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
&strepisode, &strseason, &stridentifier, &resultadd.Data[idx].EpisodeName, &dt, &resultadd.Data[idx].Overview, &resultadd.Data[idx].Poster, dbid)
}
return false
}