Initial commit

This commit is contained in:
Senad Uka
2022-03-23 05:44:42 +01:00
parent 1405281a5c
commit eea10dd03b
113 changed files with 3617 additions and 81 deletions

View File

@@ -0,0 +1,57 @@
# frozen_string_literal: true
require_relative '../../log/loggable'
require_relative '../errors/schedule_fetch_error'
require 'net/http'
module Vendors
module BroadSign
class BroadSignFetchSchedule
include Log::Loggable
AIR_DOMAIN = 'air.broadsign.com'
PATH = 'playlist/v1/generate'
def initialize(tokens)
@tokens = tokens
end
def call(params)
params = ActiveSupport::HashWithIndifferentAccess.new(
screen: '1',
duration: 3600
).merge(params_whitelist(params))
logger.info "BroadSign fetch schedule request for player #{params[:player]}"
url = URI.join("https://#{AIR_DOMAIN}", PATH)
res = Net::HTTP.post(
url,
{
player_identifier: params[:player].to_s,
screen_identifier: params[:screen]&.to_s,
duration: "#{params[:duration]}s"
}.to_json,
{
"Authorization": "Bearer #{@tokens.auth_token}",
"Content-Type": 'application/json'
}
)
case res
when Net::HTTPSuccess
JSON.parse(res.body).with_indifferent_access
else
logger.error "Error fetching BroadSign Air schedule: #{res.code}"
raise Vendors::Errors::ScheduleFetchError, { player: params[:player], response_code: res.code }.to_json
end
end
private
def params_whitelist(params)
params.slice(
:player,
:screen,
:duration
)
end
end
end
end

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
require_relative '../../log/loggable'
module Vendors
module BroadSign
class BroadSignTokens
include Singleton
def auth_token
AppConfig.get_mandatory 'vendors.broad_sign.token'
end
end
end
end

View File

@@ -0,0 +1,47 @@
# frozen_string_literal: true
require_relative '../../log/loggable'
require_relative '../../schedule_pipeline/models/schedule'
require_relative '../../schedule_pipeline/models/schedule_item'
require_relative '../errors/schedule_transform_error'
module Vendors
module BroadSign
class BroadSignTransformSchedule
include Log::Loggable
def call(vendor, player, schedule, content_map)
logger.debug("BroadSign schedule transform for vendor #{vendor}, player #{player}")
start_time = nil
# for performance reasons, here we truncate the items list to the first N items where N is the number of contents
# in the schedule. This assumes the schedule round-robins content!
truncate_schedule(schedule, content_map.size)
items = schedule[:items].collect do |item|
start_time ||= item[:startTime]
SchedulePipeline::Models::ScheduleItem.new(
item[:duration],
content_map[item[:contentIndex] || 0][:id],
# TODO: build full POP reporting url using this token
item[:token]
)
end
raise Vendors::Errors::ScheduleTransformError, 'Schedule without a start time' if start_time.nil?
SchedulePipeline::Models::Schedule.new(
"BroadSign schedule for player #{player}",
vendor,
player,
start_time,
items
)
end
private
def truncate_schedule(schedule, size)
schedule[:items] = schedule[:items].first(size)
end
end
end
end

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
module Vendors
module Errors
class ContentIngestError < StandardError; end
end
end

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
module Vendors
module Errors
class ScheduleFetchError < StandardError; end
end
end

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
module Vendors
module Errors
class SchedulePublishError < StandardError; end
end
end

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
module Vendors
module Errors
class ScheduleTransformError < StandardError; end
end
end

View File

@@ -0,0 +1,93 @@
# frozen_string_literal: true
require_relative '../../log/loggable'
require_relative '../errors/schedule_fetch_error'
require 'net/http'
module Vendors
module Vistar
class VistarFetchAd
include Log::Loggable
PATH = 'api/v1/get_ad/json'
DEFAULT_SUPPORTED_MEDIA = %w[
application/x-shockwave-dynamic-flash
application/x-shockwave-flash
image/jpeg
image/png
video/mp4
video/mpeg
video/mpg
video/quicktime
video/webm
video/x-flv
video/x-ms-wmv
video/x-msvideo
]
def initialize(tokens)
@tokens = tokens
end
def call(params)
logger.info "Vistar fetch Ad request"
res = Net::HTTP.post(
Vendors::Vistar::VistarSettings.instance.vistar_url(PATH),
body(params),
{
"Content-Type": 'application/json'
}
)
case res
when Net::HTTPSuccess
JSON.parse(res.body).with_indifferent_access
else
logger.error "Error fetching Vistar Ad: #{res.code}"
raise Vendors::Errors::ScheduleFetchError, { response_code: res.code }.to_json
end
end
private
def body(params)
display_area = (params[:display_area] || [{}]).each_with_index.collect do |item, idx|
{
id: "display-#{idx}",
width: 1080,
height: 1920,
supported_media: DEFAULT_SUPPORTED_MEDIA,
static_duration: 8
}.merge(item)
end
params_whitelist(params).merge({
network_id: Vendors::Vistar::VistarSettings.instance.network_id,
api_key: "#{@tokens.api_key}",
direct_connection: false,
display_area: display_area
}).to_json
end
def params_whitelist(params)
params.slice(
:device_id,
:venue_id,
:display_time,
:device_attribute,
:name,
:display_area,
:id,
:width,
:height,
:allow_audio,
:supported_media,
:min_duration,
:max_duration,
:order_id,
:max_file_size_bytes,
:static_duration,
:latitude,
:longitude
)
end
end
end
end

View File

@@ -0,0 +1,37 @@
module Vendors
module Vistar
class VistarFetchSchedule
def initialize(tokens)
@tokens = tokens
end
def call(params)
fetch = fetch_ad(params)
return nil unless fetch && fetch[:advertisement]
ad = fetch[:advertisement][0]
{
contents: [
{
name: "vistar_asset_#{ad[:asset_id]}",
url: ad[:asset_url]
}
],
startTime: Time.at(ad[:display_time]).to_datetime,
items: [
{
contentIndex: 0,
duration: "#{ad[:length_in_seconds]}s",
pop_url: ad[:proof_of_play_url]
}
]
}
end
private
def fetch_ad(params)
Vendors::Vistar::VistarFetchAd.new(@tokens).call(params)
end
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
module Vendors
module Vistar
class VistarSettings
include Singleton
def vistar_url(path)
URI::join(
AppConfig.get_mandatory('vendors.vistar.base_url'),
path
)
end
def network_id
AppConfig.get_mandatory('vendors.vistar.network_id')
end
end
end
end

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
require_relative '../../log/loggable'
module Vendors
module Vistar
class VistarTokens
include Singleton
def api_key
AppConfig.get_mandatory 'vendors.vistar.api_key'
end
end
end
end

View File

@@ -0,0 +1,35 @@
# frozen_string_literal: true
require_relative '../../log/loggable'
require_relative '../../schedule_pipeline/models/schedule'
require_relative '../../schedule_pipeline/models/schedule_item'
require_relative '../errors/schedule_transform_error'
module Vendors
module Vistar
class VistarTransformSchedule
include Log::Loggable
def call(vendor, player, schedule, content_map)
logger.debug("Vistar schedule transform for vendor #{vendor}, player #{player}")
start_time = schedule[:startTime]
items = schedule[:items].collect do |item|
SchedulePipeline::Models::ScheduleItem.new(
item[:duration],
content_map[item[:contentIndex] || 0][:id],
item[:pop_url]
)
end
raise Vendors::Errors::ScheduleTransformError, 'Schedule without a start time' if start_time.nil?
SchedulePipeline::Models::Schedule.new(
"Vistar schedule for player #{player}",
vendor,
player,
start_time,
items
)
end
end
end
end