Skip to content

Instantly share code, notes, and snippets.

@Unkas82
Last active September 12, 2018 16:25
Show Gist options
  • Save Unkas82/951e61354c5dc85b1aff3a74cd8c60de to your computer and use it in GitHub Desktop.
Save Unkas82/951e61354c5dc85b1aff3a74cd8c60de to your computer and use it in GitHub Desktop.
class ExternalPlaylistsFacade
include PlaylistHelper
include Concerns::VideoFormats
include Multisportable
GLUE_THRESHOLD = 2
attr_reader :matches_count, :type, :matches
def initialize(playlist:, current_user:, type:, current_ip:)
@current_ip = current_ip
@current_user = current_user
@type = type
@playlist = playlist
@raw_events = raw_events
@matches = get_matches(@raw_events)
if @type != "External"
@matches_accesses = get_matches_accesses
end
#@folders = current_user.all_folders.includes(:playlists).order(update_dt: :desc)
#@playlists = current_user.playlists.in_root_folder.order(update_dt: :desc)
#@favorites = current_user.video_favorites
#@tags = current_user.tags
end
def title
#split for composite titles (external)
@playlist.title.nil? ? 'External playlist' : @playlist.title.presence.split('/').try(:first)
end
def subtitle
@playlist.subtitle.nil? ? '' : @playlist.subtitle
end
def events
@events ||= get_events
end
def players_tags
PlayersTagsGenerator.new(match_ids: @match_ids).generate
end
def sport_type
Multisportable.get_sport_type(@match_ids)
end
# def matches_count
# @matches_count
# end
def tags_json
episode_tags(@current_user.tags).to_json
end
def get_sorted_episodes(events)
first = events.first
return unless first.present?
# сортируем по убыванию свежести матча и по времени старта эпизода
if (@playlist.get_params.present?) && (@playlist.get_params.include? "betterGoals_nomark")
preloaded_matches = Match.where(id: events.map { |e| e['fname'].split('_').try(:first) }.uniq)
if !preloaded_matches.pluck(:scheduled_stream).include? nil # if all shedule_streams present
events.sort_by do |e|
stream_time = preloaded_matches.find { |m| m.id.to_s == e['fname'].split('_').try(:first) }.scheduled_stream.to_time.to_i
[stream_time, -e['fname'].split('_').first.to_i, e['fname'].split('_').second.to_i, e['from'].to_i]
end
else
events.sort_by do |e|
match_dt = preloaded_matches.find { |m| m.id.to_s == e['fname'].split('_').try(:first) }.dt.to_time.to_i
[-match_dt, -e['fname'].split('_').first.to_i, e['fname'].split('_').second.to_i, e['from'].to_i]
end
end
elsif first['match'] && first['match']['dt']
events.sort_by { |e| [Date.today - e['match']['dt'], e['fname'], e['from'].to_i] }
elsif first['match_id'].present? && first['match_id'] < 10_000_000
preloaded_matches = Match.where(id: events.map { |e| e['match_id'] }.uniq)
events.sort_by do |e|
match_dt = preloaded_matches.find { |m| m.id == e['match_id'] }.dt.to_time.to_i
[-match_dt, e['fname'], e['from'].to_i]
end
elsif first['match_subtitle_en'].present? && events.first['fname'].split('_').try(:first).try(:to_i) < 10_000_000
preloaded_matches = Match.where(id: events.map { |e| e['fname'].split('_').try(:first) }.uniq)
events.sort_by do |e|
match_dt = preloaded_matches.find { |m| m.id.to_s == e['fname'].split('_').try(:first) }.dt.to_time.to_i
[-match_dt, -e['fname'].split('_').first.to_i, e['fname'].split('_').second.to_i, e['from'].to_i]
end
elsif first['match_subtitle_en'].present?
events.sort { |a, b| [a['match_subtitle_en'], a['fname'], a['from'].to_i] <=> [b['match_subtitle_en'], b['fname'], b['from'].to_i] }
elsif events.first['ts'].present? # ts added in #get_events_array_ts
events.sort_by { |e| [-e['ts'], e['fname'], e['from'].to_i] }
else
preloaded_matches = get_matches(events)
events.sort_by do |e|
match_dt = preloaded_matches.find { |m| m.id == e['fname'].to_s.split('_').first.to_i }.try(:dt)
if match_dt
[-match_dt.to_time.to_i, e['fname'], e['from'].to_i]
else
[e['fname'], e['from'].to_i]
end
end
end
end
def has_autoplay?
# move into a common file
@current_user.client_id != Client::MEDIAZEST
end
def get_matches_accesses
accesses = {}
@matches.each { |match| accesses[match.id] ||= get_access(match) }
accesses
end
private
def get_matches(events)
ids = events.map do |event|
next if event['fname'].blank?
event['fname'].to_s.split('_').first
end.uniq
@match_ids = ids
@matches_count = ids.count
football_matches = Match.ordered_by_latest.where(id: ids.select { |id| id.to_i < 10_000_000 }.uniq )
non_football_matches = ids.select { |id| id.to_i > 10_000_000 }.uniq.map { |id| Match.new(id: id) }
matches = football_matches + non_football_matches
return [] if matches.blank? || not_need_match_delimiters?(ids)
matches
end
def get_access(match)
match.video_accesses(@current_user, @current_ip)
end
def get_events
events = @raw_events
add_data(events)
apply_episodes_time_settings(events)
end
def raw_events
if @type == 'External'
events = @playlist.decoded_data.uniq
events = get_sorted_episodes(events)
if @playlist.playlist_type.nil? || [email protected]_type.eql?('football_team')
glue(events)
else
events
end
else
@playlist.ordered_episodes
end
end
def add_data(events)
events.each do |event|
if event['fname'].blank?
event['accesses'] = { '0' => false }
next
end
match_id = event['fname'].to_s.split('_').first.to_i
#type = "External" if caller.grep(/scout/).any? { |str| str.include?('external_playlists_controller') }
# accessibility
if @type == "External"
event['accesses'] = { '0' => true }#@matches_accesses[match_id] || { '0' => true }
else
event['accesses'] = @matches_accesses[match_id] || { '0' => true }
end
# match_data
event['match_id'] = match_id
event['sport_type'] = Multisportable.get_sport_type(match_id) unless event['sport_type']
match = @matches.find { |match| match.id == match_id }
event['match'] = match.to_playlist_data_min(@current_ip, @current_user.id, @type) if match
event['id'] = generate_episode_id(event, events) unless event['id']
end
events
end
def apply_episodes_time_settings(events)
#return events unless @playlist.is_football?
settings = @current_user.episodes_time_settings
if settings[:use_defaults]
events.each do |event|
event['title'] = get_event_title(event)
end
build_title(events)
else
match_events = MatchEvent.where(id: events.map {|e| e['id']}).to_a
events.each do |event|
match_event = match_events.detect {|e| e.id == event['id']}
if settings[:all_poss]
event['from'] = (match_event && match_event.possession_tss[:poss_start_ts]) ? match_event.possession_tss[:poss_start_ts] : event['from'].to_i
event['to'] = (match_event && match_event.possession_tss[:poss_end_ts]) ? match_event.possession_tss[:poss_end_ts] : event['to'].to_i
elsif match_event && match_event.attack_id.present?
event['from'] = [event['from'].to_i - (settings[:sec_before] || 0), 0].max
event['to'] = event['to'].to_i + (settings[:sec_after] || 0)
else
if event['ts']
event['from'] = [event['ts'].to_i - settings[:sec_before], 0].max
event['to'] = event['ts'].to_i + settings[:sec_after]
end
end
event['title'] = event["custom_title"] ? event["title"] : get_event_title(event)
end
build_title(events)
end
return events
end
def get_event_title(event)
if event["custom_title"]
event["title"]
else
"#{format_seconds(event['from'])} - #{format_seconds(event['to'])}"
end
end
def build_title(events)
events.each do |event|
match_id = event["fname"].split('_')[0].to_i
period_index = event["fname"].split('_')[1].to_i
str_period = "#{get_period_index(period_index)} #{get_period_name(match_id, period_index)}"
if event["custom_title"]
event["title"] = event['title']
else
event["title"] = "#{str_period}, #{event['title']}"
end
end
events
end
def glue_conditions?(event, previous, threshold)
is_from_same_period = event['fname'] == previous['fname']
return false unless is_from_same_period
diff = event['from'].to_i - previous['to'].to_i
is_close_enough = diff <= threshold
is_close_enough
end
def glue(events)
return [] if !events || events.size == 0
return events if events.size == 1
events[1..-1].each_with_object([[events.first]]) do |event, result_array|
previous = result_array.last.last
if glue_conditions?(event, previous, GLUE_THRESHOLD)
result_array[-1] << event
else
result_array << [event]
end
end.map do |sub_array|
sub_array.first.tap do |event|
event['to'] = sub_array.last['to']
end
end
end
def not_need_match_delimiters?(match_ids)
# если после эпизодов игры 1, идут эпизоды игры 2, а потом снова игра 1 - то в таком плейлисте делители делать не надо
match_ids.each_with_index do |current_id, i|
next if i < 2
previous_id = match_ids[i - 1]
return true if previous_id != current_id && match_ids[0..i - 2].include?(current_id)
end
false
end
def generate_episode_id(episode, episodes)
ids = episodes.select { |e| e.has_key?('id') }.map { |e| e['id'] }
loop do
random_id = SecureRandom.urlsafe_base64(nil, false)
break random_id unless random_id.in?(ids)
end
end
end
=======================================================
class ExternalPlaylistsController < ApplicationController
include Concerns::ExternalPlaylistsParams
include PlaylistHelper
skip_before_action :verify_authenticity_token, only: [:create]
skip_before_action :authenticate_user!, only: [:create]
def show
@playlist = ExternalPlaylist.find(params[:id])
external_playlist_facade = ExternalPlaylistsFacade.new(
playlist: @playlist,
current_user: current_user,
type: 'External',
current_ip: current_ip
)
#@my_folders_json = external_playlist_facade.folders_json
@my_playlists_json = {}.to_json
#@my_tags_json = external_playlist_facade.tags_json
#@players_tags = external_playlist_facade.players_tags
episodes = external_playlist_facade.events
@sport_type = external_playlist_facade.sport_type
id_type = match_type(@playlist.decoded_data.first["fname"].to_s.split('_').first.to_i)
# if we have specific data in pl (hello, Stas)
if @playlist.decoded_data.first.key?("match_title_ru")
ext_list = true
data_arr = @playlist.decoded_data
data_group = data_arr.group_by{|ep| ep["match_title_en"]}
match_data_arr = []
data_group.each do |match_episodes|
match_hash = {}
match_hash["id"] = match_episodes[1][0]['fname'].to_s.split('_').first.to_i
match_hash["title_ru"] = match_episodes[1][0]['match_title_ru']
match_hash["title_en"] = match_episodes[1][0]['match_title_en']
match_hash["subtitle_ru"] = match_episodes[1][0]['match_subtitle_ru']
match_hash["subtitle_en"] = match_episodes[1][0]['match_subtitle_en']
match_hash["stream_dt"] = Match.find_by(id: match_hash["id"]).try(:scheduled_stream).try(:to_time).to_i
match_data_arr << match_hash
end
if match_data_arr[0]["id"] < 10_000_000
preloaded_matches = Match.where(id: match_data_arr.map { |e| e['id'] }.uniq )
match_data_arr.each do |e|
e['dt'] = preloaded_matches.find { |m| m.id == e['id']}.dt.to_time.to_i
end
end
@matches_json = match_data_arr.to_json
# if basketball or hockey
# elsif external_playlist_facade.matches.map(&:to_playlist_data).empty? && ["hockey", "basketball"].include?(id_type)
elsif external_playlist_facade.matches.select { |m| m.id < 10_000_000 }.empty? && ["hockey", "basketball"].include?(id_type)
ext_list = true
data_arr = @playlist.decoded_data
match_data_arr = get_match_data_arr(data_arr, id_type)
@matches_json = match_data_arr.uniq.to_json
if match_data_arr.uniq.count > 1
episodes = get_events_array_ts(external_playlist_facade.events, match_data_arr)
episodes = external_playlist_facade.get_sorted_episodes(episodes)
else
episodes.sort_by!{|e| [e['fname'], e['from'].to_i]}
end
else
@matches_json = external_playlist_facade.matches.map(&:to_playlist_data).compact.to_json
end
match_ids = @playlist.match_ids.uniq
if @playlist.is_football?
halfs = Match.where(id: match_ids).map{ |match| {match.id => match.first_pass_second_halfs}}.reduce({}, :merge)
end
all_start_ms = {}# VideoJSON::Flash.get_all_start_ms(match_id: match_ids, user_id: current_user.id, ip: current_ip)
gon.push(
matches_count: external_playlist_facade.matches_count,
episodes: episodes,
playlist_title: external_playlist_facade.title,
playlist_title_long: external_playlist_facade.title,
playlist_subtitle: external_playlist_facade.subtitle,
users: current_user.video_share_emails,
type: external_playlist_facade.type,
ext_match: ext_match||=nil,
ext_match_title: ext_match_title||=nil,
ext_match_subtitle: ext_match_subtitle||=nil,
ext_list: ext_list||=nil,
episodes_time_settings: @episodes_time_settings,
no_autoplay: external_playlist_facade.has_autoplay?,
all_start_ms: all_start_ms,
first_pass_second_halfs: halfs,
order_by_shedules: (@playlist.get_params.present?) && (@playlist.get_params.include? "betterGoals_nomark")
)
match_types = match_ids.map{|id| Multisportable.get_sport_type(id)}
if match_types.all?{ |type| type == Multisportable::SportType::FOOTBALL }
access = 1 #match_ids.map{|id| Match.find(id).video_accesses(current_user).values.any?}.any? ? 1 : 0
create_stats(
'pageVisit',
vAccess: access
)
end
render 'video/show_new'
end
def create
episodes = params.permit(episodes: [:id, :match_id, :from, :to, :ts, :fname, :period, :title, :tag_title, :order_position, skill: [:skill_id, :title, :skill_group_id]])[:episodes]
episodes = build_title(episodes)
playlist_title = nil
playlist_subtitle = nil
if params['object_type'].present? && params['object_id'].present? && params['action_names'].present?
playlist_title = ExternalPlaylist.create_title(
object_type: params['object_type'],
object_id: params['object_id'],
action_names: params['action_names'],
episodes_count: episodes.size
)
end
if params['playlist_title'].present?
playlist_title = params['playlist_title']
elsif params['external_title'].present?
playlist_title = params['external_title']
end
if params['playlist_subtitle'].present?
playlist_subtitle = params['playlist_subtitle']
end
playlist_title = params['title'] if params['title'].present?
playlist_subtitle = params['subtitle'] if params['subtitle'].present?
@playlist = ExternalPlaylist.new(plain_playlist: episodes, title: playlist_title, subtitle: playlist_subtitle)
if params['external_type'].present?
@playlist.playlist_type = params['external_type']
end
# @playlist = ExternalPlaylist.create(plain_playlist: episodes, title: playlist_title)
@playlist.save
if @playlist.persisted?
render json: { link: external_playlist_url(id: @playlist.id) }
else
render json: { success: false }, status: 422
end
end
def view
get_params = external_playlist_params
external_playlist = ExternalPlaylist.find_by(get_params: get_params.to_s)
if params[:sport_kind] == 'football'
episodes = get_football_episodes_from_web_service(get_params)
else
episodes = get_episodes_from_web_service(get_params)
end
if external_playlist
external_playlist.update(plain_playlist: episodes)
else
if episodes.present? && !episodes.empty?
external_playlist = ExternalPlaylist.create!(get_params: get_params.to_s, plain_playlist: episodes)
else
redirect_to '/404'
return
end
end
redirect_to external_playlist_path(id: external_playlist.id), protocol: '//'
end
def multisport_video
ScoutApi.match_video_url(match_id: params[:match_id], sport_type: params[:sport_type])
end
private
def get_episodes_from_web_service(get_params)
body = transform_get_params(get_params)
conn = Faraday.new(url: 'http://service.instatfootball.com')
res = conn.post do |req|
req.url '/ws.php'
req.headers['Content-Type'] = 'application/json'
req.body = body.to_json
end
JSON.parse(res.body)['data']
end
def get_football_episodes_from_web_service(get_params)
parameters = {
base: :pg_football,
server: 'instatfootball.com',
login: :scout,
pass: :scout987,
proc: :scout_external_playlist,
params: Hash[get_params.except('sport_kind').map { |k, v| ["_p_#{k}", [v, 'in'].flatten] }]
}
parameters[:params].each do |param|
if ['_p_matches', '_p_tournaments', '_p_seasons'].include? param[0]
param[1][0] = "{" + param[1][0] + "}" # wrap param with array
end
end
uri = URI('http://service.instatfootball.com/ws.php')
req = Net::HTTP::Post.new(uri, { 'Content-Type' =>'application/json' })
req.body = parameters.to_json
begin
res = Net::HTTP.start(uri.hostname, uri.port, read_timeout: 30) do |http|
http.request(req)
end
return JSON.parse(res.body)['data']
rescue Net::ReadTimeout => e
{ status: 'error', text: e }
end
end
def transform_get_params(get_params)
result = {
server: :main,
base: get_params['sport_kind'],
login: :scout_for_hockey_db,
pass: :dJ8x1Xalefbv,
proc: :scout_get_external_playlist_data,
params: {}
}
get_params.except('sport_kind').each do |param_name, value|
key = "_p_#{param_name}"
result[:params][key] = [value, :in]
end
result
end
def build_title episodes
episodes.each do |episode|
match_id = episode["fname"].split('_')[0].to_i
period_index = episode["fname"].split('_')[1].to_i
str_period = "#{get_period_index(period_index)} #{get_period_name(match_id, period_index)}".strip
episode["title"] = "#{str_period}, #{episode['title']}"
episode['tag_title'] = episode["tag_title"]
end
episodes
end
def get_period_index int
case int
when 1
"1st"
when 2
"2nd"
when 3
"3rd"
when 4
"4th"
end
end
def get_period_name match_id, period_index
return 'overtime 1' if period_index == 5
return 'overtime 2' if period_index == 6
return 'overtime 3' if period_index == 7
return 'overtime 4' if period_index == 8
return 'overtime 5' if period_index == 9
return 'shootouts' if period_index == 255
case
when match_id < 10_000_000
"half"
when (match_id > 10_000_000) && (match_id < 20_000_000)
"period"
when match_id > 20_000_000
"quarter"
end
end
def get_events_array_ts(ev_arr, match_data_arr)
ev_arr.each do |ev|
ev["ts"] = match_data_arr.detect {|match| match["id"] == ev["match_id"]}["ts"]
ev["match"]["dt"] = match_data_arr.detect {|match| match["id"] == ev["match_id"]}["dt"].to_date
ev["match"]["title"] = match_data_arr.detect {|match| match["id"] == ev["match_id"]}["title_en"]
end
ev_arr
end
end
========================== апи ===============================
class Api::V1::Clicker::PlaylistsController < Api::V1::Clicker::BaseController
def index
@playlists = ClickerModel::Playlist.where(user: current_user)
@playlists = @playlists.where(folder_id: params[:folder_id]) if params[:folder_id]
@playlists
end
def create
object_arr = []
case params["object"]
when "folder"
params["data_array"].each do |data|
folder = ClickerModel::Folder.new
folder.name = data["name"]
folder.order = data["order"]
folder.user_id = data["user_id"]
folder.match_id = data["match_id"]
folder.can_be_shared_via_link = data["can_be_shared_via_link"] ||= 0
object_arr << folder
end
when "playlist"
params["data_array"].each do |data|
playlist = ClickerModel::Playlist.new
playlist.title = data["title"]
playlist.order = data["order"]
playlist.folder_id = data["folder_id"]
playlist.user_id = data["user_id"]
playlist.can_be_shared_via_link = data["can_be_shared_via_link"] ||= 0
object_arr << playlist
end
when "episode"
params["data_array"].each do |data|
episode = ClickerModel::Episode.new
episode.title = data["title"]
episode.playlist_id = data["playlist_id"]
episode.match_id = data["match_id"]
episode.mode = data["mode"]
episode.order = data["order"]
episode.period = data["period"].to_i
episode.from_ts = data["from_ts"]
episode.to_ts = data["to_ts"]
episode.drawing_data = data["drawing_data"]
episode.order_position = data["order_position"]
episode.marker_id = data["marker_id"]
episode.fname = data["fname"]
episode.start_video_mode = data["start_video_mode"] ||= 0
object_arr << episode
end
end
_objects = []
begin
object_arr.each_with_index do |object, idx|
object.save
_objects << object.attributes.merge(fake_id: params[:data_array][idx][:fake_id]) # :fake_id по просьбе Тарасова
end
render json: { success: true, action: "create", object: _objects }
rescue Exception => e
render json: { success: false, errors: e.message }
end
end
def update
object_arr = []
case params["object"]
when "folder"
params["data_array"].each do |data|
folder = ClickerModel::Folder.find_by_id(data["id"])
unless folder.nil?
folder.name = data["name"] unless data["name"].nil?
folder.order = data["order"] unless data["order"].nil?
folder.user_id = data["user_id"] unless data["user_id"].nil?
folder.match_id = data["match_id"] unless data["match_id"].nil?
folder.can_be_shared_via_link = data["can_be_shared_via_link"] unless data["can_be_shared_via_link"].nil?
object_arr << folder
end
end
when "playlist"
params["data_array"].each do |data|
playlist = ClickerModel::Playlist.find_by_id(data["id"])
unless playlist.nil?
playlist.title = data["title"] unless data["title"].nil?
playlist.order = data["order"] unless data["order"].nil?
playlist.folder_id = data["folder_id"] unless data["folder_id"].nil?
playlist.user_id = data["user_id"] unless data["user_id"].nil?
playlist.can_be_shared_via_link = data["can_be_shared_via_link"] unless data["can_be_shared_via_link"].nil?
object_arr << playlist
end
end
when "episode"
params["data_array"].each do |data|
episode = ClickerModel::Episode.find_by_id(data["id"])
unless episode.nil?
episode.title = data["title"] unless data["title"].nil?
episode.playlist_id = data["playlist_id"] unless data["playlist_id"].nil?
episode.match_id = data["match_id"] unless data["match_id"].nil?
episode.mode = data["mode"] unless data["mode"].nil?
episode.order = data["order"] unless data["order"].nil?
episode.period = data["period"].to_i unless data["period"].nil?
episode.from_ts = data["from_ts"] unless data["from_ts"].nil?
episode.to_ts = data["to_ts"] unless data["to_ts"].nil?
episode.drawing_data = data["drawing_data"] unless data["drawing_data"].nil?
episode.order_position = data["order_position"] unless data["order_position"].nil?
episode.marker_id = data["marker_id"] unless data["marker_id"].nil?
episode.fname = data["fname"] unless data["fname"].nil?
episode.start_video_mode = data["start_video_mode"] unless data["start_video_mode"].nil?
object_arr << episode
end
end
end
_objects = []
begin
object_arr.each do |object|
object.save
_objects << object.attributes
end
render json: { success: true, action: "update", object: _objects }
rescue => e
render json: { success: false, errors: e.message }
end
end
def destroy
object_arr = []
case params["object"]
when "folder"
params["data_array"].each do |data|
folder = ClickerModel::Folder.find_by(id: data["id"])
unless folder.nil?
object_arr << folder.id
folder.destroy
end
end
when "playlist"
params["data_array"].each do |data|
playlist = ClickerModel::Playlist.find_by(id: data["id"])
unless playlist.nil?
object_arr << playlist.id
playlist.destroy
end
end
when "episode"
params["data_array"].each do |data|
episode = ClickerModel::Episode.find_by(id: data["id"])
unless episode.nil?
object_arr << episode.id
episode.destroy
end
end
end
render json: { success: true, action: "destroy", object: object_arr }
end
def sync
objects_absent = []
objects_out_of_date = []
objects_to_update = []
case params["object"]
when "folder"
obj = ClickerModel::Folder
when "playlist"
obj = ClickerModel::Playlist
when "episode"
obj = ClickerModel::Episode
end
params["data_array"].each do |data|
found_obj = obj.find_by_id(data["id"])
if found_obj.nil?
objects_absent << data["id"]
else
if data["update_dt"].to_time > found_obj.update_dt
objects_out_of_date << found_obj.attributes
elsif data["update_dt"].to_time < found_obj.update_dt
objects_to_update << found_obj.attributes
end
end
end
render json: {success: true,
action: "sync",
objects_absent: objects_absent,
objects_out_of_date: objects_out_of_date,
objects_to_update: objects_to_update,
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment