Skip to content

Commit

Permalink
MP publisher start, publishes show
Browse files Browse the repository at this point in the history
  • Loading branch information
kookster committed Nov 30, 2024
1 parent 732dee8 commit 5fb3048
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 102 deletions.
4 changes: 0 additions & 4 deletions app/models/apple/publisher.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# frozen_string_literal: true

module Apple
class Publisher < Integrations::Base::Publisher
PUBLISH_CHUNK_LEN = 25

attr_reader :public_feed,
:private_feed,
:api,
Expand Down
9 changes: 6 additions & 3 deletions app/models/apple/show.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ module Apple
class Show < Integrations::Base::Show
include Apple::ApiResponse

attr_reader :public_feed,
:private_feed,
:api
attr_reader :api

def initialize(public_feed:, private_feed:)
@public_feed = public_feed
@private_feed = private_feed
end

def self.apple_shows_json(api)
api.get_paged_collection("shows")
Expand Down
4 changes: 4 additions & 0 deletions app/models/feed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ def publish_integration?
def publish_integration!
end

def sync_log(integration)
SyncLog.latest.find_by(integration: integration, feeder_id: id, feeder_type: :feeds)
end

def set_defaults
self.file_name ||= DEFAULT_FILE_NAME
self.enclosure_template ||= Feed.enclosure_template_default
Expand Down
6 changes: 1 addition & 5 deletions app/models/integrations/base/episode.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
module Integrations
module Base
class Episode
attr_reader :feeder_episode

def initialize(feeder_episode)
@feeder_episode = feeder_episode
end
attr_accessor :feeder_episode

def synced_with_integration?
raise NotImplementedError, "Subclasses must implement synced_with_integration?"
Expand Down
3 changes: 2 additions & 1 deletion app/models/integrations/base/publisher.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module Integrations
module Base
class Publisher
PUBLISH_CHUNK_LEN = 25
include EpisodeSetOperations

attr_reader :show
attr_accessor :show

def initialize(show:)
@show = show
Expand Down
7 changes: 1 addition & 6 deletions app/models/integrations/base/show.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@ module Base
class Show
include EpisodeSetOperations

attr_reader :feed

def initialize(public_feed:, private_feed:)
@public_feed = public_feed
@private_feed = private_feed
end
attr_accessor :public_feed, :private_feed

def podcast
private_feed.podcast
Expand Down
32 changes: 17 additions & 15 deletions app/models/megaphone/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,29 @@ def initialize(token:, network_id:, endpoint_url: nil)
def get(path, params = {}, headers = {})
request = {url: join_url(path), headers: headers, params: params}
response = get_url(request)
data = incoming_body_filter(response.body)
if data.is_a?(Array)
pagination = pagination_from_headers(response.env.response_headers)
{items: data, pagination: pagination, request: request, response: response}
else
{items: [data], pagination: {}, request: request, response: response}
end
result(request, response)
end

def post(path, body, headers = {})
response = connection({url: join_url(path), headers: headers}).post do |req|
req.body = outgoing_body_filter(body)
end
incoming_body_filter(response.body)
request = {url: join_url(path), headers: headers, body: outgoing_body_filter(body)}
response = connection(request).post
result(request, response)
end

def put(path, body, headers = {})
connection({url: join_url(path), headers: headers}).put do |req|
req.body = outgoing_body_filter(body)
request = {url: join_url(path), headers: headers, body: outgoing_body_filter(body)}
response = connection(request).put
result(request, response)
end

def result(request, response)
data = incoming_body_filter(response.body)
if data.is_a?(Array)
pagination = pagination_from_headers(response.env.response_headers)
{items: data, pagination: pagination, request: request, response: response}
else
{items: [data], pagination: {}, request: request, response: response}
end
incoming_body_filter(response.body)
end

# TODO: and we need delete...
Expand Down Expand Up @@ -113,7 +115,7 @@ def connection(options)
Faraday.new(url: url, headers: headers, params: params) do |builder|
builder.request :token_auth, token
builder.response :raise_error
builder.response :logger
builder.response :logger, Rails.logger
builder.adapter :excon
end
end
Expand Down
40 changes: 23 additions & 17 deletions app/models/megaphone/episode.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
module Megaphone
class Episode < Megaphone::Model
attr_accessor :episode
class Episode < Integrations::Base::Episode
include Megaphone::Model
attr_accessor :private_feed

# Used to form the adhash value
ADHASH_VALUES = {"pre" => "0", "mid" => "1", "post" => "2"}.freeze

# Required attributes for a create
# external_id is not required by megaphone, but we need it to be set!
CREATE_REQUIRED = %w[title external_id]
CREATE_REQUIRED = %i[title external_id]

CREATE_ATTRIBUTES = CREATE_REQUIRED + %w[pubdate pubdate_timezone author link explicit draft
CREATE_ATTRIBUTES = CREATE_REQUIRED + %i[pubdate pubdate_timezone author link explicit draft
subtitle summary background_image_file_url background_audio_file_url pre_count post_count
insertion_points guid pre_offset post_offset expected_adhash original_filename original_url
episode_number season_number retain_ad_locations advertising_tags ad_free]

# All other attributes we might expect back from the Megaphone API
# (some documented, others not so much)
OTHER_ATTRIBUTES = %w[id created_at updated_at]
OTHER_ATTRIBUTES = %i[id created_at updated_at]

DEPRECATED = %w[]
DEPRECATED = %i[]

ALL_ATTRIBUTES = (CREATE_ATTRIBUTES + DEPRECATED + OTHER_ATTRIBUTES)

Expand All @@ -30,11 +31,11 @@ class Episode < Megaphone::Model

validates_absence_of :id, on: :create

def self.new_from_episode(dt_episode, feed = nil)
episode = Megaphone::Episode.new(attributes_from_episode(dt_episode))
episode.episode = dt_episode
episode.feed = feed
episode.set_audio_attributes
def self.new_from_episode(feed, feeder_episode)
episode = Megaphone::Episode.new(attributes_from_episode(feeder_episode))
episode.feeder_episode = feeder_episode
episode.private_feed = feed
episode.config = feed.config
episode
end

Expand All @@ -60,7 +61,7 @@ def self.attributes_from_episode(e)
end

def set_placement_attributes
placement = get_placement(episode.segment_count)
placement = get_placement(feeder_episode.segment_count)
self.expected_adhash = adhash_for_placement(placement)
self.pre_count = expected_adhash.count("0")
self.post_count = expected_adhash.count("2")
Expand All @@ -79,8 +80,9 @@ def get_placement(original_count)
placements&.find { |i| i.original_count == original_count }
end

# call this before create or update, yah
def set_audio_attributes
return unless episode.complete_media?
return unless feeder_episode.complete_media?
self.background_audio_file_url = upload_url
self.insertion_points = timings
self.retain_ad_locations = true
Expand All @@ -90,7 +92,7 @@ def upload_url
resp = Faraday.head(enclosure_url)
if resp.status == 302
media_version = resp.env.response_headers["x-episode-media-version"]
if media_version == episode.media_version_id
if media_version == feeder_episode.media_version_id
location = resp.env.response_headers["location"]
arrangement_version_url(location, media_version)
end
Expand All @@ -106,12 +108,16 @@ def arrangement_version_url(location, media_version)
end

def enclosure_url
url = EnclosureUrlBuilder.new.base_enclosure_url(episode.podcast, episode, feed)
EnclosureUrlBuilder.mark_authorized(url, feed)
url = EnclosureUrlBuilder.new.base_enclosure_url(
feeder_episode.podcast,
feeder_episode,
private_feed
)
EnclosureUrlBuilder.mark_authorized(url, private_feed)
end

def timings
episode.media[0..-2].map(&:duration)
feeder_episode.media[0..-2].map(&:duration)
end

def pre_after_original?(placement)
Expand Down
19 changes: 13 additions & 6 deletions app/models/megaphone/model.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
require "active_support/concern"

module Megaphone
class Model
include ActiveModel::Model
attr_accessor :feed
attr_writer :api
module Model
extend ActiveSupport::Concern

def config
feed.megaphone_config
included do
include ActiveModel::Model
attr_accessor :config
attr_writer :api
attr_accessor :api_response
end

def api
@api ||= Megaphone::Api.new(token: config.token, network_id: config.network_id)
end

def api_response_log_item
api_response&.slice(:request, :items, :pagination)
end
end
end
88 changes: 51 additions & 37 deletions app/models/megaphone/podcast.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
module Megaphone
class Podcast < Megaphone::Model
class Podcast < Integrations::Base::Show
include Megaphone::Model

# Required attributes for a create
# external_id is not required by megaphone, but we need it to be set!
CREATE_REQUIRED = %w[title subtitle summary itunes_categories language external_id]
CREATE_REQUIRED = %i[title subtitle summary itunes_categories language external_id]

# Other attributes available on create
CREATE_ATTRIBUTES = CREATE_REQUIRED + %w[link copyright author background_image_file_url
CREATE_ATTRIBUTES = CREATE_REQUIRED + %i[link copyright author background_image_file_url
explicit owner_name owner_email slug original_rss_url itunes_identifier podtrac_enabled
google_play_identifier episode_limit podcast_type advertising_tags excluded_categories]

# Update also allows the span opt in
UPDATE_ATTRIBUTES = CREATE_ATTRIBUTES + %w[span_opt_in]
UPDATE_ATTRIBUTES = CREATE_ATTRIBUTES + %i[span_opt_in]

# Deprecated, so we shouldn't rely on these, but they show up as attributes
DEPRECATED = %w[category redirect_url itunes_active redirected_at itunes_rating
DEPRECATED = %i[category redirect_url itunes_active redirected_at itunes_rating
google_podcasts_identifier stitcher_identifier]

# All other attributes we might expect back from the Megaphone API
# (some documented, others not so much)
OTHER_ATTRIBUTES = %w[id created_at updated_at image_file uid network_id recurring_import
OTHER_ATTRIBUTES = %i[id created_at updated_at image_file uid network_id recurring_import
episodes_count spotify_identifier default_ad_settings iheart_identifier feed_url
default_pre_count default_post_count cloned_feed_urls ad_free_feed_urls main_feed ad_free]

Expand All @@ -32,26 +34,19 @@ class Podcast < Megaphone::Model

validates_absence_of :id, on: :create

# initialize from attributes
def initialize(attributes = {})
super
end

def self.find_by_feed(feed)
return nil unless feed.podcast&.guid
podcast = Megaphone::Podcast.new(feed: feed)
podcast.find_by_guid(feed.podcast.guid).items.first
podcast = new_from_feed(feed)
public_feed = feed.podcast.public_feed
sync_log = public_feed.sync_log(:megaphone)
mp = podcast.find_by_megaphone_id(sync_log&.external_id)
mp ||= podcast.find_by_guid(feed.podcast.guid)
mp
end

# def self.find_by_megaphone_id(feed)
# return nil unless feed.podcast&.guid
# podcast = Megaphone::Podcast.new(feed: feed)
# podcast.find_by_guid(feed.podcast.guid).items.first
# end

def self.new_from_feed(feed)
podcast = Megaphone::Podcast.new(attributes_from_feed(feed))
podcast.feed = feed
podcast.private_feed = feed
podcast.config = feed.config
podcast
end

Expand Down Expand Up @@ -84,35 +79,54 @@ def self.attributes_from_feed(feed)
}
end

def build_integration_episode(feeder_episode)
Megaphone::Episode.new_from_episode(private_feed, feeder_episode)
end

def updated_at=(d)
d = Time.parse(d) if d.is_a?(String)
@updated_at = d
end

def list
result = api.get("podcasts")
Megaphone::PagedCollection.new(Megaphone::Podcast, result)
self.api_response = api.get("podcasts")
Megaphone::PagedCollection.new(Megaphone::Podcast, api_response)
end

def find_by_guid
result = api.get("podcasts", externalId: feed.podcast.guid)
Megaphone::PagedCollection.new(Megaphone::Podcast, result)
def find_by_guid(guid = podcast.guid)
return nil if guid.blank?
self.api_response = api.get("podcasts", externalId: guid)
handle_response(api_response)
end

def find_by_megaphone_id(mpid = id)
result = api.get("podcasts/#{mpid}")
(result[:items] || []).first
return nil if mpid.blank?
self.api_response = api.get("podcasts/#{mpid}")
handle_response(api_response)
end

def create!
validate!(:create)
body = as_json.slice(*Megaphone::Podcast::CREATE_ATTRIBUTES)
result = api.post("podcasts", body)
self.attributes = result.slice(*Megaphone::Podcast::ALL_ATTRIBUTES)
self
body = as_json(only: CREATE_ATTRIBUTES.map(&:to_s))
self.api_response = api.post("podcasts", body)
handle_response(api_response)
end

def update!
def update!(feed = nil)
if feed
self.attributes = self.class.attributes_from_feed(feed)
end
validate!(:update)
body = as_json.slice(*Megaphone::Podcast::UPDATE_ATTRIBUTES).to_json
result = api.put("podcasts/#{id}", body)
self.attributes = result.slice(*Megaphone::Podcast::ALL_ATTRIBUTES)
self
body = as_json(only: UPDATE_ATTRIBUTES.map(&:to_s))
self.api_response = api.put("podcasts/#{id}", body)
handle_response(api_response)
end

def handle_response(api_response)
if (item = (api_response[:items] || []).first)
self.attributes = item.slice(*ALL_ATTRIBUTES)
self
end
end
end
end
Loading

0 comments on commit 5fb3048

Please sign in to comment.