From f148ed374e717c10195548acf9495c13f10245e7 Mon Sep 17 00:00:00 2001 From: Evan Fiordeliso Date: Sun, 3 Mar 2024 21:41:49 -0500 Subject: [PATCH] Add Videos endpoints to API --- api/api.go | 3 + api/videos/delete_videos.go | 38 +++++++ api/videos/get_videos.go | 207 ++++++++++++++++++++++++++++++++++++ api/videos/videos.go | 18 ++++ 4 files changed, 266 insertions(+) create mode 100644 api/videos/delete_videos.go create mode 100644 api/videos/get_videos.go create mode 100644 api/videos/videos.go diff --git a/api/api.go b/api/api.go index 747055c..6915727 100644 --- a/api/api.go +++ b/api/api.go @@ -30,6 +30,7 @@ import ( "go.fifitido.net/twitch/api/subscriptions" "go.fifitido.net/twitch/api/teams" "go.fifitido.net/twitch/api/users" + "go.fifitido.net/twitch/api/videos" ) const HelixBaseUrl = "https://api.twitch.tv/helix" @@ -64,6 +65,7 @@ type API struct { Subscriptions *subscriptions.Subscriptions Teams *teams.Teams Users *users.Users + Videos *videos.Videos } func New(client *http.Client, baseUrl *url.URL) *API { @@ -97,6 +99,7 @@ func New(client *http.Client, baseUrl *url.URL) *API { Subscriptions: subscriptions.New(client, baseUrl), Teams: teams.New(client, baseUrl), Users: users.New(client, baseUrl), + Videos: videos.New(client, baseUrl), } } diff --git a/api/videos/delete_videos.go b/api/videos/delete_videos.go new file mode 100644 index 0000000..c5599c5 --- /dev/null +++ b/api/videos/delete_videos.go @@ -0,0 +1,38 @@ +package videos + +import ( + "context" + "encoding/json" + "net/http" + "net/url" +) + +type DeleteVideosResponse struct { + // The list of IDs of the videos that were deleted. + Data []string `json:"data"` +} + +// Deletes one or more videos. You may delete past broadcasts, highlights, or uploads. +// +// Requires a user access token that includes the channel:manage:videos scope. +func (v *Videos) DeleteVideos(ctx context.Context, ids []string) (*DeleteVideosResponse, error) { + endpoint := v.baseUrl.ResolveReference(&url.URL{Path: "videos", RawQuery: url.Values{"id": ids}.Encode()}) + + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil) + if err != nil { + return nil, err + } + + res, err := v.client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var data DeleteVideosResponse + if err := json.NewDecoder(res.Body).Decode(&data); err != nil { + return nil, err + } + + return &data, nil +} diff --git a/api/videos/get_videos.go b/api/videos/get_videos.go new file mode 100644 index 0000000..b6c8903 --- /dev/null +++ b/api/videos/get_videos.go @@ -0,0 +1,207 @@ +package videos + +import ( + "context" + "encoding/json" + "net/http" + "net/url" + "time" + + "github.com/google/go-querystring/query" + "go.fifitido.net/twitch/api/types" +) + +type GetVideosParams struct { + // A list of IDs that identify the videos you want to get. + // To get more than one video, include this parameter for each video you want to get. For example, id=1234&id=5678. + // You may specify a maximum of 100 IDs. The endpoint ignores duplicate IDs and IDs that weren't found (if there's at least one valid ID). + // + // The id, user_id, and game_id parameters are mutually exclusive. + IDs []string `url:"id,omitempty"` + + // The ID of the user whose list of videos you want to get. + // + // The id, user_id, and game_id parameters are mutually exclusive. + UserID *string `url:"user_id,omitempty"` + + // A category or game ID. The response contains a maximum of 500 videos that show this content. + // To get category/game IDs, use the Search Categories endpoint. + // + // The id, user_id, and game_id parameters are mutually exclusive. + GameID *string `url:"game_id,omitempty"` + + // A filter used to filter the list of videos by the language that the video owner broadcasts in. + // For example, to get videos that were broadcast in German, set this parameter to the ISO 639-1 two-letter code for German (i.e., DE). + // For a list of supported languages, see Supported Stream Language. + // If the language is not supported, use “other.” + // + // Specify this parameter only if you specify the game_id query parameter. + Language *string `url:"language,omitempty"` + + // A filter used to filter the list of videos by when they were published. + // For example, videos published in the last week. Possible values are: + // + // all, day, month, week + // + // The default is "all," which returns videos published in all periods. + // + // Specify this parameter only if you specify the game_id or user_id query parameter. + Period *string `url:"period,omitempty"` + + // The order to sort the returned videos in. Possible values are: + // + // time — Sort the results in descending order by when they were created (i.e., latest video first). + // + // trending — Sort the results in descending order by biggest gains in viewership (i.e., highest trending video first). + // + // views — Sort the results in descending order by most views (i.e., highest number of views first). + // + // The default is "time." + // + // Specify this parameter only if you specify the game_id or user_id query parameter. + Sort *string `url:"sort,omitempty"` + + // A filter used to filter the list of videos by the video's type. Possible case-sensitive values are: + // + // all + // + // archive — On-demand videos (VODs) of past streams. + // + // highlight — Highlight reels of past streams. + // + // upload — External videos that the broadcaster uploaded using the Video Producer. + // + // The default is "all," which returns all video types. + // + // Specify this parameter only if you specify the game_id or user_id query parameter. + Type *string `url:"type,omitempty"` + + // The maximum number of items to return per page in the response. + // The minimum page size is 1 item per page and the maximum is 100. + // The default is 20. + First *int `url:"first,omitempty"` + + // The cursor used to get the next page of results. + // The Pagination object in the response contains the cursor’s value. + // Read More: https://dev.twitch.tv/docs/api/guide#pagination + After *types.Cursor `url:"after,omitempty"` + + // The cursor used to get the previous page of results. + // The Pagination object in the response contains the cursor’s value. + // Read More: https://dev.twitch.tv/docs/api/guide#pagination + Before *types.Cursor `url:"before,omitempty"` +} + +type GetVideosResponse struct { + // The list of published videos that match the filter criteria. + Data []Video `json:"data"` + + // The information used to page through the list of results. + // The object is empty if there are no more pages left to page through. + // Read More: https://dev.twitch.tv/docs/api/guide#pagination + Pagination types.Pagination `json:"pagination"` +} + +type Video struct { + // An ID that identifies the video. + Id string `json:"id"` + + // The ID of the stream that the video originated from if the video's type is "archive;" otherwise, null. + StreamId string `json:"stream_id"` + + // The ID of the broadcaster that owns the video. + UserId string `json:"user_id"` + + // The broadcaster's login name. + UserLogin string `json:"user_login"` + + // The broadcaster's display name. + UserName string `json:"user_name"` + + // The video's title. + Title string `json:"title"` + + // The video's description. + Description string `json:"description"` + + // The date and time, in UTC, of when the video was created. The timestamp is in RFC3339 format. + CreatedAt time.Time `json:"created_at"` + + // The date and time, in UTC, of when the video was published. The timestamp is in RFC3339 format. + PublishedAt time.Time `json:"published_at"` + + // The video's URL. + Url string `json:"url"` + + // A URL to a thumbnail image of the video. + // Before using the URL, you must replace the %{width} and %{height} placeholders with the width and height of the thumbnail you want returned. + // Due to current limitations, ${width} must be 320 and ${height} must be 180. + ThumbnailUrl string `json:"thumbnail_url"` + + // The video's viewable state. Always set to public. + Viewable string `json:"viewable"` + + // The number of times that users have watched the video. + ViewCount int `json:"view_count"` + + // The ISO 639-1 two-letter language code that the video was broadcast in. + // For example, the language code is DE if the video was broadcast in German. + // For a list of supported languages, see Supported Stream Language: https://help.twitch.tv/s/article/languages-on-twitch#streamlang + // The language value is "other" if the video was broadcast in a language not in the list of supported languages. + Language string `json:"language"` + + // The video's type. Possible values are: + // + // archive — An on-demand video (VOD) of one of the broadcaster's past streams. + // + // highlight — A highlight reel of one of the broadcaster's past streams. + // See Creating Highlights: https://help.twitch.tv/s/article/creating-highlights-and-stream-markers + // + // upload — A video that the broadcaster uploaded to their video library. + // See Upload under Video Producer: https://help.twitch.tv/s/article/video-on-demand?language=en_US#videoproducer + Type string `json:"type"` + + // The videos length in ISO 8601 duration format. For example, 3m21s represents 3 minutes, 21 seconds. + Duration time.Duration `json:"duration"` + + // The segments that Twitch Audio Recognition muted; otherwise, null. + MutedSegments []struct { + // The duration of the muted segment, in seconds. + Duration int `json:"duration"` + + // The offset, in seconds, from the beginning of the video to where the muted segment begins. + Offset int `json:"offset"` + } `json:"muted_segments"` +} + +// Gets information about one or more published videos. You may get videos by ID, by user, or by game/category. +// +// You may apply several filters to get a subset of the videos. +// The filters are applied as an AND operation to each video. +// For example, if language is set to ‘de’ and game_id is set to 21779, +// the response includes only videos that show playing League of Legends by users that stream in German. +// The filters apply only if you get videos by user ID or game ID. +// +// Requires an app access token or user access token. +func (vid *Videos) GetVideos(ctx context.Context, params *GetVideosParams) (*GetVideosResponse, error) { + v, _ := query.Values(params) + endpoint := vid.baseUrl.ResolveReference(&url.URL{Path: "videos", RawQuery: v.Encode()}) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) + if err != nil { + return nil, err + } + + res, err := vid.client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + var data GetVideosResponse + if err := json.NewDecoder(res.Body).Decode(&data); err != nil { + return nil, err + } + + return &data, nil +} diff --git a/api/videos/videos.go b/api/videos/videos.go new file mode 100644 index 0000000..d164f64 --- /dev/null +++ b/api/videos/videos.go @@ -0,0 +1,18 @@ +package videos + +import ( + "net/http" + "net/url" +) + +type Videos struct { + client *http.Client + baseUrl *url.URL +} + +func New(client *http.Client, baseUrl *url.URL) *Videos { + return &Videos{ + client: client, + baseUrl: baseUrl, + } +}