bagel 0.1.0

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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.prettierignore +2 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +13 -0
  6. data/CHANGELOG.md +5 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +187 -0
  10. data/Rakefile +12 -0
  11. data/bagel.gemspec +32 -0
  12. data/bin/console +15 -0
  13. data/bin/setup +8 -0
  14. data/lib/bagel/colors.rb +8 -0
  15. data/lib/bagel/fonts.rb +3 -0
  16. data/lib/bagel/graphic/comment.rb +49 -0
  17. data/lib/bagel/graphic/intro.rb +49 -0
  18. data/lib/bagel/graphic/scoreboard/context.rb +28 -0
  19. data/lib/bagel/graphic/scoreboard/games.rb +13 -0
  20. data/lib/bagel/graphic/scoreboard/name.rb +32 -0
  21. data/lib/bagel/graphic/scoreboard/points.rb +13 -0
  22. data/lib/bagel/graphic/scoreboard/score.rb +38 -0
  23. data/lib/bagel/graphic/scoreboard/server.rb +16 -0
  24. data/lib/bagel/graphic/scoreboard/sets.rb +13 -0
  25. data/lib/bagel/graphic/scoreboard.rb +102 -0
  26. data/lib/bagel/graphic/stats/duration.rb +33 -0
  27. data/lib/bagel/graphic/stats/header.rb +32 -0
  28. data/lib/bagel/graphic/stats/result.rb +41 -0
  29. data/lib/bagel/graphic/stats/stat.rb +48 -0
  30. data/lib/bagel/graphic/stats.rb +80 -0
  31. data/lib/bagel/graphic.rb +23 -0
  32. data/lib/bagel/tennis/checker.rb +100 -0
  33. data/lib/bagel/tennis/outcome.rb +48 -0
  34. data/lib/bagel/tennis/player.rb +22 -0
  35. data/lib/bagel/tennis/point.rb +17 -0
  36. data/lib/bagel/tennis/score.rb +68 -0
  37. data/lib/bagel/tennis/scoring.rb +26 -0
  38. data/lib/bagel/tennis/serve.rb +36 -0
  39. data/lib/bagel/tennis/stats/aces.rb +28 -0
  40. data/lib/bagel/tennis/stats/break_points_won.rb +33 -0
  41. data/lib/bagel/tennis/stats/double_faults.rb +28 -0
  42. data/lib/bagel/tennis/stats/first_serve_in.rb +33 -0
  43. data/lib/bagel/tennis/stats/first_serve_points_won.rb +33 -0
  44. data/lib/bagel/tennis/stats/net_points_won.rb +33 -0
  45. data/lib/bagel/tennis/stats/points_won.rb +24 -0
  46. data/lib/bagel/tennis/stats/second_serve_points_won.rb +33 -0
  47. data/lib/bagel/tennis/stats/stat.rb +40 -0
  48. data/lib/bagel/tennis/stats/unforced_errors.rb +24 -0
  49. data/lib/bagel/tennis/stats/winners.rb +24 -0
  50. data/lib/bagel/tennis/stats.rb +16 -0
  51. data/lib/bagel/tennis.rb +13 -0
  52. data/lib/bagel/timeframe.rb +31 -0
  53. data/lib/bagel/version.rb +5 -0
  54. data/lib/bagel/video/clip.rb +41 -0
  55. data/lib/bagel/video/clip_builder_factory.rb +13 -0
  56. data/lib/bagel/video/clip_builders/clip_builder.rb +32 -0
  57. data/lib/bagel/video/clip_builders/intro_clip_builder.rb +37 -0
  58. data/lib/bagel/video/clip_builders/point_clip_builder.rb +67 -0
  59. data/lib/bagel/video/clip_builders/stats_clip_builder.rb +56 -0
  60. data/lib/bagel/video/clip_data_parser.rb +39 -0
  61. data/lib/bagel/video/clip_director.rb +16 -0
  62. data/lib/bagel/video/clip_saver.rb +59 -0
  63. data/lib/bagel/video/fade.rb +24 -0
  64. data/lib/bagel/video/ffmpeg/concat_file.rb +45 -0
  65. data/lib/bagel/video/ffmpeg/fade_filter.rb +34 -0
  66. data/lib/bagel/video/ffmpeg/overlay_filter.rb +52 -0
  67. data/lib/bagel/video/ffmpeg.rb +41 -0
  68. data/lib/bagel/video/orientation.rb +8 -0
  69. data/lib/bagel/video/overlays/comment.rb +15 -0
  70. data/lib/bagel/video/overlays/intro.rb +15 -0
  71. data/lib/bagel/video/overlays/overlay.rb +28 -0
  72. data/lib/bagel/video/overlays/scoreboard.rb +15 -0
  73. data/lib/bagel/video/overlays/second_serve.rb +15 -0
  74. data/lib/bagel/video/overlays/stats.rb +15 -0
  75. data/lib/bagel/video/padding.rb +9 -0
  76. data/lib/bagel/video/video.rb +18 -0
  77. data/lib/bagel/video.rb +32 -0
  78. data/lib/bagel.rb +76 -0
  79. metadata +196 -0
@@ -0,0 +1,33 @@
1
+ module Bagel::Tennis::Stats
2
+ class FirstServeIn < Stat
3
+ private attr_reader :p1_label, :p2_label, :p1_percentage, :p2_percentage
4
+
5
+ def name
6
+ '1st SERVE IN'
7
+ end
8
+
9
+ def values
10
+ [ p1_label, p2_label ]
11
+ end
12
+
13
+ def superior
14
+ [ p1_percentage > p2_percentage, p2_percentage > p1_percentage ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ p1_served = points.select(&:p1_serve?)
21
+ p2_served = points.select(&:p2_serve?)
22
+
23
+ p1_served_no_faults = p1_served.select(&:no_faults?)
24
+ p2_served_no_faults = p2_served.select(&:no_faults?)
25
+
26
+ @p1_percentage = percentage(p1_served.count, p1_served_no_faults.count)
27
+ @p2_percentage = percentage(p2_served.count, p2_served_no_faults.count)
28
+
29
+ @p1_label = label(p1_served.count, p1_served_no_faults.count, p1_percentage)
30
+ @p2_label = label(p2_served.count, p2_served_no_faults.count, p2_percentage)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Bagel::Tennis::Stats
2
+ class FirstServePointsWon < Stat
3
+ private attr_reader :p1_label, :p2_label, :p1_percentage, :p2_percentage
4
+
5
+ def name
6
+ '1st SERVE POINTS WON'
7
+ end
8
+
9
+ def values
10
+ [ p1_label, p2_label ]
11
+ end
12
+
13
+ def superior
14
+ [ p1_percentage > p2_percentage, p2_percentage > p1_percentage ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ p1_first_serves = points.select(&:p1_serve?).select(&:no_faults?)
21
+ p2_first_serves = points.select(&:p2_serve?).select(&:no_faults?)
22
+
23
+ p1_first_serves_won = p1_first_serves.select(&:p1_won?)
24
+ p2_first_serves_won = p2_first_serves.select(&:p2_won?)
25
+
26
+ @p1_percentage = percentage(p1_first_serves.count, p1_first_serves_won.count)
27
+ @p2_percentage = percentage(p2_first_serves.count, p2_first_serves_won.count)
28
+
29
+ @p1_label = label(p1_first_serves.count, p1_first_serves_won.count, p1_percentage)
30
+ @p2_label = label(p2_first_serves.count, p2_first_serves_won.count, p2_percentage)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Bagel::Tennis::Stats
2
+ class NetPointsWon < Stat
3
+ private attr_reader :p1_label, :p2_label, :p1_percentage, :p2_percentage
4
+
5
+ def name
6
+ 'NET POINTS WON'
7
+ end
8
+
9
+ def values
10
+ [ p1_label, p2_label ]
11
+ end
12
+
13
+ def superior
14
+ [ p1_percentage > p2_percentage, p2_percentage > p1_percentage ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ p1_net_points = points.select(&:p1_net_point?)
21
+ p2_net_points = points.select(&:p2_net_point?)
22
+
23
+ p1_net_points_won = p1_net_points.select(&:p1_won?)
24
+ p2_net_points_won = p2_net_points.select(&:p2_won?)
25
+
26
+ @p1_percentage = percentage(p1_net_points.count, p1_net_points_won.count)
27
+ @p2_percentage = percentage(p2_net_points.count, p2_net_points_won.count)
28
+
29
+ @p1_label = label(p1_net_points.count, p1_net_points_won.count, p1_percentage)
30
+ @p2_label = label(p2_net_points.count, p2_net_points_won.count, p2_percentage)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ module Bagel::Tennis::Stats
2
+ class PointsWon < Stat
3
+ private attr_reader :p1, :p2
4
+
5
+ def name
6
+ 'POINTS WON'
7
+ end
8
+
9
+ def values
10
+ [ p1, p2 ]
11
+ end
12
+
13
+ def superior
14
+ [ p1 > p2, p2 > p1 ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ @p1 = points.select(&:p1_won?).count
21
+ @p2 = points.select(&:p2_won?).count
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ module Bagel::Tennis::Stats
2
+ class SecondServePointsWon < Stat
3
+ private attr_reader :p1_label, :p2_label, :p1_percentage, :p2_percentage
4
+
5
+ def name
6
+ '2nd SERVE POINTS WON'
7
+ end
8
+
9
+ def values
10
+ [ p1_label, p2_label ]
11
+ end
12
+
13
+ def superior
14
+ [ p1_percentage > p2_percentage, p2_percentage > p1_percentage ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ p1_second_serves = points.select(&:p1_serve?).select(&:second_serve?)
21
+ p2_second_serves = points.select(&:p2_serve?).select(&:second_serve?)
22
+
23
+ p1_second_serves_won = p1_second_serves.select(&:p1_won?)
24
+ p2_second_serves_won = p2_second_serves.select(&:p2_won?)
25
+
26
+ @p1_percentage = percentage(p1_second_serves.count, p1_second_serves_won.count)
27
+ @p2_percentage = percentage(p2_second_serves.count, p2_second_serves_won.count)
28
+
29
+ @p1_label = label(p1_second_serves.count, p1_second_serves_won.count, p1_percentage)
30
+ @p2_label = label(p2_second_serves.count, p2_second_serves_won.count, p2_percentage)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ module Bagel::Tennis::Stats
2
+ class Stat
3
+ private attr_reader :points
4
+
5
+ def initialize(points)
6
+ @points = points
7
+ calculate
8
+ end
9
+
10
+ def name
11
+ raise_not_implemented_error(self.class, __method__)
12
+ end
13
+
14
+ def values
15
+ raise_not_implemented_error(self.class, __method__)
16
+ end
17
+
18
+ def superior
19
+ raise_not_implemented_error(self.class, __method__)
20
+ end
21
+
22
+ private
23
+
24
+ def raise_not_implemented_error(klass, method)
25
+ raise NotImplementedError, "#{klass} has not implemented method '#{method}'"
26
+ end
27
+
28
+ def calculate
29
+ raise_not_implemented_error(self.class, __method__)
30
+ end
31
+
32
+ def percentage(total, won)
33
+ (won / total.to_f * 100).round rescue 0
34
+ end
35
+
36
+ def label(total, won, percentage)
37
+ "#{won}/#{total} (#{percentage}%)"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module Bagel::Tennis::Stats
2
+ class UnforcedErrors < Stat
3
+ private attr_reader :p1, :p2
4
+
5
+ def name
6
+ 'UNFORCED ERRORS'
7
+ end
8
+
9
+ def values
10
+ [ p1, p2 ]
11
+ end
12
+
13
+ def superior
14
+ [ p1 < p2, p2 < p1 ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ @p1 = points.select(&:p2_won?).select(&:unforced_error?).count
21
+ @p2 = points.select(&:p1_won?).select(&:unforced_error?).count
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Bagel::Tennis::Stats
2
+ class Winners < Stat
3
+ private attr_reader :p1, :p2
4
+
5
+ def name
6
+ 'WINNERS'
7
+ end
8
+
9
+ def values
10
+ [ p1, p2 ]
11
+ end
12
+
13
+ def superior
14
+ [ p1 > p2, p2 > p1 ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ @p1 = points.select(&:p1_won?).select(&:winner?).count
21
+ @p2 = points.select(&:p2_won?).select(&:winner?).count
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require 'bagel/tennis/stats/stat'
2
+ require 'bagel/tennis/stats/aces'
3
+ require 'bagel/tennis/stats/double_faults'
4
+ require 'bagel/tennis/stats/first_serve_in'
5
+ require 'bagel/tennis/stats/first_serve_points_won'
6
+ require 'bagel/tennis/stats/second_serve_points_won'
7
+ require 'bagel/tennis/stats/winners'
8
+ require 'bagel/tennis/stats/unforced_errors'
9
+ require 'bagel/tennis/stats/net_points_won'
10
+ require 'bagel/tennis/stats/break_points_won'
11
+ require 'bagel/tennis/stats/points_won'
12
+
13
+ module Bagel::Tennis
14
+ module Stats
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ require 'bagel/tennis/point'
2
+ require 'bagel/tennis/score'
3
+ require 'bagel/tennis/serve'
4
+ require 'bagel/tennis/outcome'
5
+ require 'bagel/tennis/scoring'
6
+ require 'bagel/tennis/checker'
7
+ require 'bagel/tennis/player'
8
+ require 'bagel/tennis/stats'
9
+
10
+ module Bagel
11
+ module Tennis
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ module Bagel
2
+ class Timeframe
3
+ attr_reader :start, :finish
4
+
5
+ SECONDS_IN_MINUTE = 60
6
+ SECONDS_IN_HOUR = 3600
7
+
8
+ def initialize(start, finish)
9
+ @start = start
10
+ @finish = finish
11
+ end
12
+
13
+ def duration
14
+ seconds(finish) - seconds(start)
15
+ end
16
+
17
+ def duration_minutes
18
+ duration / SECONDS_IN_MINUTE
19
+ end
20
+
21
+ private
22
+
23
+ def seconds(timestamp)
24
+ parts = timestamp.split(':')
25
+ s = parts.pop
26
+ m = parts.pop
27
+ h = parts.pop
28
+ (h.to_i * SECONDS_IN_HOUR) + (m.to_i * SECONDS_IN_MINUTE) + s.to_i
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bagel
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,41 @@
1
+ module Bagel::Video
2
+ class Clip
3
+ attr_accessor :id, :fade, :overlays
4
+ private attr_reader :timeframe
5
+
6
+ EXTENSION = '.mp4'
7
+
8
+ def initialize(timeframe)
9
+ @id = nil
10
+ @fade = nil
11
+ @timeframe = timeframe
12
+ @overlays = []
13
+ end
14
+
15
+ def save
16
+ ClipSaver.new(self).save
17
+ end
18
+
19
+ def start
20
+ timeframe.start
21
+ end
22
+
23
+ def duration
24
+ timeframe.duration
25
+ end
26
+
27
+ def trim_path
28
+ File.join(Bagel.config.trim_dir, filename)
29
+ end
30
+
31
+ def clip_path
32
+ File.join(Bagel.config.clip_dir, filename)
33
+ end
34
+
35
+ private
36
+
37
+ def filename
38
+ "#{id}#{EXTENSION}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ module Bagel::Video
2
+ class ClipBuilderFactory
3
+ BUILDERS = {
4
+ intro: Bagel::Video::ClipBuilders::IntroClipBuilder,
5
+ point: Bagel::Video::ClipBuilders::PointClipBuilder,
6
+ stats: Bagel::Video::ClipBuilders::StatsClipBuilder
7
+ }
8
+
9
+ def self.for(type, data)
10
+ BUILDERS[type].new(data)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ module Bagel::Video::ClipBuilders
2
+ class ClipBuilder
3
+ attr_reader :clip
4
+
5
+ def initialize(data)
6
+ @clip = Video::Clip.new(Timeframe.new(data[:start], data[:finish]))
7
+ post_initialize(data)
8
+ end
9
+
10
+ def set_id
11
+ raise_not_implemented_error(self.class, __method__)
12
+ end
13
+
14
+ def add_clip_fade
15
+ raise_not_implemented_error(self.class, __method__)
16
+ end
17
+
18
+ def add_overlays
19
+ raise_not_implemented_error(self.class, __method__)
20
+ end
21
+
22
+ private
23
+
24
+ def post_initialize(data)
25
+ raise_not_implemented_error(self.class, __method__)
26
+ end
27
+
28
+ def raise_not_implemented_error(klass, method)
29
+ raise NotImplementedError, "#{klass} has not implemented method '#{method}'"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ module Bagel::Video::ClipBuilders
2
+ class IntroClipBuilder < ClipBuilder
3
+ private attr_reader :title, :description
4
+
5
+ ID = '00-intro'
6
+
7
+ def set_id
8
+ clip.id = ID
9
+ end
10
+
11
+ def add_clip_fade
12
+ clip.fade = Video::Fade.new(in_start: 0, in_duration: 1.5, out_start: 6.5, out_duration: 1.5)
13
+ end
14
+
15
+ def add_overlays
16
+ add_intro_overlay
17
+ end
18
+
19
+ private
20
+
21
+ def post_initialize(data)
22
+ @title, @description = data[:title], data[:description]
23
+ end
24
+
25
+ def add_intro_overlay
26
+ clip.overlays << intro_overlay
27
+ end
28
+
29
+ def intro_overlay
30
+ Overlays::Intro.new(intro_graphic)
31
+ end
32
+
33
+ def intro_graphic
34
+ Graphic::Intro.new(title, description).save
35
+ end
36
+ end
37
+ end