require 'json' require 'net/http' require 'uri' module Youtube class ApiClient ENDPOINT = 'https://www.googleapis.com/youtube/v3' def initialize api_key: ENV.fetch('YOUTUBE_API_KEY') @api_key = api_key end def search_videos q:, published_after: nil, published_before: nil, page_token: nil get_json('/search', { part: 'snippet', type: 'video', q:, order: 'date', maxResults: 50, regionCode: 'JP', relevanceLanguage: 'ja', publishedAfter: published_after&.iso8601, publishedBefore: published_before&.iso8601, pageToken: page_token }.compact) end def videos ids return { 'items' => [] } if ids.empty? get_json('/videos', part: 'snippet,status,contentDetails', id: ids.join(',')) end def playlist_items playlist_id:, page_token: nil get_json('/playlistItems', { part: 'snippet,contentDetails,status', playlistId: playlist_id, maxResults: 50, pageToken: page_token }.compact) end def channel id: nil, handle: nil raise ArgumentError, 'id or handle is required' if id.present? == handle.present? params = { part: 'snippet,contentDetails' } params[:id] = id if id params[:forHandle] = handle if handle get_json('/channels', params) end private def get_json path, params uri = URI(ENDPOINT + path) uri.query = URI.encode_www_form(params.merge(key: @api_key)) response = Net::HTTP.start(uri.host, uri.port, use_ssl: true, open_timeout: 10, read_timeout: 30) do |http| http.get(uri) end unless response.is_a?(Net::HTTPSuccess) raise "YouTube API error: #{ response.code } #{ response.body }" end JSON.parse(response.body) end end end