Skip to content

Commit

Permalink
Improvements to the web UI (#2)
Browse files Browse the repository at this point in the history
* Improvements to the web UI:
- Allow running archive from the UI
- Give feedback when archive is running
- Always display login status for Google and Zoom, there was a blank screen when both where active
* Add meetings details to web interface
* Upload file if we can't parse the start and end time
  • Loading branch information
alvarolobato authored Jan 5, 2020
1 parent 756738f commit 7289639
Showing 1 changed file with 155 additions and 35 deletions.
190 changes: 155 additions & 35 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ import (
"github.com/graphaelli/zat/zoom"
)

func NewMux(logger *log.Logger, googleClient *google.Client, zoomClient *zoom.Client) *http.ServeMux {
func NewMux(zat *Config, params runParams) *http.ServeMux {
logger := zat.logger
googleClient := zat.googleClient
zoomClient := zat.zoomClient

mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
Expand All @@ -32,19 +36,55 @@ func NewMux(logger *log.Logger, googleClient *google.Client, zoomClient *zoom.Cl
}

w.Header().Set("Content-Type", "text/html")
w.Write([]byte("<meta http-equiv=\"refresh\" content=\"10\"/>"))
w.Write([]byte("<head><style>table, table th,table tr, table td{border-collapse: collapse;border:1px solid #000000;padding:3px}</style></head><body>"))

w.Write([]byte("<br>Google: "))
if !googleClient.HasCreds() {
logger.Print("no google credentials")
w.Write([]byte("<br><a href=\"/google\">google</a>"))
w.Write([]byte("<a href=\"/google\">login</a>"))
// googleClient.OauthRedirect(w, r)
//return
} else {
w.Write([]byte("<span style=\"color:green\">OK</span>"))
}

w.Write([]byte("<br>Zoom: "))
if !zoomClient.HasCreds() {
logger.Print("no zoom credentials")
w.Write([]byte("<br><a href=\"/zoom\">zoom</a>"))
w.Write([]byte("<a href=\"/zoom\">login</a>"))
//zoomClient.OauthRedirect(w, r)
//return
} else {
w.Write([]byte("<span style=\"color:green\">OK</span>"))
}

if archIsRunning {
w.Write([]byte("<br/>Archiving...</a>"))
} else if googleClient.HasCreds() && zoomClient.HasCreds() {
w.Write([]byte("<br/><a href=\"/archive\">Archive Now</a>"))
} else {
w.Write([]byte("<br/>Login, to be able to archive"))
}

if len(archDetails) > 0 {
w.Write([]byte("<br/><br/><table><tr><th>Name</th><th>Date</th><th>Files</th><th>Status</th></th>"))
for i := 0; i < len(archDetails); i++ {
arch := archDetails[i]
w.Write([]byte(fmt.Sprintf("<tr><td><a href=\"%s\">%s</a></td><td>%s</td><td>%d</td><td><a href=\"%s\">%s</a></td></tr>",
arch.zoomUrl, arch.name, arch.date, arch.fileNumber, arch.googleDriveURL, arch.status)))
}
w.Write([]byte("</table>"))
}
w.Write([]byte("</body>"))
})

mux.HandleFunc("/archive", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/archive" {
http.NotFound(w, r)
return
}

doRun(zat, params)
http.Redirect(w, r, "/", http.StatusSeeOther)
})

mux.HandleFunc("/google", func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -75,6 +115,7 @@ func NewMux(logger *log.Logger, googleClient *google.Client, zoomClient *zoom.Cl
logger.Print(err)
}
})

mux.HandleFunc("/zoom", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/zoom" {
http.NotFound(w, r)
Expand Down Expand Up @@ -217,7 +258,15 @@ func mkdir(ctx context.Context, gdrive *drive.Service, parent *drive.File, folde
}
}

func (z *Config) Archive(meeting zoom.Meeting) error {
func (z *Config) Archive(meeting zoom.Meeting, params runParams) error {

var curArchMeeting = archivedMeeting{name: meeting.Topic,
fileNumber: 0,
status: "archiving",
date: meeting.StartTime.Format("2006-01-02 15:04"),
zoomUrl: meeting.ShareURL}
archDetails = append(archDetails, &curArchMeeting)

// check what is already uploaded for this meeting
gdrive, err := z.googleClient.Service(context.TODO())
if err != nil {
Expand All @@ -226,16 +275,19 @@ func (z *Config) Archive(meeting zoom.Meeting) error {
// parent folder of all meetings
parentFolderName := z.copies[meeting.ID]
if parentFolderName == "" {
curArchMeeting.status = "error"
return fmt.Errorf("no mapping found for meeting %d", meeting.ID)
}
parent, err := gdrive.Files.Get(parentFolderName).SupportsAllDrives(true).Do()
if err != nil {
curArchMeeting.status = "error"
return fmt.Errorf("while finding parent of %q: %w", parentFolderName, err)
}

// parent folder for this meeting
meetingFolder, err, created := mkdir(context.TODO(), gdrive, parent, meetingFolderName(meeting))
if err != nil {
curArchMeeting.status = "error"
return fmt.Errorf("while finding/creating meeting folder: %w", err)
}
if created {
Expand All @@ -246,6 +298,8 @@ func (z *Config) Archive(meeting zoom.Meeting) error {
meetingFolder.Name, meetingFolder.Id)
}

curArchMeeting.googleDriveURL = "https://drive.google.com/drive/folders/" + meetingFolder.Id

// list folder for this meeting
alreadyUploaded := make(map[string]struct{})
nextPageToken := ""
Expand All @@ -256,6 +310,7 @@ func (z *Config) Archive(meeting zoom.Meeting) error {
}
meetingFiles, err := call.Do()
if err != nil {
curArchMeeting.status = "error"
return fmt.Errorf("while listing meeting folder: %w", err)
}
for _, f := range meetingFiles.Files {
Expand All @@ -271,28 +326,56 @@ func (z *Config) Archive(meeting zoom.Meeting) error {
z.logger.Printf("archiving meeting %d to %s (https://drive.google.com/drive/folders/%s)",
meeting.ID, meetingFolder.Name, meetingFolder.Id)
for _, f := range meeting.RecordingFiles {

//check if recording file duration is shorter than minimum
start, err := time.Parse(time.RFC3339, f.RecordingStart)
if err != nil {
z.logger.Printf("couldn't parse file recording start %s - %s: %w", f.ID, f.RecordingStart, err)
}

end, err2 := time.Parse(time.RFC3339, f.RecordingEnd)
if err2 != nil {
z.logger.Printf("couldn't parse file recording end %s - %s: %w", f.ID, f.RecordingStart, err)
}

if err == nil && err2 == nil {
duration := int(end.Sub(start).Minutes())
if duration < params.minDuration {
curArchMeeting.status = "skipped - length"
z.logger.Printf("skipped %d minute recording at %s - %s", duration, start, end)
continue
}
}

name := recordingFileName(meeting, f)
if _, exists := alreadyUploaded[name]; exists {
curArchMeeting.status = "done"
curArchMeeting.fileNumber++
z.logger.Printf("skipping upload %s to %s/%s, already exists", name, parent.Name, meetingFolder.Name)
continue
}
z.logger.Printf("uploading %q to \"%s/%s\"", name, parent.Name, meetingFolder.Name)
r, err := http.Get(f.DownloadURL)
if err != nil {
curArchMeeting.status = "error"
return fmt.Errorf("while downloading recording %s: %w", f.DownloadURL, err)
}
if r.StatusCode != http.StatusOK {
curArchMeeting.status = "error"
return fmt.Errorf("download failed, got %d error: %#v", r.StatusCode, r)
}
_, err = gdrive.Files.Create(&drive.File{
Name: name,
Parents: []string{meetingFolder.Id},
}).Media(r.Body).SupportsAllDrives(true).Do()
if err != nil {
curArchMeeting.status = "error"
return fmt.Errorf("while uploading recording %s: %w", f.DownloadURL, err)
}
curArchMeeting.fileNumber++
z.logger.Printf("uploaded %q to %s/%s", name, parent.Name, meetingFolder.Name)
}
curArchMeeting.status = "done"
return nil
}

Expand All @@ -303,6 +386,7 @@ type runParams struct {

func (z *Config) Run(params runParams) error {
z.logger.Print("archiving recordings")
archDetails = []*archivedMeeting{}
nextPageToken := ""
for {
recordings, err := z.zoomClient.ListRecordings(time.Now().Add(-1*params.since), nextPageToken)
Expand All @@ -314,7 +398,7 @@ func (z *Config) Run(params runParams) error {
z.logger.Printf("skipped %d minute meeting at %s", meeting.Duration, meeting.StartTime)
continue
}
if err := z.Archive(meeting); err != nil {
if err := z.Archive(meeting, params); err != nil {
z.logger.Print(err)
}
}
Expand All @@ -327,6 +411,57 @@ func (z *Config) Run(params runParams) error {
return nil
}

type archivedMeeting struct {
name string
fileNumber int
status string
date string
zoomUrl string
googleDriveURL string
}

var (
archIsRunning bool
archIsRunningMu sync.Mutex
archDetails = []*archivedMeeting{}
)

func doRun(zat *Config, params runParams) {
zat.logger.Print("starting archive tool")
if !zat.googleClient.HasCreds() {
zat.logger.Println("no Google creds")
return
}
if !zat.zoomClient.HasCreds() {
zat.logger.Println("no Zoom creds")
return
}

archIsRunningMu.Lock()
start := !archIsRunning
archIsRunning = true
archIsRunningMu.Unlock()

if start {
var wg sync.WaitGroup
if zat != nil {
wg.Add(1)
go func() {
if err := zat.Run(params); err != nil {
zat.logger.Println(err)
}
wg.Done()

archIsRunningMu.Lock()
archIsRunning = false
archIsRunningMu.Unlock()
}()
}
} else {
zat.logger.Println("archiving skipped, it's already running")
}
}

func main() {
cfgDir := cmd.FlagConfigDir()
addr := flag.String("addr", "localhost:8080", "web server listener address")
Expand Down Expand Up @@ -354,12 +489,22 @@ func main() {
logger.Fatal(err)
}

rp := runParams{
minDuration: *minDuration,
since: *since,
}

zat, err := NewConfigFromFile(logger, cmd.ZatConfigPath, googleClient, zoomClient)
if err != nil {
logger.Println("failed to load config", err)
}

var wg sync.WaitGroup
if !*noServer {
wg.Add(1)
server := http.Server{
Addr: *addr,
Handler: NewMux(logger, googleClient, zoomClient),
Handler: NewMux(zat, rp),
}
go func() {
logger.Printf("starting on http://%s", server.Addr)
Expand All @@ -370,32 +515,7 @@ func main() {
}()
}

zat, err := NewConfigFromFile(logger, cmd.ZatConfigPath, googleClient, zoomClient)
if err != nil {
logger.Println("failed to load config", err)
}

if !googleClient.HasCreds() {
logger.Println("no google creds")
}
if !zoomClient.HasCreds() {
logger.Println("no zoom creds")
}

if zat != nil {
wg.Add(1)
go func() {
rp := runParams{
minDuration: *minDuration,
since: *since,
}
logger.Print("starting archive tool")
if err := zat.Run(rp); err != nil {
logger.Println(err)
}
wg.Done()
}()
}

doRun(zat, rp)

wg.Wait()
}

0 comments on commit 7289639

Please sign in to comment.