Add YouTube embed and fix faviconUnverified
c812fa6 parent: 787b9de modified
internal/server/server.go +35 -0 | @@ -229,6 +229,41 @@ func (s *Server) loadTemplates() { | ||
| 229 | 229 | "add": func(a, b int) int { |
| 230 | 230 | 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 | 267 | "sanitizeHTML": func(input string) template.HTML { |
| 233 | 268 | return template.HTML(sanitize.HTML(input)) |
| 234 | 269 | }, |
| @@ -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 + b | 230 | 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 @@ | ||
| 53 | 53 | {{end}} |
| 54 | 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 | 64 | <hr class="my-8 border-spot-divider"> |
| 57 | 65 | |
| 58 | 66 | {{if .Article.Content.Valid}} |
| @@ -63,8 +71,8 @@ | ||
| 63 | 71 | <div class="article-body"> |
| 64 | 72 | {{sanitizeHTML .Article.Summary.String}} |
| 65 | 73 | </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> | |
| 68 | 76 | {{end}} |
| 69 | 77 | </article> |
| 70 | 78 | |
| @@ -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 differ | Binary 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"/> | |
| 14 | 14 | </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> |