|
5 | 5 | "io" |
6 | 6 | "log" |
7 | 7 | "net/http" |
| 8 | + "strconv" |
8 | 9 | "strings" |
9 | 10 | "text/template" |
10 | 11 | "time" |
@@ -246,6 +247,125 @@ func (h *Handler) AlbumListHandler(c echo.Context) error { |
246 | 247 | return c.HTML(200, sb.String()) |
247 | 248 | } |
248 | 249 |
|
| 250 | +func (h *Handler) ImportStartHandler(c echo.Context) error { |
| 251 | + if err := validateAuth(c); err != nil { |
| 252 | + return err |
| 253 | + } |
| 254 | + urls := strings.Split(c.FormValue("bandcampUrls"), "\n") |
| 255 | + var albumRows strings.Builder |
| 256 | + var urlList []string |
| 257 | + for i, url := range urls { |
| 258 | + url = strings.TrimSpace(url) |
| 259 | + if url == "" { |
| 260 | + continue |
| 261 | + } |
| 262 | + urlList = append(urlList, url) |
| 263 | + albumRows.WriteString(`<div id="album-status-` + fmt.Sprint(i) + `" class="flex items-center gap-2 py-2 border-b border-gray-700">`) |
| 264 | + albumRows.WriteString(`<span class="w-8 h-8 flex items-center justify-center bg-gray-700 rounded-full text-gray-400">`) |
| 265 | + albumRows.WriteString(fmt.Sprint(i + 1)) |
| 266 | + albumRows.WriteString(`</span>`) |
| 267 | + albumRows.WriteString(`<span class="flex-1 text-gray-200">` + template.HTMLEscapeString(url) + `</span>`) |
| 268 | + albumRows.WriteString(`<span class="text-yellow-400">Pending</span>`) |
| 269 | + albumRows.WriteString(`</div>`) |
| 270 | + } |
| 271 | + progressBar := `<div class="w-full bg-gray-700 rounded-full h-4 mb-4"> |
| 272 | + <div id="import-progress-bar" class="bg-blue-600 h-4 rounded-full" style="width:0%"></div> |
| 273 | + </div>` |
| 274 | + html := `<div id="import-progress-container">` + progressBar + `<div id="import-album-status-list">` + albumRows.String() + `</div></div>` |
| 275 | + // Add a script to trigger the first import if there are any URLs |
| 276 | + if len(urlList) > 0 { |
| 277 | + html += `<div hx-post="/admin/import/process" |
| 278 | + hx-target="#album-status-0" |
| 279 | + hx-swap="outerHTML" |
| 280 | + hx-vals='{"albumIndex":0,"total":` + fmt.Sprint(len(urlList)) + `,"bandcampUrls":"` + template.JSEscapeString(strings.Join(urlList, "\\n")) + `"}' |
| 281 | + hx-trigger="load"></div>` |
| 282 | + // Add a script to reload the import tab after a delay (when all are done) |
| 283 | + html += `<div id="import-finish-trigger"></div>` |
| 284 | + html += `<script> |
| 285 | + function checkImportDone() { |
| 286 | + var lastRow = document.getElementById('album-status-` + fmt.Sprint(len(urlList)-1) + `'); |
| 287 | + if (lastRow && lastRow.querySelector('.text-green-400, .text-red-400, .text-yellow-400')) { |
| 288 | + setTimeout(function() { |
| 289 | + htmx.ajax('GET', '/admin/content/import', {target: '#admin-content', swap: 'innerHTML'}); |
| 290 | + document.getElementById('import-progress-global').innerHTML = ''; |
| 291 | + setTimeout(function() { |
| 292 | + var msg = document.getElementById('import-status-message'); |
| 293 | + if (msg) msg.innerHTML = '<div class="bg-green-700 text-white p-2 rounded mb-2">Import complete!</div>'; |
| 294 | + }, 500); |
| 295 | + }, 1000); |
| 296 | + } else { |
| 297 | + setTimeout(checkImportDone, 500); |
| 298 | + } |
| 299 | + } |
| 300 | + checkImportDone(); |
| 301 | + </script>` |
| 302 | + } |
| 303 | + return c.HTML(200, html) |
| 304 | +} |
| 305 | + |
| 306 | +func (h *Handler) ImportProcessHandler(c echo.Context) error { |
| 307 | + if err := validateAuth(c); err != nil { |
| 308 | + return err |
| 309 | + } |
| 310 | + albumIndex, _ := strconv.Atoi(c.FormValue("albumIndex")) |
| 311 | + total, _ := strconv.Atoi(c.FormValue("total")) |
| 312 | + urls := strings.Split(c.FormValue("bandcampUrls"), "\n") |
| 313 | + if albumIndex >= len(urls) { |
| 314 | + return nil |
| 315 | + } |
| 316 | + url := strings.TrimSpace(urls[albumIndex]) |
| 317 | + status := "" |
| 318 | + color := "" |
| 319 | + icon := "" |
| 320 | + if url == "" { |
| 321 | + status = "Skipped" |
| 322 | + color = "text-gray-400" |
| 323 | + icon = "-" |
| 324 | + } else if !strings.Contains(url, "bandcamp.com") { |
| 325 | + status = "Invalid URL" |
| 326 | + color = "text-red-400" |
| 327 | + icon = "✗" |
| 328 | + } else { |
| 329 | + exists, err := loader.AlbumUrlExists(url) |
| 330 | + if err != nil { |
| 331 | + status = "Error" |
| 332 | + color = "text-red-400" |
| 333 | + icon = "✗" |
| 334 | + } else if exists { |
| 335 | + status = "Already Imported" |
| 336 | + color = "text-yellow-400" |
| 337 | + icon = "!" |
| 338 | + } else { |
| 339 | + albumData, err := fetch.FetchFromBandcamp(url) |
| 340 | + if err != nil { |
| 341 | + status = "Fetch Error" |
| 342 | + color = "text-red-400" |
| 343 | + icon = "✗" |
| 344 | + } else if err := loader.SaveAlbum(albumData); err != nil { |
| 345 | + status = "Save Error" |
| 346 | + color = "text-red-400" |
| 347 | + icon = "✗" |
| 348 | + } else { |
| 349 | + status = "Imported" |
| 350 | + color = "text-green-400" |
| 351 | + icon = "✓" |
| 352 | + } |
| 353 | + } |
| 354 | + } |
| 355 | + row := `<div id="album-status-` + fmt.Sprint(albumIndex) + `" class="flex items-center gap-2 py-2 border-b border-gray-700" hx-swap-oob="true">` |
| 356 | + row += `<span class="w-8 h-8 flex items-center justify-center bg-gray-700 rounded-full ` + color + `">` + icon + `</span>` |
| 357 | + row += `<span class="flex-1 text-gray-200">` + template.HTMLEscapeString(url) + `</span>` |
| 358 | + row += `<span class="` + color + `">` + status + `</span>` |
| 359 | + row += `</div>` |
| 360 | + progress := int(float64(albumIndex+1) / float64(total) * 100) |
| 361 | + progressBar := `<div id="import-progress-bar" class="bg-blue-600 h-4 rounded-full" style="width:` + fmt.Sprint(progress) + `%;" hx-swap-oob="true"></div>` |
| 362 | + triggerNext := "" |
| 363 | + if albumIndex+1 < total { |
| 364 | + triggerNext = `<div hx-post=\"/admin/import/process\" hx-target=\"#album-status-` + fmt.Sprint(albumIndex+1) + `\" hx-swap=\"outerHTML\" hx-vals='{"albumIndex":` + fmt.Sprint(albumIndex+1) + `,"total":` + fmt.Sprint(total) + `,"bandcampUrls":"` + template.JSEscapeString(strings.Join(urls, "\\n")) + `"}' hx-trigger=\"load\"></div>` |
| 365 | + } |
| 366 | + return c.HTML(200, row+progressBar+triggerNext) |
| 367 | +} |
| 368 | + |
249 | 369 | func validateAuth(c echo.Context) error { |
250 | 370 | cookie, err := c.Cookie("session") |
251 | 371 | if err != nil { |
|
0 commit comments