new_videos.templ
4978 bytes
1package pages
2
3import (
4 "fmt"
5
6 "github.com/TheEdgeOfRage/ytrssil-api/models"
7)
8
9templ ProgressBar(video models.Video) {
10 if video.ProgressSeconds > 0 {
11 <div id={ fmt.Sprintf("progress-%s", video.ID) } class="progress" role="progressbar" style="height: 6px">
12 <div class="progress-bar" style={ fmt.Sprintf("background: #b11; width: %d%%", video.ProgressPercentage()) }></div>
13 </div>
14 } else {
15 <div id={ fmt.Sprintf("progress-%s", video.ID) } style="height: 6px"></div>
16 }
17}
18
19templ thumbnail(video models.Video) {
20 <a
21 target="_blank"
22 rel="noopener"
23 data-open-in-safari
24 href={ templ.SafeURL(video.WatchURL()) }
25 style="display: block; overflow: hidden; position: relative"
26 if video.IsShort {
27 class="pillarbox"
28 }
29 >
30 if video.IsShort {
31 <img
32 src={ fmt.Sprintf("https://img.youtube.com/vi/%s/0.jpg", video.ID) }
33 class="card-img-top"
34 alt={ video.Title }
35 style="width: 100%; aspect-ratio: 16/9; object-fit: contain;"
36 />
37 <svg
38 xmlns="http://www.w3.org/2000/svg"
39 height="24"
40 viewBox="0 0 24 24"
41 width="24"
42 focusable="false"
43 style="pointer-events: none; width: 2rem; height: 2rem; z-index: 2; position: absolute; bottom: 0.5rem; left: 0.5rem;"
44 fill="white"
45 ><path d="m13.974 2.052-8 4.7a4 4 0 00.385 7.097l.942.423-1.327.78a4 4 0 004.052 6.897l8-4.7a4.001 4.001 0 00-.384-7.096L16.7 9.73l1.326-.78a4 4 0 10-4.052-6.897ZM10 15V9l5 3-5 3Z"></path></svg>
46 } else {
47 <img
48 src={ fmt.Sprintf("https://img.youtube.com/vi/%s/0.jpg", video.ID) }
49 class="card-img-top"
50 alt={ video.Title }
51 style="width: 100%; aspect-ratio: 16/9; object-fit: cover;"
52 />
53 }
54 if video.IsLive {
55 <span
56 focusable="false"
57 class="text-white bg-danger px-1 py-1 rounded fw-bold"
58 style="pointer-events: none; z-index: 2; position: absolute; bottom: 0.2rem; right: 0.2rem;"
59 >
60 LIVE
61 </span>
62 } else {
63 <span
64 focusable="false"
65 class="text-body bg-body bg-opacity-75 px-1 py-1 rounded"
66 style="pointer-events: none; z-index: 2; position: absolute; bottom: 0.2rem; right: 0.2rem;"
67 >
68 { video.Duration() }
69 </span>
70 }
71 </a>
72 @ProgressBar(video)
73}
74
75templ VideoCard(video models.Video) {
76 <div id={ fmt.Sprintf("video-card-%s", video.ID) } class="video-card col-md-3 p-2" data-title={ video.Title } data-channel-name={ video.ChannelName }>
77 <div class="card">
78 @thumbnail(video)
79 <div class="card-body">
80 <p class="card-title text-truncate mb-0">{ video.Title }</p>
81 <p class="d-flex justify-content-between">
82 <a
83 target="_blank"
84 rel="noopener"
85 data-open-in-safari
86 href={ fmt.Sprintf("https://www.youtube.com/channel/%s", video.ChannelID) }
87 class="card-text fw-light link-light link-underline-opacity-0"
88 >{ video.ChannelName }</a>
89 <span>
90 { video.HumanizedPublishTime() }
91 </span>
92 </p>
93 <div class="d-flex justify-content-between">
94 <form
95 data-signals="{progress: ''}"
96 data-on:submit__prevent={ fmt.Sprintf("$progress = evt.target.querySelector('input').value; @patch('/videos/%s/progress')", video.ID) }
97 class="d-flex"
98 >
99 <input type="text" placeholder="mm:ss/11m22s" class="form-control" style="width: 50%"/>
100 <button type="submit" class="btn btn-primary ms-3"><i class="bi bi-clock"></i></button>
101 </form>
102 <div class="d-flex gap-2">
103 if video.IsDownloaded() {
104 <a
105 href={ templ.SafeURL(fmt.Sprintf("/videos/%s/file", video.ID)) }
106 data-open-in-safari
107 class="btn btn-success"
108 >
109 <i class="bi bi-download"></i>
110 </a>
111 } else if video.IsDownloading() {
112 <button
113 class="btn btn-primary"
114 disabled
115 data-on-interval__duration.2s={ fmt.Sprintf("@get('/videos/%s/card')", video.ID) }
116 >
117 <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
118 </button>
119 } else if video.DownloadFailed() {
120 <button
121 type="button"
122 class="btn btn-danger"
123 title={ fmt.Sprintf("Download failed: %s", *video.DownloadError) }
124 data-video-id={ video.ID }
125 data-video-title={ video.Title }
126 onclick="openResolutionModal(this)"
127 >
128 <i class="bi bi-exclamation-triangle"></i>
129 </button>
130 } else {
131 <button
132 type="button"
133 class="btn btn-outline-primary"
134 data-video-id={ video.ID }
135 data-video-title={ video.Title }
136 onclick="openResolutionModal(this)"
137 >
138 <i class="bi bi-download"></i>
139 </button>
140 }
141 <button
142 data-on:click={ fmt.Sprintf("@patch('/videos/%s/watch')", video.ID) }
143 class="btn btn-danger"
144 >
145 <i class="bi bi-check-circle"></i>
146 </button>
147 </div>
148 </div>
149 </div>
150 </div>
151 </div>
152}
153
154templ NewVideosPage(videos []models.Video) {
155 @BaseLayout("ytrssil - New Videos", "new") {
156 <div class="row">
157 for _, video := range videos {
158 @VideoCard(video)
159 }
160 </div>
161 }
162}