more sse, disable sse, disable auto-refresh if compelted

This commit is contained in:
Carl Pearson
2024-10-21 06:04:22 -06:00
parent 6cea35c699
commit 34fa95aa05
7 changed files with 169 additions and 25 deletions

View File

@@ -716,11 +716,20 @@ func videosHandler(c echo.Context) error {
var origs []originals.Original
db.Where("user_id = ?", userID).Order("id DESC").Find(&origs)
refresh := false
for _, orig := range origs {
if orig.Status != "completed" {
refresh = true
break
}
}
var playlists []playlists.Playlist
db.Where("user_id = ?", userID).Order("id DESC").Find(&playlists)
return c.Render(http.StatusOK, "videos.html",
map[string]interface{}{
"refresh": refresh,
"videos": origs,
"playlists": playlists,
"Footer": handlers.MakeFooter(),

View File

@@ -148,6 +148,7 @@ func main() {
e.POST("/transcode_to_video/:id", transcodeToVideoHandler, handlers.AuthMiddleware)
e.POST("/transcode_to_audio/:id", transcodeToAudioHandler, handlers.AuthMiddleware)
e.GET("/status", handlers.StatusGet, handlers.AuthMiddleware)
e.GET("/videos/events", handlers.VideosEvents, handlers.AuthMiddleware)
e.GET("/p/:id", playlistHandler, handlers.AuthMiddleware)
e.POST("/p/:id/delete", deletePlaylistHandler, handlers.AuthMiddleware)

View File

@@ -8,6 +8,9 @@ func Init(logger *logrus.Logger) error {
log = logger.WithFields(logrus.Fields{
"component": "originals",
}).Logger
listeners = make(map[uint][]*Queue)
return nil
}

View File

@@ -39,14 +39,14 @@ type Original struct {
var listeners map[uint][]*Queue // map of userId to queues
var lMu sync.Mutex
func bcast(userId, origId uint, status Status) {
func bcast(userId, origId uint, pl VideoEventPayload) {
lMu.Lock()
defer lMu.Unlock()
qs, ok := listeners[userId]
if ok {
for _, q := range qs {
q.Ch <- Event{origId, status}
q.Ch <- Event{origId, pl}
}
}
}
@@ -63,7 +63,7 @@ func SetStatus(id uint, status Status) error {
if err != nil {
return err
}
bcast(orig.UserID, id, status)
bcast(orig.UserID, id, makeVideosPayload(status, orig.Title))
return nil
}
@@ -87,9 +87,18 @@ func SetStatusTranscodingOrCompleted(id uint) error {
}
}
type VideoEventPayload struct {
Status Status
Title string
}
func makeVideosPayload(status Status, title string) VideoEventPayload {
return VideoEventPayload{status, title}
}
type Event struct {
VideoId uint
Status Status
VideoEventPayload
}
type Queue struct {

View File

@@ -0,0 +1,101 @@
// Function to disable the auto-refresh
function disableAutoRefresh() {
// Find the meta tag that controls the refresh
var metaRefresh = document.querySelector('meta[http-equiv="refresh"]');
// If the meta tag exists, remove it
if (metaRefresh) {
metaRefresh.remove();
console.log("Auto-refresh disabled");
} else {
console.log("No auto-refresh meta tag found");
}
}
const eventSource = new EventSource('/videos/events');
function closeEventSource() {
if (eventSource) {
eventSource.close();
}
}
function hideDivs(parent, hide, classes) {
classes.forEach(cls => {
divs = parent.querySelectorAll('div' + cls)
console.log(divs)
if (hide) {
divs.forEach(div => div.classList.add("hidden"))
} else {
divs.forEach(div => div.classList.remove("hidden"))
}
})
}
function showDivs(parent, show, classes) {
classes.forEach(cls => {
divs = parent.querySelectorAll(cls)
console.log(divs)
if (show) {
divs.forEach(div => div.classList.remove("hidden"))
} else {
divs.forEach(div => div.classList.add("hidden"))
}
})
}
function updateCardsStyling(card) {
console.log(`updateCardsStyling: card:`, card);
const statusDiv = card.querySelector('.video-status');
if (statusDiv) {
const statusText = statusDiv.textContent.trim().toLowerCase();
if (["completed", "download completed", "transcoding"].includes(statusText)) {
hideDivs(card, false, [".video-title-link"])
hideDivs(card, true, [".video-title-bare"])
} else { // failed
hideDivs(card, true, [".video-title-link"])
hideDivs(card, false, [".video-title-bare"])
}
showDivs(card, (statusText == "completed"), [".reprocess-btn", ".delete-btn"])
showDivs(card, (statusText == "failed"), [".restart-btn"])
}
}
eventSource.onmessage = function (event) {
const data = JSON.parse(event.data);
console.log(data)
const videoCard = document.getElementById(`video-card-${data.VideoId}`);
if (videoCard) {
const statusDiv = videoCard.querySelector('.video-info.video-status');
if (statusDiv) {
statusDiv.textContent = data.Status;
} else {
console.error(`Status div not found for video ID ${data.VideoId}`);
}
} else {
console.error(`Video card not found for ID ${data.VideoId}`);
}
updateCardsStyling(videoCard)
};
eventSource.onopen = function (event) {
console.log("Connection to server opened.");
disableAutoRefresh();
};
eventSource.onerror = function (error) {
console.error('EventSource failed:', error);
eventSource.close();
};
// Add event listener for when the page is about to unload
window.addEventListener('beforeunload', closeEventSource);
// Call the function when the page loads
// window.onload = disableAutoRefresh;

View File

@@ -37,4 +37,9 @@
.video-options .delete-btn {
background-color: rgb(255, 70, 70);
color: white;
}
.video-card .hidden {
display: none;
visibility: hidden;
}

View File

@@ -4,7 +4,9 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="10">
{{if .refresh}}
<meta http-equiv="refresh" content="15">
{{end}}
<title>Downloaded Videos</title>
<link rel="stylesheet" href="/static/style/common.css">
<link rel="stylesheet" href="/static/style/videos.css">
@@ -19,38 +21,49 @@
<div class="video-list">
{{range .videos}}
<div class="video-card">
<div class="video-title">
{{if or (eq .Status "download completed") (eq .Status "transcoding") (eq .Status "completed")}}
<div class="video-card" id="video-card-{{.ID}}">
{{$bareHidden := ""}}
{{$linkHidden := ""}}
{{if or (eq .Status "completed") (eq .Status "transcoding") (eq .Status "download completed")}}
{{$bareHidden = "hidden"}}
{{else}}
{{$linkHidden = "hidden"}}
{{end}}
<div class="video-title video-title-link {{$linkHidden}}">
<a href="/video/{{.ID}}">{{.Title}}</a>
{{else}}
</div>
<div class="video-title video-title-bare {{$bareHidden}}">
{{.Title}}
{{end}}
</div>
<div class="video-info">{{.Artist}}</div>
<div class="video-info"><a href="{{.URL}}">{{.URL}}</a></div>
<div class="video-info">{{.Status}}</div>
<div class="video-info video-status">{{.Status}}</div>
<div class="video-info">
{{if .Audio}}
Audio
{{end}}
{{if .Video}}
Video
{{end}}
{{if .Audio}} Audio {{end}}
{{if .Video}} Video {{end}}
</div>
<div class="video-options">
{{$processHidden := ""}}
{{$deleteHidden := ""}}
{{$restartHidden := ""}}
{{if eq .Status "completed"}}
<form action="/video/{{.ID}}/process" method="post" style="display:inline;">
<button type="submit">Reprocess</button>
</form>
{{$restartHidden = "hidden"}}
{{else if eq .Status "failed"}}
<form action="/video/{{.ID}}/restart" method="post" style="display:inline;">
<button type="submit">Restart</button>
</form>
{{else if eq .Status "downloading"}}
{{$processHidden = "hidden"}}
{{$deleteHidden = "hidden"}}
{{else}}
{{$processHidden = "hidden"}}
{{$deleteHidden = "hidden"}}
{{$restartHidden = "hidden"}}
{{end}}
<form action="/video/{{.ID}}/process" method="post" style="display:inline;">
<button type="submit" class="reprocess-btn {{$processHidden}}">Reprocess</button>
</form>
<form action="/video/{{.ID}}/delete" method="post" style="display:inline;">
<button type="submit" class="delete-btn">Delete</button>
<button type="submit" class="delete-btn {{$deleteHidden}}">Delete</button>
</form>
<form action="/video/{{.ID}}/restart" method="post" style="display:inline;">
<button type="submit" class="restart-btn {{$restartHidden}}">Restart</button>
</form>
</div>
</div>
@@ -77,7 +90,10 @@
</div>
{{end}}
</div>
{{template "footer" .}}
<!-- <script src="/static/script/videos-events.js" defer></script> -->
</body>
</html>