nandi/gleanpublic⑂ Fork 0
⑂ c812fa6
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/glean.git
git clone ssh://git@rickub.com/nandi/glean.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Add YouTube embed and fix faviconUnverified

Julien Robert committed 2026-04-20T21:51:15+02:00 Browse files
c812fa6 parent: 787b9de
modified internal/server/server.go +35 -0
@@ -229,6 +229,41 @@ func (s *Server) loadTemplates() {
229229 "add": func(a, b int) int {
230230 return a + b
231231 },
232+ "youtubeID": func(rawURL string) string {
233+ u, err := url.Parse(rawURL)
234+ if err != nil {
235+ return ""
236+ }
237+ host := strings.ToLower(u.Hostname())
238+ if host == "youtu.be" {
239+ id := strings.TrimPrefix(u.Path, "/")
240+ if id != "" {
241+ return id
242+ }
243+ return ""
244+ }
245+ if host == "www.youtube.com" || host == "youtube.com" || host == "m.youtube.com" {
246+ if u.Path == "/watch" || u.Path == "/watch/" {
247+ id := u.Query().Get("v")
248+ if id != "" {
249+ return id
250+ }
251+ }
252+ if strings.HasPrefix(u.Path, "/embed/") {
253+ id := strings.TrimPrefix(u.Path, "/embed/")
254+ if id != "" {
255+ return id
256+ }
257+ }
258+ if strings.HasPrefix(u.Path, "/shorts/") {
259+ id := strings.TrimPrefix(u.Path, "/shorts/")
260+ if id != "" {
261+ return id
262+ }
263+ }
264+ }
265+ return ""
266+ },
232267 "sanitizeHTML": func(input string) template.HTML {
233268 return template.HTML(sanitize.HTML(input))
234269 },
@@ -229,6 +229,41 @@ func (s *Server) loadTemplates() {
229 "add": func(a, b int) int {229 "add": func(a, b int) int {
230 return a + b230 return a + b
231 },231 },
232+ "youtubeID": func(rawURL string) string {
233+ u, err := url.Parse(rawURL)
234+ if err != nil {
235+ return ""
236+ }
237+ host := strings.ToLower(u.Hostname())
238+ if host == "youtu.be" {
239+ id := strings.TrimPrefix(u.Path, "/")
240+ if id != "" {
241+ return id
242+ }
243+ return ""
244+ }
245+ if host == "www.youtube.com" || host == "youtube.com" || host == "m.youtube.com" {
246+ if u.Path == "/watch" || u.Path == "/watch/" {
247+ id := u.Query().Get("v")
248+ if id != "" {
249+ return id
250+ }
251+ }
252+ if strings.HasPrefix(u.Path, "/embed/") {
253+ id := strings.TrimPrefix(u.Path, "/embed/")
254+ if id != "" {
255+ return id
256+ }
257+ }
258+ if strings.HasPrefix(u.Path, "/shorts/") {
259+ id := strings.TrimPrefix(u.Path, "/shorts/")
260+ if id != "" {
261+ return id
262+ }
263+ }
264+ }
265+ return ""
266+ },
232 "sanitizeHTML": func(input string) template.HTML {267 "sanitizeHTML": func(input string) template.HTML {
233 return template.HTML(sanitize.HTML(input))268 return template.HTML(sanitize.HTML(input))
234 },269 },
modified internal/tmpl/article_detail.html +10 -2
@@ -53,6 +53,14 @@
5353 {{end}}
5454 </div>
5555
56+ {{if .Article.URL.Valid}}
57+ {{with youtubeID .Article.URL.String}}
58+ <div class="my-6 aspect-video w-full rounded-xl overflow-hidden shadow-spot">
59+ <iframe class="w-full h-full" src="https://www.youtube.com/embed/{{.}}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
60+ </div>
61+ {{end}}
62+ {{end}}
63+
5664 <hr class="my-8 border-spot-divider">
5765
5866 {{if .Article.Content.Valid}}
@@ -63,8 +71,8 @@
6371 <div class="article-body">
6472 {{sanitizeHTML .Article.Summary.String}}
6573 </div>
66- {{else}}
67- <p class="text-spot-secondary">No content available. <a href="{{.Article.URL.String}}" class="text-spot-green hover:underline">Read the original article.</a></p>
74+ {{else if not .Article.URL.Valid}}
75+ <p class="text-spot-secondary">No content available.</p>
6876 {{end}}
6977 </article>
7078
@@ -53,6 +53,14 @@
53 {{end}}53 {{end}}
54 </div>54 </div>
55 55
56+ {{if .Article.URL.Valid}}
57+ {{with youtubeID .Article.URL.String}}
58+ <div class="my-6 aspect-video w-full rounded-xl overflow-hidden shadow-spot">
59+ <iframe class="w-full h-full" src="https://www.youtube.com/embed/{{.}}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
60+ </div>
61+ {{end}}
62+ {{end}}
63+
56 <hr class="my-8 border-spot-divider">64 <hr class="my-8 border-spot-divider">
57 65
58 {{if .Article.Content.Valid}}66 {{if .Article.Content.Valid}}
@@ -63,8 +71,8 @@
63 <div class="article-body">71 <div class="article-body">
64 {{sanitizeHTML .Article.Summary.String}}72 {{sanitizeHTML .Article.Summary.String}}
65 </div>73 </div>
66- {{else}}74+ {{else if not .Article.URL.Valid}}
67- <p class="text-spot-secondary">No content available. <a href="{{.Article.URL.String}}" class="text-spot-green hover:underline">Read the original article.</a></p>75+ <p class="text-spot-secondary">No content available.</p>
68 {{end}}76 {{end}}
69 </article>77 </article>
70 78
added static/favicon.png +0 -0
new file mode 100644
Binary files /dev/null and b/static/favicon.png differ
new file mode 100644
Binary files /dev/null and b/static/favicon.png differBinary files /dev/null and b/static/favicon.png differ
modified static/favicon.svg +13 -13
@@ -1,14 +1,14 @@
1-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
2- <rect width="32" height="32" rx="8" fill="#1E3932"/>
3- <ellipse cx="16" cy="20" rx="5.5" ry="6" fill="#00754A"/>
4- <path d="M10.5 19.5c-2.5-1-5.5-4-5-7.5.2-1.2 1-1.8 1.8-1.5 2 .7 3.5 4 3.8 7" fill="#00754A" fill-opacity=".5"/>
5- <path d="M21.5 19.5c2.5-1 5.5-4 5-7.5-.2-1.2-1-1.8-1.8-1.5-2 .7-3.5 4-3.8 7" fill="#00754A" fill-opacity=".5"/>
6- <line x1="13" y1="17" x2="19" y2="17" stroke="#1E3932" stroke-width="1.2"/>
7- <line x1="12.5" y1="20" x2="19.5" y2="20" stroke="#1E3932" stroke-width="1.2"/>
8- <line x1="13" y1="23" x2="19" y2="23" stroke="#1E3932" stroke-width="1.2"/>
9- <circle cx="13.5" cy="11" r="1.8" fill="#00754A"/>
10- <circle cx="18.5" cy="11" r="1.8" fill="#00754A"/>
11- <line x1="13.5" y1="9.2" x2="12" y2="6.5" stroke="#00754A" stroke-width="1.3" stroke-linecap="round"/>
12- <line x1="18.5" y1="9.2" x2="20" y2="6.5" stroke="#00754A" stroke-width="1.3" stroke-linecap="round"/>
13- <path d="M13 26c.5 1 1.5 1.5 3 1.5s2.5-.5 3-1.5" stroke="#00754A" stroke-width="1.2" stroke-linecap="round" fill="none"/>
1+<svg class="w-full h-full" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2+ <rect width="32" height="32" rx="8" fill="#00754A"/>
3+ <ellipse cx="16" cy="20" rx="5.5" ry="6" fill="#fff"/>
4+ <path d="M10.5 19.5c-2.5-1-5.5-4-5-7.5.2-1.2 1-1.8 1.8-1.5 2 .7 3.5 4 3.8 7" fill="#fff" fill-opacity=".5"/>
5+ <path d="M21.5 19.5c2.5-1 5.5-4 5-7.5-.2-1.2-1-1.8-1.8-1.5-2 .7-3.5 4-3.8 7" fill="#fff" fill-opacity=".5"/>
6+ <line x1="13" y1="17" x2="19" y2="17" stroke="#00754A" stroke-width="1.2"/>
7+ <line x1="12.5" y1="20" x2="19.5" y2="20" stroke="#00754A" stroke-width="1.2"/>
8+ <line x1="13" y1="23" x2="19" y2="23" stroke="#00754A" stroke-width="1.2"/>
9+ <circle cx="13.5" cy="11" r="1.8" fill="#fff"/>
10+ <circle cx="18.5" cy="11" r="1.8" fill="#fff"/>
11+ <line x1="13.5" y1="9.2" x2="12" y2="6.5" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/>
12+ <line x1="18.5" y1="9.2" x2="20" y2="6.5" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/>
13+ <path d="M13 26c.5 1 1.5 1.5 3 1.5s2.5-.5 3-1.5" stroke="#fff" stroke-width="1.2" stroke-linecap="round" fill="none"/>
1414 </svg>
@@ -1,14 +1,14 @@
1-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">1+<svg class="w-full h-full" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2- <rect width="32" height="32" rx="8" fill="#1E3932"/>2+ <rect width="32" height="32" rx="8" fill="#00754A"/>
3- <ellipse cx="16" cy="20" rx="5.5" ry="6" fill="#00754A"/>3+ <ellipse cx="16" cy="20" rx="5.5" ry="6" fill="#fff"/>
4- <path d="M10.5 19.5c-2.5-1-5.5-4-5-7.5.2-1.2 1-1.8 1.8-1.5 2 .7 3.5 4 3.8 7" fill="#00754A" fill-opacity=".5"/>4+ <path d="M10.5 19.5c-2.5-1-5.5-4-5-7.5.2-1.2 1-1.8 1.8-1.5 2 .7 3.5 4 3.8 7" fill="#fff" fill-opacity=".5"/>
5- <path d="M21.5 19.5c2.5-1 5.5-4 5-7.5-.2-1.2-1-1.8-1.8-1.5-2 .7-3.5 4-3.8 7" fill="#00754A" fill-opacity=".5"/>5+ <path d="M21.5 19.5c2.5-1 5.5-4 5-7.5-.2-1.2-1-1.8-1.8-1.5-2 .7-3.5 4-3.8 7" fill="#fff" fill-opacity=".5"/>
6- <line x1="13" y1="17" x2="19" y2="17" stroke="#1E3932" stroke-width="1.2"/>6+ <line x1="13" y1="17" x2="19" y2="17" stroke="#00754A" stroke-width="1.2"/>
7- <line x1="12.5" y1="20" x2="19.5" y2="20" stroke="#1E3932" stroke-width="1.2"/>7+ <line x1="12.5" y1="20" x2="19.5" y2="20" stroke="#00754A" stroke-width="1.2"/>
8- <line x1="13" y1="23" x2="19" y2="23" stroke="#1E3932" stroke-width="1.2"/>8+ <line x1="13" y1="23" x2="19" y2="23" stroke="#00754A" stroke-width="1.2"/>
9- <circle cx="13.5" cy="11" r="1.8" fill="#00754A"/>9+ <circle cx="13.5" cy="11" r="1.8" fill="#fff"/>
10- <circle cx="18.5" cy="11" r="1.8" fill="#00754A"/>10+ <circle cx="18.5" cy="11" r="1.8" fill="#fff"/>
11- <line x1="13.5" y1="9.2" x2="12" y2="6.5" stroke="#00754A" stroke-width="1.3" stroke-linecap="round"/>11+ <line x1="13.5" y1="9.2" x2="12" y2="6.5" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/>
12- <line x1="18.5" y1="9.2" x2="20" y2="6.5" stroke="#00754A" stroke-width="1.3" stroke-linecap="round"/>12+ <line x1="18.5" y1="9.2" x2="20" y2="6.5" stroke="#fff" stroke-width="1.3" stroke-linecap="round"/>
13- <path d="M13 26c.5 1 1.5 1.5 3 1.5s2.5-.5 3-1.5" stroke="#00754A" stroke-width="1.2" stroke-linecap="round" fill="none"/>13+ <path d="M13 26c.5 1 1.5 1.5 3 1.5s2.5-.5 3-1.5" stroke="#fff" stroke-width="1.2" stroke-linecap="round" fill="none"/>
14 </svg>14 </svg>