From 071362133d211b55fe6f7c567dadc24602f575cd Mon Sep 17 00:00:00 2001 From: Kang-Kyu Lee Date: Mon, 18 Aug 2025 11:51:18 -0700 Subject: [PATCH 1/4] Add branding_setting.unsubscribed_trailer --- lib/yt/collections/branding_settings.rb | 31 +++++++++++++++++++++++++ lib/yt/models/branding_setting.rb | 29 +++++++++++++++++++++++ lib/yt/models/channel.rb | 15 ++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 lib/yt/collections/branding_settings.rb create mode 100644 lib/yt/models/branding_setting.rb diff --git a/lib/yt/collections/branding_settings.rb b/lib/yt/collections/branding_settings.rb new file mode 100644 index 00000000..0c28a9aa --- /dev/null +++ b/lib/yt/collections/branding_settings.rb @@ -0,0 +1,31 @@ +require 'yt/collections/base' +require 'yt/models/branding_setting' + +module Yt + module Collections + # @private + class BrandingSettings < Base + + private + + def attributes_for_new_item(data) + puts data + { data: data['brandingSettings'] } + end + + # @return [Hash] the parameters to submit to YouTube to get the + # branding_settings of a resource, for instance a channel. + # @see https://developers.google.com/youtube/v3/docs/channels#resource + def list_params + super.tap do |params| + params[:path] = "/youtube/v3/#{@parent.kind.pluralize}" + params[:params] = branding_settings_params + end + end + + def branding_settings_params + { max_results: 50, part: 'brandingSettings', id: @parent.id } + end + end + end +end diff --git a/lib/yt/models/branding_setting.rb b/lib/yt/models/branding_setting.rb new file mode 100644 index 00000000..a0e79375 --- /dev/null +++ b/lib/yt/models/branding_setting.rb @@ -0,0 +1,29 @@ +require 'yt/models/base' + +module Yt + module Models + # @private + # Encapsulates branding settings about the resource, such as trailer on channel + # @see https://developers.google.com/youtube/v3/docs/channels#resource-representation + class BrandingSetting < Base + attr_reader :data + + def initialize(options = {}) + @data = options[:data] + end + + has_attribute :channel, default: {} + + def unsubscribed_trailer + channel['unsubscribedTrailer'] + end + + has_attribute :image, default: {} + + def banner_external_url + image['bannerExternalUrl'] + end + end + end +end + diff --git a/lib/yt/models/channel.rb b/lib/yt/models/channel.rb index a32acad2..67bfaf4f 100644 --- a/lib/yt/models/channel.rb +++ b/lib/yt/models/channel.rb @@ -232,6 +232,18 @@ def subscriber_count_visible? statistics_set.hidden_subscriber_count == false end + ### BRANDING SETTINGS ### + + has_one :branding_setting + + # @!attribute [r] unsubscribed_trailer + # @return [String] the channel’s trailer video id. + delegate :unsubscribed_trailer, to: :branding_setting + + # @!attribute [r] banner_external_url + # @return [String] the channel’s banner image URL. + delegate :banner_external_url, to: :branding_setting + ### CONTENT OWNER DETAILS ### has_one :content_owner_detail @@ -289,6 +301,9 @@ def initialize(options = {}) if options[:content_owner_details] @content_owner_detail = ContentOwnerDetail.new data: options[:content_owner_details] end + if options[:branding_settings] + @branding_setting = BrandingSetting.new data: options[:branding_settings] + end end # @private From 380987a706b2600a133b047050347f4087533b60 Mon Sep 17 00:00:00 2001 From: Kang-Kyu Lee Date: Mon, 15 Sep 2025 13:55:49 -0700 Subject: [PATCH 2/4] Add channel_sections under channel --- lib/yt/collections/branding_settings.rb | 1 - lib/yt/collections/channel_sections.rb | 45 +++++++++++++++++++++++++ lib/yt/models/channel.rb | 11 ++++++ lib/yt/models/channel_section.rb | 25 ++++++++++++++ lib/yt/models/group_info.rb | 2 +- 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 lib/yt/collections/channel_sections.rb create mode 100644 lib/yt/models/channel_section.rb diff --git a/lib/yt/collections/branding_settings.rb b/lib/yt/collections/branding_settings.rb index 0c28a9aa..52b2d0a1 100644 --- a/lib/yt/collections/branding_settings.rb +++ b/lib/yt/collections/branding_settings.rb @@ -9,7 +9,6 @@ class BrandingSettings < Base private def attributes_for_new_item(data) - puts data { data: data['brandingSettings'] } end diff --git a/lib/yt/collections/channel_sections.rb b/lib/yt/collections/channel_sections.rb new file mode 100644 index 00000000..643bcb47 --- /dev/null +++ b/lib/yt/collections/channel_sections.rb @@ -0,0 +1,45 @@ +require 'yt/collections/base' +require 'yt/models/channel_section' + +module Yt + module Collections + # Provides methods for a collection of YouTube channel sections. + # + # Resources with channel_sections is {Yt::Models::Channel channels}. + class ChannelSections < Base + + def attributes_for_new_item(data) + {}.tap do |attributes| + attributes[:id] = data['id'] + attributes[:snippet] = data['snippet'] + attributes[:content_details] = data['contentDetails'] + end + end + + # @return [Hash] the parameters to submit to YouTube to list channel sections. + # @see https://developers.google.com/youtube/v3/docs/channelSections/list + def list_params + super.tap do |params| + params[:params] = channel_sections_params + params[:path] = '/youtube/v3/channelSections' + end + end + + def channel_sections_params + {}.tap do |params| + params[:part] = 'snippet' + params.merge! @parent.channel_sections_params if @parent + # TODO: when to mine, when to on_behalf_of_content_owner + # if @parent.auth + # if @parent.auth.owner_name + # params[:on_behalf_of_content_owner] = @parent.auth.owner_name + # else + # params[:mine] = true + # end + # end + params + end + end + end + end +end diff --git a/lib/yt/models/channel.rb b/lib/yt/models/channel.rb index 67bfaf4f..23cf963a 100644 --- a/lib/yt/models/channel.rb +++ b/lib/yt/models/channel.rb @@ -126,6 +126,10 @@ def unsubscribe! # explicitly select the option to keep all subscriptions private. has_many :subscribed_channels + # @!attribute [r] channel_sections + # @return [Yt::Collections::ChannelSections] the channel’s channel sections. + has_many :channel_sections + ### ANALYTICS ### # @macro reports @@ -306,6 +310,13 @@ def initialize(options = {}) end end + # @private + # Used for `has_many :channel_sections` to return all youtube#channelSection items + # of the channel. + def channel_sections_params + {channel_id: id} + end + # @private # Tells `has_many :videos` that channel.videos should return all the # videos publicly available on the channel. diff --git a/lib/yt/models/channel_section.rb b/lib/yt/models/channel_section.rb new file mode 100644 index 00000000..1c7335b2 --- /dev/null +++ b/lib/yt/models/channel_section.rb @@ -0,0 +1,25 @@ +require 'yt/models/base' + +module Yt + module Models + class ChannelSection < Base + attr_reader :data + + # @private + def initialize(options = {}) + @id = options[:id] + @data = options[:snippet] + end + + has_attribute :type + has_attribute :channel_id + has_attribute :position, type: Integer + + ### ID ### + + # @!attribute [r] id + # @return [String] the ID that YouTube uses to identify each resource. + attr_reader :id + end + end +end diff --git a/lib/yt/models/group_info.rb b/lib/yt/models/group_info.rb index 5573b1e4..3ff7f37b 100644 --- a/lib/yt/models/group_info.rb +++ b/lib/yt/models/group_info.rb @@ -13,4 +13,4 @@ def initialize(options = {}) has_attribute :published_at, type: Time end end -end \ No newline at end of file +end From e59109734e971f828652c9fd438afa446b0551ec Mon Sep 17 00:00:00 2001 From: Kang-Kyu Lee Date: Tue, 18 Nov 2025 23:28:32 -0800 Subject: [PATCH 3/4] Add model spec for branding_setting --- spec/models/channel_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/models/channel_spec.rb b/spec/models/channel_spec.rb index e0c3ae06..b68573de 100644 --- a/spec/models/channel_spec.rb +++ b/spec/models/channel_spec.rb @@ -141,4 +141,23 @@ it { expect(channel.status).to be_a Yt::Status } end end + + describe '#branding_setting' do + context 'given fetching a channel returns a branding_setting' do + let(:attrs) { {branding_settings: {"channel"=>{"unsubscribedTrailer"=>"abcdefghijk"}}} } + it { expect(channel.branding_setting).to be_a Yt::BrandingSetting } + end + end + + describe '#unsubscribed_trailer' do + context 'given a branding_settings with a unsubscribed trailer' do + let(:attrs) { {branding_settings: {"channel"=>{"unsubscribedTrailer"=>"abcdefghijk"}}} } + it { expect(channel.unsubscribed_trailer).to eq 'abcdefghijk' } + end + + context 'given a branding_settings without a unsubscribed trailer' do + let(:attrs) { {branding_settings: {"channel"=>{}}} } + it { expect(channel.unsubscribed_trailer).to be_nil } + end + end end From c9c7b3de4ac3b047ea604fefe473fdf9672c762b Mon Sep 17 00:00:00 2001 From: Kang-Kyu Lee Date: Tue, 18 Nov 2025 23:45:39 -0800 Subject: [PATCH 4/4] Add test for channel_section model --- spec/models/channel_section_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 spec/models/channel_section_spec.rb diff --git a/spec/models/channel_section_spec.rb b/spec/models/channel_section_spec.rb new file mode 100644 index 00000000..817ac10f --- /dev/null +++ b/spec/models/channel_section_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' +require 'yt/models/channel_section' + +describe Yt::ChannelSection do + subject(:channel_section) { Yt::ChannelSection.new attrs } + + describe '#position' do + context 'given fetching a channel_section returns a snippet' do + let(:attrs) { {snippet: {"title"=>"New Section", "position"=>0}} } + it { expect(channel_section.position).to eq 0 } + end + end +end