211 lines
5.3 KiB
Go
211 lines
5.3 KiB
Go
// Copyright 2025 eat-pray-ai & OpenWaygate
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package subscription
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/eat-pray-ai/yutu/pkg/common"
|
|
"github.com/jedib0t/go-pretty/v6/table"
|
|
"google.golang.org/api/youtube/v3"
|
|
)
|
|
|
|
var (
|
|
errGetSubscription = errors.New("failed to get subscription")
|
|
errDeleteSubscription = errors.New("failed to delete subscription")
|
|
errInsertSubscription = errors.New("failed to insert subscription")
|
|
)
|
|
|
|
type Subscription struct {
|
|
common.Fields
|
|
SubscriberChannelId string `yaml:"subscriber_channel_id" json:"subscriber_channel_id,omitempty"`
|
|
Description string `yaml:"description" json:"description,omitempty"`
|
|
ForChannelId string `yaml:"for_channel_id" json:"for_channel_id,omitempty"`
|
|
For string `yaml:"for" json:"for,omitempty"`
|
|
Order string `yaml:"order" json:"order,omitempty"`
|
|
Title string `yaml:"title" json:"title,omitempty"`
|
|
|
|
OnBehalfOfContentOwnerChannel string `yaml:"on_behalf_of_content_owner_channel" json:"on_behalf_of_content_owner_channel,omitempty"`
|
|
}
|
|
|
|
type ISubscription[T any] interface {
|
|
Get() ([]*T, error)
|
|
List(io.Writer) error
|
|
Insert(io.Writer) error
|
|
Delete(io.Writer) error
|
|
}
|
|
|
|
type Option func(*Subscription)
|
|
|
|
func NewSubscription(opts ...Option) ISubscription[youtube.Subscription] {
|
|
s := &Subscription{Fields: common.Fields{}}
|
|
for _, opt := range opts {
|
|
opt(s)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (s *Subscription) Get() ([]*youtube.Subscription, error) {
|
|
if err := s.EnsureService(); err != nil {
|
|
return nil, err
|
|
}
|
|
call := s.Service.Subscriptions.List(s.Parts)
|
|
if len(s.Ids) > 0 {
|
|
call = call.Id(s.Ids...)
|
|
}
|
|
if s.ChannelId != "" {
|
|
call = call.ChannelId(s.ChannelId)
|
|
}
|
|
if s.ForChannelId != "" {
|
|
call = call.ForChannelId(s.ForChannelId)
|
|
}
|
|
switch s.For {
|
|
case "mine":
|
|
call = call.Mine(true)
|
|
case "myRecentSubscribers":
|
|
call = call.MyRecentSubscribers(true)
|
|
case "mySubscribers":
|
|
call = call.MySubscribers(true)
|
|
}
|
|
if s.OnBehalfOfContentOwner != "" {
|
|
call = call.OnBehalfOfContentOwner(s.OnBehalfOfContentOwner)
|
|
}
|
|
if s.OnBehalfOfContentOwnerChannel != "" {
|
|
call = call.OnBehalfOfContentOwnerChannel(s.OnBehalfOfContentOwnerChannel)
|
|
}
|
|
if s.Order != "" {
|
|
call = call.Order(s.Order)
|
|
}
|
|
|
|
return common.Paginate(
|
|
&s.Fields, call,
|
|
func(r *youtube.SubscriptionListResponse) ([]*youtube.Subscription, string) {
|
|
return r.Items, r.NextPageToken
|
|
}, errGetSubscription,
|
|
)
|
|
}
|
|
|
|
func (s *Subscription) List(writer io.Writer) error {
|
|
subscriptions, err := s.Get()
|
|
if err != nil && subscriptions == nil {
|
|
return err
|
|
}
|
|
|
|
common.PrintList(
|
|
s.Output, subscriptions, writer,
|
|
table.Row{"ID", "Kind", "Resource ID", "Channel Title"},
|
|
func(sub *youtube.Subscription) table.Row {
|
|
var resourceId string
|
|
switch sub.Snippet.ResourceId.Kind {
|
|
case "youtube#video":
|
|
resourceId = sub.Snippet.ResourceId.VideoId
|
|
case "youtube#channel":
|
|
resourceId = sub.Snippet.ResourceId.ChannelId
|
|
case "youtube#playlist":
|
|
resourceId = sub.Snippet.ResourceId.PlaylistId
|
|
}
|
|
return table.Row{sub.Id, sub.Snippet.ResourceId.Kind, resourceId, sub.Snippet.Title}
|
|
},
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (s *Subscription) Insert(writer io.Writer) error {
|
|
if err := s.EnsureService(); err != nil {
|
|
return err
|
|
}
|
|
subscription := &youtube.Subscription{
|
|
Snippet: &youtube.SubscriptionSnippet{
|
|
ChannelId: s.SubscriberChannelId,
|
|
Description: s.Description,
|
|
ResourceId: &youtube.ResourceId{
|
|
ChannelId: s.ChannelId,
|
|
},
|
|
Title: s.Title,
|
|
},
|
|
}
|
|
|
|
call := s.Service.Subscriptions.Insert([]string{"snippet"}, subscription)
|
|
res, err := call.Do()
|
|
if err != nil {
|
|
return errors.Join(errInsertSubscription, err)
|
|
}
|
|
|
|
common.PrintResult(
|
|
s.Output, res, writer, "Subscription inserted: %s\n", res.Id,
|
|
)
|
|
return nil
|
|
}
|
|
|
|
func (s *Subscription) Delete(writer io.Writer) error {
|
|
if err := s.EnsureService(); err != nil {
|
|
return err
|
|
}
|
|
for _, id := range s.Ids {
|
|
call := s.Service.Subscriptions.Delete(id)
|
|
err := call.Do()
|
|
if err != nil {
|
|
return errors.Join(errDeleteSubscription, err)
|
|
}
|
|
|
|
_, _ = fmt.Fprintf(writer, "Subscription %s deleted", id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func WithSubscriberChannelId(id string) Option {
|
|
return func(s *Subscription) {
|
|
s.SubscriberChannelId = id
|
|
}
|
|
}
|
|
|
|
func WithDescription(description string) Option {
|
|
return func(s *Subscription) {
|
|
s.Description = description
|
|
}
|
|
}
|
|
|
|
func WithForChannelId(forChannelId string) Option {
|
|
return func(s *Subscription) {
|
|
s.ForChannelId = forChannelId
|
|
}
|
|
}
|
|
|
|
func WithFor(f string) Option {
|
|
return func(s *Subscription) {
|
|
s.For = f
|
|
}
|
|
}
|
|
|
|
func WithOnBehalfOfContentOwnerChannel(onBehalfOfContentOwnerChannel string) Option {
|
|
return func(s *Subscription) {
|
|
s.OnBehalfOfContentOwnerChannel = onBehalfOfContentOwnerChannel
|
|
}
|
|
}
|
|
|
|
func WithOrder(order string) Option {
|
|
return func(s *Subscription) {
|
|
s.Order = order
|
|
}
|
|
}
|
|
|
|
func WithTitle(title string) Option {
|
|
return func(s *Subscription) {
|
|
s.Title = title
|
|
}
|
|
}
|
|
|
|
var (
|
|
WithParts = common.WithParts[*Subscription]
|
|
WithOutput = common.WithOutput[*Subscription]
|
|
WithService = common.WithService[*Subscription]
|
|
WithIds = common.WithIds[*Subscription]
|
|
WithMaxResults = common.WithMaxResults[*Subscription]
|
|
WithChannelId = common.WithChannelId[*Subscription]
|
|
|
|
WithOnBehalfOfContentOwner = common.WithOnBehalfOfContentOwner[*Subscription]
|
|
)
|