From 284db56c063e3c120eaa6f776df25bb7bb489ae6 Mon Sep 17 00:00:00 2001 From: christiangoeschel Date: Tue, 8 Oct 2024 15:20:55 -0400 Subject: [PATCH] Fixes bensadeh/circumflex#140 --- bubble/list/list.go | 33 +++++++++++++++++ bubble/list/message/message.go | 4 +++ download/download.go | 65 ++++++++++++++++++++++++++++++++++ info/info.go | 3 ++ 4 files changed, 105 insertions(+) create mode 100644 download/download.go diff --git a/bubble/list/list.go b/bubble/list/list.go index 16296228..c4ad90ef 100644 --- a/bubble/list/list.go +++ b/bubble/list/list.go @@ -20,6 +20,7 @@ import ( "clx/bubble/ranking" "clx/cli" "clx/constants/category" + "clx/download" "clx/favorites" "clx/header" "clx/help" @@ -615,6 +616,21 @@ func (m *Model) Update(msg tea.Msg) (*Model, tea.Cmd) { m.NewStatusMessage(msg.Message) m.updatePagination() + + case message.DownloadFile: + statusMessage := "Document downloaded successfully" + m.hideStatusMessage() + m.disableInput = false + + filename, _ := download.GuessDownloadFileName(msg.Url) + downloadDir, _ := download.GetDownloadDir() + absoluteFilepath := fmt.Sprintf("%s/%s", downloadDir, filename) + err := download.DownloadFile(msg.Url, absoluteFilepath) + if err != nil { + statusMessage = "Could not download document" + } + + return m, m.NewStatusMessageWithDuration(statusMessage, time.Second*3) } if m.isOnHelpScreen { @@ -949,6 +965,23 @@ func (m *Model) handleBrowsing(msg tea.Msg) tea.Cmd { CommentCount: m.SelectedItem().CommentsCount, } } + + case msg.String() == "d": + if !strings.Contains(m.SelectedItem().Title, "[pdf]") { + return m.NewStatusMessage("Only PDFs are currently supported ...") + } + m.SetDisabledInput(true) + + downloadCmd := func() tea.Msg { + return message.DownloadFile{ + Url: m.SelectedItem().URL, + } + } + + cmds = append(cmds, downloadCmd) + cmds = append(cmds, m.NewStatusMessage("Downloading ...")) + + return tea.Batch(cmds...) } } diff --git a/bubble/list/message/message.go b/bubble/list/message/message.go index 8283ecae..6041d688 100644 --- a/bubble/list/message/message.go +++ b/bubble/list/message/message.go @@ -63,3 +63,7 @@ type CategoryFetchingFinished struct { type AddToFavorites struct { Item *item.Item } + +type DownloadFile struct { + Url string +} diff --git a/download/download.go b/download/download.go new file mode 100644 index 00000000..7d718c9f --- /dev/null +++ b/download/download.go @@ -0,0 +1,65 @@ +package download + +import ( + "fmt" + "io" + "net/http" + "os" + "path" + "time" +) + +const DownloadDir string = "circumflex/downloads" + +// Guess the PDF filename by extracting the last part of the download url +func GuessDownloadFileName(url string) (string, error) { + absoluteFilepath := path.Base(url) + + if absoluteFilepath == "." || absoluteFilepath == "/" { + return "", fmt.Errorf("Could not guess filename, potential invalid URL!") + } + + return absoluteFilepath, nil +} + +func GetDownloadDir() (string, error) { + dir, err := os.UserHomeDir() + if err != nil { + // If getting the current working directory fails as well, + // the user might have greater issues than simply downloading a file + dir, err = os.Getwd() + if err != nil { + return "", err + } + } else { + dir = fmt.Sprintf("%s/%s", dir, DownloadDir) + } + + return dir, nil +} + +// Writes to the destination file as it downloads it, without +// loading the entire file into memory. +func DownloadFile(url string, absoluteFilepath string) error { + out, err := os.Create(absoluteFilepath) + if err != nil { + return fmt.Errorf("Could not create file") + } + defer out.Close() + + // Timeout download attempt after 10 seconds + client := http.Client{Timeout: 10 * time.Second} + + resp, err := client.Get(url) + if err != nil { + return fmt.Errorf("Could not download file") + } + defer resp.Body.Close() + + _, err = io.Copy(out, resp.Body) + if err != nil { + return fmt.Errorf("Could not write download file data to destination") + } + + return nil +} diff --git a/info/info.go b/info/info.go index af65fb64..59162af1 100644 --- a/info/info.go +++ b/info/info.go @@ -4,10 +4,12 @@ import ( "strings" "clx/constants/nerdfonts" + . "github.com/logrusorgru/aurora/v3" "clx/constants/margins" "clx/keymaps" + text "github.com/MichaelMure/go-term-text" ) @@ -26,6 +28,7 @@ func GetText(screenWidth int, enableNerdFonts bool) string { keys.AddKeymap("Open story link in browser", "o") keys.AddKeymap("Open comments in browser", "c") keys.AddSeparator() + keys.AddKeymap("Download article's document (only PDFs)", "d") keys.AddKeymap("Add to favorites", "f") keys.AddKeymap("Remove from favorites", "x") keys.AddSeparator()