Skip to content

Commit 5a40d85

Browse files
authored
Add video embed feature to splashpage (#74)
* Add video embed feature to splashpage Allows admins to configure a video URL that will be displayed on the conference splashpage with autoplay. Supports multiple video platforms: YouTube, Vimeo, PeerTube, and Invidious. Features: - Multi-platform video support with automatic platform detection - Video autoplay with mute for browser compatibility - Responsive 16:9 aspect ratio video player - Extensible architecture for adding new platforms - Full i18n support (English and Spanish) - Comprehensive model tests Implementation: - Add video_url field to splashpages table - Platform detection using regex patterns - Automatic embed URL generation with autoplay parameters - Admin form with video URL field and validation - Model validations for URL format and supported platforms Closes #8 * Update concurrent-ruby
1 parent dc57207 commit 5a40d85

File tree

14 files changed

+468
-2
lines changed

14 files changed

+468
-2
lines changed

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ GEM
169169
rest-client (>= 2.0.0)
170170
cocoon (1.2.15)
171171
colorize (0.8.1)
172-
concurrent-ruby (1.3.5)
172+
concurrent-ruby (1.3.4)
173173
countable-rails (0.0.1)
174174
railties (>= 3.1)
175175
crack (0.4.5)

app/assets/stylesheets/osem-splash.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,26 @@
173173
color: white;
174174
}
175175
}
176+
177+
#video {
178+
.video-wrapper {
179+
position: relative;
180+
padding-bottom: 56.25%; /* 16:9 aspect ratio */
181+
padding-top: 25px;
182+
height: 0;
183+
max-width: 1080px;
184+
margin: 0 auto;
185+
186+
iframe {
187+
position: absolute;
188+
top: 0;
189+
left: 0;
190+
width: 100%;
191+
height: 100%;
192+
border: none;
193+
}
194+
}
195+
}
176196
}
177197

178198
.publicorprivate {

app/controllers/admin/splashpages_controller.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ def splashpage_params
5050
:include_venue, :include_registrations,
5151
:include_tickets, :include_lodgings,
5252
:include_sponsors, :include_social_media,
53-
:include_booths, :include_past_editions)
53+
:include_booths, :include_past_editions,
54+
:video_url)
5455
end
5556
end
5657
end

app/models/splashpage.rb

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,109 @@ class Splashpage < ApplicationRecord
44
belongs_to :conference
55

66
has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id }
7+
8+
validates :video_url,
9+
format: { with: URI::regexp(%w(http https)), message: :invalid_url },
10+
allow_blank: true
11+
12+
validate :video_url_is_supported_platform, if: :video_url?
13+
14+
# Configuration for supported video platforms
15+
# To add a new platform: add an entry with patterns (regex) and embed_template
16+
SUPPORTED_PLATFORMS = {
17+
youtube: {
18+
patterns: [
19+
/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|m\.youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/
20+
],
21+
embed_template: 'https://www.youtube.com/embed/%{video_id}',
22+
autoplay_params: 'autoplay=1&loop=1&mute=1&controls=0&disablekb=1&fs=0&iv_load_policy=3&modestbranding=1&playsinline=1'
23+
},
24+
vimeo: {
25+
patterns: [/vimeo\.com\/(?:video\/)?(\d+)/],
26+
embed_template: 'https://player.vimeo.com/video/%{video_id}',
27+
autoplay_params: 'autoplay=1&muted=1&loop=1&playsinline=1'
28+
},
29+
peertube: {
30+
patterns: [
31+
/(https?:\/\/[^\/]+)\/videos\/watch\/([a-zA-Z0-9-]+)/,
32+
/(https?:\/\/[^\/]+)\/w\/([a-zA-Z0-9-]+)/
33+
],
34+
embed_template: '%{instance_url}/videos/embed/%{video_id}',
35+
autoplay_params: 'autoplay=1&loop=1'
36+
},
37+
invidious: {
38+
patterns: [/(https?:\/\/[^\/]+)\/watch\?v=([a-zA-Z0-9_-]{11})/],
39+
embed_template: '%{instance_url}/embed/%{video_id}',
40+
autoplay_params: 'autoplay=1&loop=1'
41+
}
42+
}.freeze
43+
44+
# Detect platform from video URL
45+
def video_platform
46+
return nil unless video_url.present?
47+
48+
SUPPORTED_PLATFORMS.find do |platform, config|
49+
config[:patterns].any? { |pattern| video_url.match?(pattern) }
50+
end&.first
51+
end
52+
53+
# Extract video ID from URL
54+
def video_id
55+
match_data = url_match_data
56+
return nil unless match_data
57+
58+
# For federated platforms (PeerTube, Invidious), video_id is the second capture
59+
match_data.captures.length > 1 ? match_data[2] : match_data[1]
60+
end
61+
62+
# Extract instance URL for federated platforms (PeerTube, Invidious)
63+
def video_instance_url
64+
return nil unless [:peertube, :invidious].include?(video_platform)
65+
66+
match_data = url_match_data
67+
match_data&.captures&.length.to_i > 1 ? match_data[1] : nil
68+
end
69+
70+
# Generate complete embed URL with autoplay parameters
71+
def video_embed_url
72+
return nil unless video_platform && video_id
73+
74+
config = SUPPORTED_PLATFORMS[video_platform]
75+
base_url = build_base_url(config)
76+
77+
"#{base_url}?#{config[:autoplay_params]}"
78+
end
79+
80+
private
81+
82+
# Get regex match data for the current video URL
83+
def url_match_data
84+
return nil unless video_url.present? && video_platform.present?
85+
86+
config = SUPPORTED_PLATFORMS[video_platform]
87+
config[:patterns].each do |pattern|
88+
match = video_url.match(pattern)
89+
return match if match
90+
end
91+
92+
nil
93+
end
94+
95+
# Build base embed URL from template
96+
def build_base_url(config)
97+
template = config[:embed_template]
98+
99+
if [:peertube, :invidious].include?(video_platform)
100+
template % { instance_url: video_instance_url, video_id: video_id }
101+
else
102+
template % { video_id: video_id }
103+
end
104+
end
105+
106+
# Validation: ensure URL is from a supported platform
107+
def video_url_is_supported_platform
108+
return if video_platform.present?
109+
110+
errors.add(:video_url, :unsupported_platform)
111+
end
7112
end

app/views/admin/splashpages/_form.html.haml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@
4545
%label
4646
= f.check_box :include_social_media
4747
Display the social media links?
48+
%h4
49+
= t('admin.splashpages.form.video.title')
50+
%hr
51+
.form-group
52+
= f.label :video_url
53+
= f.url_field :video_url,
54+
class: 'form-control',
55+
placeholder: t('admin.splashpages.form.video.placeholder')
56+
%p.help-block
57+
= t('admin.splashpages.form.video.help_html').html_safe
4858
%h4
4959
Access
5060
%hr

app/views/conferences/_video.haml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
- cache [splashpage, splashpage.video_url, '#splash#video'] do
2+
- if splashpage.video_embed_url.present?
3+
%section#video
4+
.container
5+
.row
6+
.col-md-8.col-md-offset-2
7+
.video-wrapper
8+
%iframe#video-iframe{
9+
src: splashpage.video_embed_url,
10+
frameborder: "0",
11+
allowfullscreen: "",
12+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
13+
title: "Conference Video"
14+
}

app/views/conferences/show.html.haml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
-# header/description
2525
= render 'header', conference: @conference, venue: @conference.venue
2626

27+
-# Video section
28+
= render 'video', splashpage: @conference.splashpage
29+
2730
-# calls for content, or program
2831
- if @conference.splashpage.include_cfp
2932
= render 'call_for_content', conference: @conference,

config/locales/en.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ en:
4848
nickname: Nickname
4949
biography: Biography
5050
affiliation: Affiliation
51+
splashpage:
52+
video_url: Video URL
53+
errors:
54+
models:
55+
splashpage:
56+
attributes:
57+
video_url:
58+
invalid_url: must be a valid URL
59+
unsupported_platform: must be from a supported platform (Youtube, Vimeo, Peertube, Invidious)

config/locales/es.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,12 @@ es:
1313
nickname: Nickname
1414
biography: Biografía
1515
affiliation: Afiliación
16+
splashpage:
17+
video_url: URL del vídeo
18+
errors:
19+
models:
20+
splashpage:
21+
attributes:
22+
video_url:
23+
invalid_url: debe ser una URL válida
24+
unsupported_platform: debe ser de una plataforma soportada (Youtube, Vimeo, Peertube, Invidious)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
en:
2+
admin:
3+
splashpages:
4+
form:
5+
video:
6+
title: Video
7+
label: Video URL
8+
placeholder: "https://www.youtube.com/watch?v=..."
9+
help_html: |
10+
Video URL to display on the splash page (will autoplay muted).
11+
<br>
12+
Supported: <strong>YouTube, Vimeo, PeerTube, Invidious</strong>

0 commit comments

Comments
 (0)