product_tours 1.2.0 → 1.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/README.md +1 -1
- data/app/models/product_tours/tour.rb +2 -1
- data/app/views/product_tours/tours/_form.html.erb +3 -3
- data/lib/product_tours/version.rb +1 -1
- data/lib/product_tours/video_resolver.rb +10 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c62b8b42042732f293443f7cdfc154b1679fd7b0ba0c6774aca4ade157a54fa
|
|
4
|
+
data.tar.gz: 88dc14f258d8e5bd14260fe40dbf77c0c58e8adc7816f26a6bb31c4cbd4122b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 705534e0de88c34651aa56751e663f4a2d31830b935d09e65a0d8a3c86bc56026630e55e009c5ad82489a6684c693a6395a5f7b9c855c263bee33295fd6510ab
|
|
7
|
+
data.tar.gz: 4804cc836bac346588ccbab6c547b205fb6e17d45163b02d34cab395d6c272cabce2870793abaaca128691973845ff1ab19b0565f32cfe978ddd4e7a875a124c
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.3.0] - 2026-11-09
|
|
6
|
+
|
|
7
|
+
- **A direct video can be a path on the host app**, such as
|
|
8
|
+
`/tutorials/billing.en.mp4`, like the poster and captions already could. The
|
|
9
|
+
tour then plays from whichever environment holds the record, rather than
|
|
10
|
+
pointing a copied staging or development database at production media.
|
|
11
|
+
Protocol-relative (`//host/…`) and bare relative paths are still rejected.
|
|
12
|
+
- The dashboard's video field is now a text field, since the browser's URL
|
|
13
|
+
field refuses a path. It costs the browser's own URL check; the model's
|
|
14
|
+
validation still decides.
|
|
15
|
+
|
|
5
16
|
## [1.2.0] - 2026-11-03
|
|
6
17
|
|
|
7
18
|
- **Direct and uploaded videos can carry a poster and captions.** A tutorial
|
|
@@ -242,7 +253,8 @@
|
|
|
242
253
|
- Removed generic tutorial duplication now that translations provide the only
|
|
243
254
|
intentional content-copying workflow.
|
|
244
255
|
|
|
245
|
-
[Unreleased]: https://github.com/yshmarov/product_tours/compare/v1.
|
|
256
|
+
[Unreleased]: https://github.com/yshmarov/product_tours/compare/v1.3.0...HEAD
|
|
257
|
+
[1.3.0]: https://github.com/yshmarov/product_tours/compare/v1.2.0...v1.3.0
|
|
246
258
|
[1.2.0]: https://github.com/yshmarov/product_tours/compare/v1.1.0...v1.2.0
|
|
247
259
|
[1.1.0]: https://github.com/yshmarov/product_tours/compare/v1.0.0...v1.1.0
|
|
248
260
|
[1.0.0]: https://github.com/yshmarov/product_tours/compare/v0.3.3...v1.0.0
|
data/README.md
CHANGED
|
@@ -144,7 +144,7 @@ Paste any supported HTTPS URL:
|
|
|
144
144
|
| Loom | Share and embed links |
|
|
145
145
|
| Tella | Video links |
|
|
146
146
|
| Voomly | Share, video, and embed links |
|
|
147
|
-
| Direct | URLs ending in `.mp4` or `.webm` |
|
|
147
|
+
| Direct | HTTPS URLs or paths on your app (`/videos/intro.mp4`) ending in `.mp4` or `.webm` |
|
|
148
148
|
|
|
149
149
|
YouTube, Vimeo, and Loom metadata comes from their oEmbed endpoints. Metadata is
|
|
150
150
|
best-effort: a timeout never prevents you from saving a valid URL. YouTube uses
|
|
@@ -110,7 +110,8 @@ module ProductTours
|
|
|
110
110
|
def video_url_is_supported
|
|
111
111
|
return if video_url.blank? || VideoResolver.resolve(video_url)
|
|
112
112
|
|
|
113
|
-
errors.add(:video_url,
|
|
113
|
+
errors.add(:video_url,
|
|
114
|
+
'must be an HTTPS YouTube, Vimeo, Loom, Tella, Voomly, MP4, or WebM URL, or an MP4 or WebM path')
|
|
114
115
|
end
|
|
115
116
|
|
|
116
117
|
def action_url_is_safe
|
|
@@ -89,9 +89,9 @@
|
|
|
89
89
|
<div data-video-source-fields="url" <%= "hidden" unless video_source == "url" %>>
|
|
90
90
|
<div class="field">
|
|
91
91
|
<%= form.label :video_url, t("product_tours.dashboard.video_url", default: "Video URL") %>
|
|
92
|
-
<%= form.
|
|
93
|
-
|
|
94
|
-
<small><%= t("product_tours.dashboard.video_hint", default: "YouTube, Vimeo, Loom, Tella, Voomly, or a direct MP4/WebM URL.") %></small>
|
|
92
|
+
<%= form.text_field :video_url, placeholder: "https://www.loom.com/share/...",
|
|
93
|
+
disabled: video_source != "url", data: { video_preview_input: true } %>
|
|
94
|
+
<small><%= t("product_tours.dashboard.video_hint", default: "YouTube, Vimeo, Loom, Tella, Voomly, or a direct MP4/WebM URL or path.") %></small>
|
|
95
95
|
</div>
|
|
96
96
|
<div class="form-video-preview" data-video-preview
|
|
97
97
|
data-endpoint="<%= video_preview_tours_path %>"
|
|
@@ -33,6 +33,8 @@ module ProductTours
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def resolve
|
|
36
|
+
return relative if @url.start_with?('/') && !@url.start_with?('//')
|
|
37
|
+
|
|
36
38
|
uri = URI.parse(@url)
|
|
37
39
|
return unless uri.is_a?(URI::HTTPS) && uri.host.present? && uri.userinfo.nil?
|
|
38
40
|
|
|
@@ -110,6 +112,14 @@ module ProductTours
|
|
|
110
112
|
Result.new(kind: 'iframe', provider: 'voomly', provider_id: id, url: embed)
|
|
111
113
|
end
|
|
112
114
|
|
|
115
|
+
# A path on the host app's own origin, so a tour plays from whichever environment holds the record.
|
|
116
|
+
def relative
|
|
117
|
+
uri = URI.parse(@url)
|
|
118
|
+
direct(uri) if uri.host.nil? && uri.scheme.nil?
|
|
119
|
+
rescue URI::InvalidURIError
|
|
120
|
+
nil
|
|
121
|
+
end
|
|
122
|
+
|
|
113
123
|
def direct(uri)
|
|
114
124
|
return unless uri.path.match?(/\.(?:mp4|webm)\z/i)
|
|
115
125
|
|