wco_models 3.1.0.312 → 3.1.0.314

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.txt +19 -4
  3. data/app/assets/stylesheets/wco/main.scss +4 -0
  4. data/app/assets/stylesheets/wco/newsvideos.scss +10 -2
  5. data/app/controllers/wco/api/leads_controller.rb +5 -2
  6. data/app/controllers/wco/api/newspartials_controller.rb +15 -4
  7. data/app/controllers/wco/api/newsproducer_controller.rb +26 -0
  8. data/app/controllers/wco/api/newsvideos_controller.rb +17 -3
  9. data/app/controllers/wco/api/tags_controller.rb +2 -0
  10. data/app/controllers/wco/api/videos_controller.rb +3 -3
  11. data/app/controllers/wco/api_controller.rb +9 -9
  12. data/app/controllers/wco/leadsets_controller.rb +8 -4
  13. data/app/controllers/wco/newsvideos_controller.rb +102 -1
  14. data/app/models/wco/newspartial.rb +7 -3
  15. data/app/models/wco/newsvideo.rb +93 -78
  16. data/app/models/wco/tag.rb +4 -0
  17. data/app/models/wco_email/context.rb +11 -0
  18. data/app/models/wco_email/email_filter_condition.rb +17 -1
  19. data/app/views/wco/api/newsproducer/README.txt +2 -0
  20. data/app/views/wco/{newsproducer → api/newsproducer}/_studio_1.js +111 -104
  21. data/app/views/wco/{newsproducer → api/newsproducer}/studio_1.html.erb +4 -3
  22. data/app/views/wco/api/newsvideos/show.jbuilder +13 -0
  23. data/app/views/wco/leadsets/index.haml +2 -1
  24. data/app/views/wco/newspartials/_form.haml +4 -2
  25. data/app/views/wco/newspartials/_show_in_newsvideo.haml +3 -3
  26. data/app/views/wco/newsvideos/_form.haml +19 -6
  27. data/app/views/wco/newsvideos/edit.haml +2 -1
  28. data/app/views/wco/newsvideos/show.haml +9 -2
  29. data/config/initializers/08_integrations.rb +2 -0
  30. data/config/initializers/08_integrations.rb-example +2 -0
  31. data/config/routes.rb +12 -4
  32. metadata +9 -8
  33. data/app/controllers/wco/api/obfuscated_redirects_controller.rb +0 -26
  34. data/app/controllers/wco/newsproducer_controller.rb +0 -22
  35. /data/app/views/wco/{newsproducer → api/newsproducer}/canvas.html.erb +0 -0
  36. /data/app/views/wco/{newsproducer → api/newsproducer}/canvas_minimal.html.erb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4c36c9a95197b089d3a733fbdb9cb20160205d64a86a86ebd19114e667bdc5d
4
- data.tar.gz: a6f9f64d6768de1debc1850885951a3640413c2999f2c64e391db015a9b9b11a
3
+ metadata.gz: 702f87e01cf8d48d84cb1c0cc9d6c084f86663f4e685024229d862382f3ea744
4
+ data.tar.gz: daf2ffd2b25049f26b84501687413cd307d79b3cee5399bf3115f047ee6bbed9
5
5
  SHA512:
6
- metadata.gz: a0646fa441c8d26c3338fc51816401fe0ccc591e428f25c3429befabe1860915baba33a86e1835c95baf925b62a1a318228cd9c980c773f968b9d01d7ba51ae8
7
- data.tar.gz: '09f82b2e7b79093b15c259976a9af9da76d20ad356775cbc91bc5431dacd5b6bad1cf78b765df3a8d26ca591448bb5074d1b8c80462d1f22180d2a204184068e'
6
+ metadata.gz: 8049284e53db198d9e4df32bf270b48b2441eeafce4d1a6461f9008f424cd15af375869dbd89ef54240847778359a7793ae78b0afe8aaf71cf05674dbb7f603a
7
+ data.tar.gz: 077aa431a75eb8c8231c6ebc4278922271c9b1ad6ee740d4999e32ab7e2c23cba61fdae3bd72afe5b2d2f7413ba71bfaffba5d0056bdb1f28ac043e07136242b
data/README.txt CHANGED
@@ -18,15 +18,30 @@ Some infrastructure is driven by ansible - therefore, local python3 and ansible
18
18
 
19
19
  = Develop / Use =
20
20
 
21
+
22
+ cmd = "#{cmd} [ -f newspartial_#{idx}.mp4 ] || ffmpeg -y -i newspartial_#{idx}.webm -r 24 -pix_fmt yuv420p newspartial_#{idx}.mp4 ; "
23
+
24
+
21
25
  == Image to video ==
22
26
 
23
27
  ffmpeg -loop 1 -i $inn -c:v libx264 -t 3 -pix_fmt yuv420p output.mp4
24
28
 
25
- ## unnecessary:
26
- ffmpeg -loop 1 -i "$inn" -vf "scale=iw:ih" -c:v libx264 -t 3 -pix_fmt yuv420p output.mp4
27
- ## odd pixel:
28
- ffmpeg -loop 1 -i "$inn" -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -c:v libx264 -t 3 -pix_fmt yuv420p output.mp4
29
+ ## illustrative image to video, sized
30
+
31
+ export width=848
32
+ export height=480
33
+ export duration_sec=3
34
+ magick "$inn" -resize "${width}x${height}^" -gravity center -extent "${width}x${height}" "$inn"
35
+ ffmpeg -loop 1 -i $inn -c:v libx264 -t $duration_sec -pix_fmt yuv420p $inn-out.mp4
36
+
37
+ ## image in a news studio
29
38
 
39
+ export image=image.jpg ; \
40
+ export video_base=base_4_5sec.mp4 ; \
41
+ rm output.mp4 ; \
42
+ ffmpeg -i $video_base -i $image \
43
+ -filter_complex "[1:v]scale='min(iw,420)':-1,pad=iw+20:ih+20:10:10:black[img];[0:v][img]overlay=(W-w)/2:(H-h)/2-50" \
44
+ -c:v libx264 -pix_fmt yuv420p -c:a copy output.mp4
30
45
 
31
46
  = Test =
32
47
 
@@ -28,6 +28,10 @@ textarea {
28
28
  background: pink;
29
29
  }
30
30
 
31
+ .align-right {
32
+ text-align: right;
33
+ }
34
+
31
35
  /* B */
32
36
 
33
37
  .blue {
@@ -30,8 +30,13 @@
30
30
 
31
31
  .NewsoverlayTimeline {
32
32
  border: 5px solid green;
33
+ background: white;
33
34
  position: absolute;
34
35
  left: 230px;
36
+
37
+ &:hover {
38
+ z-index: 5;
39
+ }
35
40
  }
36
41
 
37
42
  .NewspartialTimelineW {
@@ -47,8 +52,11 @@
47
52
  width: 400px;
48
53
 
49
54
  .full-text {
50
- position: absolute;
51
- left: 300px;
55
+ // position: absolute;
56
+ // left: 300px;
57
+
58
+ float: left;
59
+ margin-left: 300px;
52
60
 
53
61
  .header {
54
62
  background: #efefef;
@@ -1,8 +1,7 @@
1
1
 
2
2
  class Wco::Api::LeadsController < Wco::ApiController
3
3
 
4
- skip_before_action :decode_jwt
5
- before_action :check_credentials, only: [ :by_email ]
4
+ before_action :decode_secret, only: [ :by_email ]
6
5
 
7
6
  def by_email
8
7
  @lead = Wco::Lead.find_or_create_by_email( params[:email] )
@@ -10,6 +9,7 @@ class Wco::Api::LeadsController < Wco::ApiController
10
9
  end
11
10
 
12
11
  ## select2-leads-ajax
12
+ ## session is present
13
13
  def index
14
14
  authorize! :index, Wco::Lead
15
15
  @leads = Wco::Lead.all
@@ -21,8 +21,11 @@ class Wco::Api::LeadsController < Wco::ApiController
21
21
  { name: /#{q}/i },
22
22
  );
23
23
  end
24
+
25
+ @leads = @leads.limit(100)
24
26
  end
25
27
 
28
+ ## session is present
26
29
  def index_hash
27
30
  authorize! :index, Wco::Lead
28
31
  @leads = Wco::Lead.find( params[:lead_ids].split(',') )
@@ -1,11 +1,11 @@
1
1
 
2
2
  class Wco::Api::NewspartialsController < Wco::ApiController
3
3
 
4
- skip_before_action :decode_jwt
5
- skip_before_action :verify_authenticity_token
6
4
  before_action :decode_simple_api_key
7
5
 
8
-
6
+ ##
7
+ ## PUT config, actually
8
+ ##
9
9
  def add_config
10
10
  puts! params, 'api newspartials#add_config params'
11
11
 
@@ -44,9 +44,20 @@ class Wco::Api::NewspartialsController < Wco::ApiController
44
44
  end
45
45
  end
46
46
 
47
+ ## syncronous, everyone waits.
48
+ def generate_speech
49
+ @newspartial = Wco::Newspartial.unscoped.find params[:id]
50
+ @newspartial.generate_speech
51
+ render json: { status: :ok }
52
+ end
53
+
47
54
  def show_config
48
55
  @newspartial = Wco::Newspartial.find params[:id]
49
- render json: @newspartial.speech_json
56
+ config = JSON.parse @newspartial.speech_json
57
+ config[:w_px] = @newspartial.newsvideo.w_px
58
+ config[:h_px] = @newspartial.newsvideo.h_px
59
+ config[:slug] = "#{@newspartial.newsvideo.slug}::#{@newspartial.slug}"
60
+ render json: config
50
61
  end
51
62
 
52
63
  end
@@ -0,0 +1,26 @@
1
+
2
+ class Wco::Api::NewsproducerController < Wco::ApiController
3
+
4
+ before_action :decode_simple_api_key
5
+
6
+ ## trash
7
+ def canvas
8
+ authorize! :home, Wco
9
+ # render 'canvas_minimal'
10
+ end
11
+
12
+ ## trash
13
+ def canvas_minimal
14
+ authorize! :home, Wco
15
+ render 'canvas_minimal'
16
+ end
17
+
18
+ ##
19
+ ## current
20
+ ##
21
+ def studio_1
22
+ authorize! :home, Wco
23
+ end
24
+
25
+ end
26
+
@@ -1,11 +1,21 @@
1
1
 
2
-
3
2
  class Wco::Api::NewsvideosController < Wco::ApiController
4
3
 
5
- skip_before_action :decode_jwt
6
- skip_before_action :verify_authenticity_token
7
4
  before_action :decode_simple_api_key
8
5
 
6
+ ##
7
+ ## 2026-08-12 _vp_ continue
8
+ ##
9
+ def generate
10
+ @newsvideo = Wco::Newsvideo.unscoped.find params[:id]
11
+
12
+ Rails.env.production? ?
13
+ Wco::NewsvideoGenerateJob.perform_async(@newsvideo.id.to_s) :
14
+ Wco::NewsvideoGenerateJob.perform_sync( @newsvideo.id.to_s)
15
+
16
+ render json: { status: :ok, message: 'Scheduled the generation' }
17
+ end
18
+
9
19
  ##
10
20
  ## the other one is used, not this one. Because I don't want to deal with api keys.
11
21
  ##
@@ -20,4 +30,8 @@ class Wco::Api::NewsvideosController < Wco::ApiController
20
30
  # redirect_to action: 'show', newsvideo_id: params[:id]
21
31
  end
22
32
 
33
+ def show
34
+ @newsvideo = Wco::Newsvideo.unscoped.find params[:id]
35
+ end
36
+
23
37
  end
@@ -1,6 +1,8 @@
1
1
 
2
2
  class Wco::Api::TagsController < Wco::ApiController
3
3
 
4
+ before_action :decode_jwt
5
+
4
6
  def index
5
7
  authorize! :index, Wco::Tag
6
8
  @tags = Wco::Tag.all()
@@ -1,19 +1,19 @@
1
1
 
2
2
  class Wco::Api::VideosController < Wco::ApiController
3
3
 
4
- skip_before_action :decode_jwt
5
- skip_before_action :verify_authenticity_token, only: [:create]
6
4
  before_action :decode_simple_api_key
7
5
 
8
6
  def create
9
7
  # puts! params, 'api videos#create params'
8
+ authorize! :create, Wco::Video
9
+ old_videos = Wco::Video.where({ newspartial_id: params[:newspartial_id] }).map { |v| v.delete }
10
+ puts! old_videos, 'deleted old videos'
10
11
 
11
12
  @video = Wco::Video.new({ name: params[:name],
12
13
  thumb: params[:thumb],
13
14
  video: params[:video],
14
15
  newspartial_id: params[:newspartial_id],
15
16
  })
16
- authorize! :create, @video
17
17
 
18
18
  if @video.save
19
19
  puts! @video, 'Created @video.'
@@ -2,14 +2,21 @@
2
2
  class Wco::ApiController < ActionController::Base
3
3
  layout false
4
4
 
5
- before_action :decode_jwt
5
+ skip_before_action :verify_authenticity_token
6
6
 
7
7
  ##
8
8
  ## private
9
9
  ##
10
10
  private
11
11
 
12
- def check_credentials
12
+ def decode_jwt
13
+ out = JWT.decode params[:jwt_token], nil, false
14
+ email = out[0]['email']
15
+ user = User.find_by({ email: email })
16
+ sign_in user
17
+ end
18
+
19
+ def decode_secret
13
20
  if params[:secret].blank? ||
14
21
  params[:secret] != AWS_SES_LAMBDA_SECRET
15
22
 
@@ -18,13 +25,6 @@ class Wco::ApiController < ActionController::Base
18
25
  end
19
26
  end
20
27
 
21
- def decode_jwt
22
- out = JWT.decode params[:jwt_token], nil, false
23
- email = out[0]['email']
24
- user = User.find_by({ email: email })
25
- sign_in user
26
- end
27
-
28
28
  def decode_simple_api_key
29
29
  if params[:api_key].present? &&
30
30
  params[:api_secret].present? &&
@@ -20,13 +20,17 @@ class Wco::LeadsetsController < Wco::ApplicationController
20
20
  end
21
21
 
22
22
  def destroy
23
- leadsets = Leadset.find( params[:leadset_ids] )
23
+ authorize! :create, Wco::Leadset
24
+ leadsets = Wco::Leadset.find( params[:leadset_ids] ) rescue []
24
25
  @results = []
25
26
  leadsets.each do |leadset|
26
- @results.push leadset.discard
27
+ @results.push leadset.destroy
27
28
  end
28
- flash[:notice] = "Discard outcome: #{@results.inspect}."
29
- redirect_to action: 'index'
29
+ if params[:id]
30
+ @results.push Wco::Leadset.find(params[:id]).destroy
31
+ end
32
+ flash[:notice] = "destroy outcome: #{@results.inspect}."
33
+ redirect_to request.referrer || { action: 'index' }
30
34
  end
31
35
 
32
36
  def edit
@@ -4,7 +4,82 @@ require 'pragmatic_segmenter'
4
4
  class Wco::NewsvideosController < Wco::ApplicationController
5
5
 
6
6
  before_action :set_lists
7
- skip_before_action :verify_authenticity_token, only: [ :generate_illustration ]
7
+ skip_before_action :verify_authenticity_token, only: [ :add_image_as_overlay, :generate_illustration ]
8
+
9
+ def add_image_as_overlay
10
+ duration_sec = 3
11
+
12
+ @newsvideo = Wco::Newsvideo.unscoped.find params[:id]
13
+ authorize! :edit, @newsvideo
14
+
15
+ workdir = Rails.root.join('tmp', @newsvideo.id.to_s)
16
+ FileUtils.mkdir_p(workdir)
17
+
18
+ w_px = @newsvideo.w_px
19
+ h_px = @newsvideo.h_px
20
+ fps = @newsvideo.fps
21
+
22
+ input = workdir.join("overlay_#{@newsvideo.next_overlay_ms}.jpg")
23
+ URI.open(params[:image_url]) do |src|
24
+ File.binwrite(input, src.read)
25
+ end
26
+
27
+ image_path = workdir.join("overlay_#{@newsvideo.next_overlay_ms}_#{w_px}x#{h_px}.jpg")
28
+ cmd = <<~CMD
29
+ magick #{input} \
30
+ -resize "#{w_px}x#{h_px}^" \
31
+ -gravity center \
32
+ -crop "#{w_px}x#{h_px}+0+0" \
33
+ +repage \
34
+ -quality 95 \
35
+ #{image_path}
36
+ CMD
37
+ puts "+++ magick cmd:"
38
+ puts cmd
39
+ out = `#{cmd}`
40
+ puts! out, 'out'
41
+
42
+ video_path = workdir.join("overlay_#{@newsvideo.next_overlay_ms}.mp4")
43
+ cmd = <<~CMD
44
+ ffmpeg -y \
45
+ -loop 1 \
46
+ -i #{image_path} \
47
+ -t #{duration_sec} \
48
+ -r #{fps} \
49
+ -c:v libx264 \
50
+ -pix_fmt yuv420p \
51
+ -vf "scale=#{w_px}:#{h_px}" \
52
+ #{video_path}
53
+ CMD
54
+ puts "+++ ffmpeg cmd:"
55
+ puts cmd
56
+ out = `#{cmd}`
57
+ puts! out, 'out'
58
+
59
+ @video = Wco::Video.new name: "#{@newsvideo.title} overlay #{@newsvideo.next_overlay_ms}"
60
+ @video.video = File.open(video_path)
61
+ @video.thumb = File.open(image_path)
62
+ @video.tags = [ Wco::Tag.illustration, Wco::Tag.find_or_create_by({ slug: @newsvideo.slug }) ]
63
+ flag = @video.save
64
+ if !flag
65
+ puts "Could not create video:"
66
+ puts @video.errors.full_messages.join(", ")
67
+ end
68
+
69
+ @overlay = Wco::Newsoverlay.new({ duration_ms: duration_sec * 1000,
70
+ newsvideo: @newsvideo,
71
+ start_at_ms: @newsvideo.next_overlay_ms,
72
+ video: @video,
73
+ });
74
+ if @overlay.save
75
+ @newsvideo.update( next_overlay_ms: @newsvideo.next_overlay_ms + @overlay.duration_ms )
76
+ else
77
+ puts "Could not create overlay:"
78
+ puts @overlay.errors.full_messages.join(", ")
79
+ end
80
+
81
+ render json: { status: :ok }
82
+ end
8
83
 
9
84
  def create
10
85
  params[:newsvideo][:tag_ids]&.delete ''
@@ -39,6 +114,32 @@ class Wco::NewsvideosController < Wco::ApplicationController
39
114
  render json: { status: :ok, message: 'Scheduled the generation' }
40
115
  end
41
116
 
117
+ def generate_all_audio
118
+ @newsvideo = Wco::Newsvideo.unscoped.find params[:id]
119
+ authorize! :edit, @newsvideo
120
+
121
+ cmd = "cd #{PUPPETEER_ROOT} ; yarn run newsvideo_generate_all_audio #{@newsvideo.id}"
122
+ puts "+++ puppeteer cmd:"
123
+ puts cmd
124
+ out = `#{cmd}`
125
+ puts! out, 'out'
126
+
127
+ render json: { status: :ok }
128
+ end
129
+
130
+ def generate_all_video
131
+ @newsvideo = Wco::Newsvideo.unscoped.find params[:id]
132
+ authorize! :edit, @newsvideo
133
+
134
+ cmd = "cd #{PUPPETEER_ROOT} ; yarn run newsvideo_generate_all_video #{@newsvideo.id}"
135
+ puts "+++ puppeteer cmd:"
136
+ puts cmd
137
+ out = `#{cmd}`
138
+ puts! out, 'out'
139
+
140
+ render json: { status: :ok }
141
+ end
142
+
42
143
  def generate_illustration
43
144
  @newsvideo = Wco::Newsvideo.unscoped.find params[:id]
44
145
  authorize! :edit, @newsvideo
@@ -10,7 +10,7 @@ class Wco::Newspartial
10
10
 
11
11
  PAGE_PARAM_NAME = 'newspartials_page'
12
12
 
13
- MAX_WORDS = 20
13
+ MAX_WORDS = 30
14
14
 
15
15
  field :title
16
16
  # validates :title, presence: true, uniqueness: true
@@ -23,6 +23,8 @@ class Wco::Newspartial
23
23
  belongs_to :newsvideo
24
24
  has_one :video
25
25
 
26
+ field :slug, type: :string
27
+
26
28
  has_mongoid_attached_file :audio,
27
29
  :storage => :s3,
28
30
  :s3_credentials => ::S3_CREDENTIALS,
@@ -66,13 +68,15 @@ class Wco::Newspartial
66
68
  'Accept' => 'application/json',
67
69
  }, body: {
68
70
  input: body,
69
- voice: "af_bella",
71
+ # voice: "am_fenrir", # good?
72
+ # "af_bella" # bad
73
+ voice: "af_jessica", # good
70
74
  language: "en-us",
71
75
  audioEncoding: "wav",
72
76
  }.to_json
73
77
  );
74
78
  out = out.body
75
- # puts! out, 'out'
79
+ puts! out, 'out'
76
80
 
77
81
  self[:config_json] = out
78
82
  tmp = JSON.parse( out )