diff --git a/doc/todo.txt b/doc/todo.txt
index 2ea5236..85bf458 100644
--- a/doc/todo.txt
+++ b/doc/todo.txt
@@ -1,4 +1,3 @@
- etc: test new parser extensively
- etc: check feedburner
-- new: youtube/vimeo iframe generator in "read here"
- fix: loading items (by scrolling down) is glitching while feeds are refreshing
diff --git a/src/assets/index.html b/src/assets/index.html
index 46bf240..5814253 100644
--- a/src/assets/index.html
+++ b/src/assets/index.html
@@ -260,8 +260,10 @@
-
-
+
+
![]()
+
+
diff --git a/src/assets/javascripts/api.js b/src/assets/javascripts/api.js
index 3422465..ead9e8f 100644
--- a/src/assets/javascripts/api.js
+++ b/src/assets/javascripts/api.js
@@ -105,7 +105,7 @@
return api('post', './logout')
},
crawl: function(url) {
- return api('post', './page?url=' + url).then(json)
+ return api('get', './page?url=' + url).then(json)
}
}
})()
diff --git a/src/assets/stylesheets/app.css b/src/assets/stylesheets/app.css
index 277d001..1525250 100644
--- a/src/assets/stylesheets/app.css
+++ b/src/assets/stylesheets/app.css
@@ -386,6 +386,12 @@ select.form-control:not([multiple]):not([size]) {
height: auto;
}
+.content iframe {
+ display: block;
+ max-width: 100%;
+ margin-bottom: 0.5rem;
+}
+
.content pre {
overflow-x: auto;
color: inherit;
diff --git a/src/content/silo/iframe.go b/src/content/silo/iframe.go
new file mode 100644
index 0000000..57fc6d6
--- /dev/null
+++ b/src/content/silo/iframe.go
@@ -0,0 +1,38 @@
+package silo
+
+import (
+ "fmt"
+ "net/url"
+ "regexp"
+ "strings"
+)
+
+var (
+ youtubeFrame = ``
+ vimeoFrame = ``
+ vimeoRegex = regexp.MustCompile(`\/(\d+)$`)
+)
+
+func VideoIFrame(link string) string {
+ l, err := url.Parse(link)
+ if err != nil {
+ return ""
+ }
+
+ youtubeID := ""
+ if l.Host == "www.youtube.com" && l.Path == "/watch" {
+ youtubeID = l.Query().Get("v")
+ } else if l.Host == "youtu.be" {
+ youtubeID = strings.TrimLeft(l.Path, "/")
+ }
+ if youtubeID != "" {
+ return fmt.Sprintf(youtubeFrame, youtubeID)
+ }
+
+ if l.Host == "vimeo.com" {
+ if matches := vimeoRegex.FindStringSubmatch(l.Path); len(matches) > 0 {
+ return fmt.Sprintf(vimeoFrame, matches[1])
+ }
+ }
+ return ""
+}
diff --git a/src/content/silo/iframe_test.go b/src/content/silo/iframe_test.go
new file mode 100644
index 0000000..1b88802
--- /dev/null
+++ b/src/content/silo/iframe_test.go
@@ -0,0 +1,36 @@
+package silo
+
+import "testing"
+
+func TestYoutubeIframe(t *testing.T) {
+ links := []string{
+ "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ "https://youtu.be/dQw4w9WgXcQ",
+ "https://youtu.be/dQw4w9WgXcQ",
+ }
+ for _, link := range links {
+ have := VideoIFrame(link)
+ want := ``
+ if have != want {
+ t.Logf("want: %s", want)
+ t.Logf("have: %s", have)
+ t.Fail()
+ }
+ }
+}
+
+func TestVimeoIframe(t *testing.T) {
+ links := []string{
+ "https://vimeo.com/channels/staffpicks/526381128",
+ "https://vimeo.com/526381128",
+ }
+ for _, link := range links {
+ have := VideoIFrame(link)
+ want := ``
+ if have != want {
+ t.Logf("want: %s", want)
+ t.Logf("have: %s", have)
+ t.Fail()
+ }
+ }
+}
diff --git a/src/server/routes.go b/src/server/routes.go
index daf033c..36a3928 100644
--- a/src/server/routes.go
+++ b/src/server/routes.go
@@ -10,6 +10,7 @@ import (
"github.com/nkanaev/yarr/src/assets"
"github.com/nkanaev/yarr/src/content/readability"
"github.com/nkanaev/yarr/src/content/sanitizer"
+ "github.com/nkanaev/yarr/src/content/silo"
"github.com/nkanaev/yarr/src/server/router"
"github.com/nkanaev/yarr/src/server/auth"
"github.com/nkanaev/yarr/src/server/opml"
@@ -403,19 +404,19 @@ func (s *Server) handleOPMLExport(c *router.Context) {
}
func (s *Server) handlePageCrawl(c *router.Context) {
- if c.Req.Method != "POST" {
- c.Out.WriteHeader(http.StatusBadRequest)
- return
- }
url := c.Req.URL.Query().Get("url")
- if url == "" {
- c.Out.WriteHeader(http.StatusBadRequest)
+
+ if content := silo.VideoIFrame(url); content != "" {
+ c.JSON(http.StatusOK, map[string]string{
+ "content": content,
+ })
return
}
+
res, err := http.Get(url)
if err != nil {
log.Print(err)
- c.Out.WriteHeader(http.StatusNoContent)
+ c.Out.WriteHeader(http.StatusBadRequest)
return
}
defer res.Body.Close()