1
0
mirror of https://github.com/makew0rld/amfora.git synced 2024-06-15 19:15:24 +00:00

🚧 Downloading pages works - #38

This commit is contained in:
makeworld 2020-07-09 19:28:39 -04:00
parent f00fbce7f9
commit 39fa7c6a8b
6 changed files with 163 additions and 6 deletions

View File

@ -18,22 +18,26 @@ func main() {
if len(os.Args) > 1 {
if os.Args[1] == "--version" || os.Args[1] == "-v" {
fmt.Print(version + "\r\n")
fmt.Println(version)
return
}
if os.Args[1] == "--help" || os.Args[1] == "-h" {
fmt.Print("Amfora is a fancy terminal browser for the Gemini protocol.\r\n\r\n")
fmt.Print("Usage:\r\namfora [URL]\r\namfora --version, -v\r\n")
fmt.Println("Amfora is a fancy terminal browser for the Gemini protocol.")
fmt.Println()
fmt.Println("Usage:")
fmt.Println("amfora [URL]")
fmt.Println("amfora --version, -v")
return
}
}
err := config.Init()
if err != nil {
panic(err)
fmt.Printf("Config error: %v\n", err)
os.Exit(1)
}
display.Init()
display.Init()
display.NewTab()
display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping
display.CloseTab()

View File

@ -1,6 +1,7 @@
package config
import (
"fmt"
"os"
"path/filepath"
"runtime"
@ -24,10 +25,13 @@ var BkmkStore = viper.New()
var bkmkDir string
var bkmkPath string
// For other pkgs to use
var DownloadsDir string
func Init() error {
home, err := homedir.Dir()
if err != nil {
panic(err)
return err
}
// Store AppData path
if runtime.GOOS == "windows" {
@ -144,6 +148,7 @@ func Init() error {
viper.SetDefault("a-general.bullets", true)
viper.SetDefault("a-general.left_margin", 0.15)
viper.SetDefault("a-general.max_width", 100)
viper.SetDefault("a-general.downloads", "")
viper.SetDefault("cache.max_size", 0)
viper.SetDefault("cache.max_pages", 20)
@ -154,6 +159,37 @@ func Init() error {
return err
}
// Setup downloads dir
if viper.GetString("a-general.downloads") == "" {
// Find default Downloads dir
// This seems to work for all OSes?
DownloadsDir = filepath.Join(home, "Downloads")
// Create it just in case
err = os.MkdirAll(DownloadsDir, 0755)
if err != nil {
return fmt.Errorf("downloads path could not be created: %s", DownloadsDir)
}
} else {
// Validate path
dDir := viper.GetString("a-general.downloads")
di, err := os.Stat(dDir)
if err == nil {
if !di.IsDir() {
return fmt.Errorf("downloads path specified is not a directory: %s", dDir)
}
} else if os.IsNotExist(err) {
// Try to create path
err = os.MkdirAll(dDir, 0755)
if err != nil {
return fmt.Errorf("downloads path could not be created: %s", dDir)
}
} else {
// Some other error
return fmt.Errorf("couldn't access downloads directory: %s", dDir)
}
DownloadsDir = dDir
}
// Setup cache from config
cache.SetMaxSize(viper.GetInt("cache.max_size"))
cache.SetMaxPages(viper.GetInt("cache.max_pages"))

View File

@ -27,6 +27,7 @@ left_margin = 0.15
max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped.
# 'downloads' is the path to a downloads folder.
# An empty value means the code will find the default downloads folder for your system.
# If the path does not exist it will be created.
downloads = ""
# Options for page cache - which is only for text/gemini pages
# Increase the cache size to speed up browsing at the expense of memory

View File

@ -24,6 +24,7 @@ left_margin = 0.15
max_width = 100 # The max number of columns to wrap a page's text to. Preformatted blocks are not wrapped.
# 'downloads' is the path to a downloads folder.
# An empty value means the code will find the default downloads folder for your system.
# If the path does not exist it will be created.
downloads = ""
# Options for page cache - which is only for text/gemini pages
# Increase the cache size to speed up browsing at the expense of memory

View File

@ -274,6 +274,18 @@ func Init() {
case tcell.KeyPgDn:
tabs[curTab].pageDown()
return nil
case tcell.KeyCtrlS:
if tabs[curTab].hasContent() {
savePath, err := downloadPage(tabs[curTab].page)
if err != nil {
Error("Download Error", fmt.Sprintf("Error saving page content: %v", err))
} else {
Info(fmt.Sprintf("Page content saved to %s. ", savePath))
}
} else {
Info("The current page has no content, so it couldn't be downloaded.")
}
return nil
case tcell.KeyRune:
// Regular key was sent
switch string(event.Rune()) {

103
display/download.go Normal file
View File

@ -0,0 +1,103 @@
package display
import (
"io/ioutil"
"net/url"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/makeworld-the-better-one/amfora/config"
"github.com/makeworld-the-better-one/amfora/structs"
)
// getSafeDownloadName is used by downloads.go only.
// It returns a modified name that is unique for the downloads folder.
// This way duplicate saved files will not overwrite each other.
//
// lastDot should be set to true if the number added to the name should come before
// the last dot in the filename instead of the first.
//
// n should be set to 0, it is used for recursiveness.
func getSafeDownloadName(name string, lastDot bool, n int) (string, error) {
// newName("test.txt", 3) -> "test(3).txt"
newName := func() string {
if n <= 0 {
return name
}
if lastDot {
ext := filepath.Ext(name)
return strings.TrimSuffix(name, ext) + "(" + strconv.Itoa(n) + ")" + ext
} else {
idx := strings.Index(name, ".")
if idx == -1 {
return name + "(" + strconv.Itoa(n) + ")"
}
return name[:idx] + "(" + strconv.Itoa(n) + ")" + name[idx:]
}
}
d, err := os.Open(config.DownloadsDir)
if err != nil {
return "", err
}
files, err := d.Readdirnames(-1)
if err != nil {
d.Close()
return "", err
}
nn := newName()
for i := range files {
if nn == files[i] {
d.Close()
return getSafeDownloadName(name, lastDot, n+1)
}
}
d.Close()
return nn, nil // Name doesn't exist already
}
// downloadPage saves the passed Page to a file.
// It returns the saved path and an error.
// It always cleans up, so if an error is returned there is no file saved
func downloadPage(p *structs.Page) (string, error) {
// Figure out file name
var name string
var err error
parsed, _ := url.Parse(p.Url)
if parsed.Path == "" || path.Base(parsed.Path) == "/" {
// No file, just the root domain
if p.Mediatype == structs.TextGemini {
name, err = getSafeDownloadName(parsed.Hostname()+".gmi", true, 0)
if err != nil {
return "", err
}
} else {
name, err = getSafeDownloadName(parsed.Hostname()+".txt", true, 0)
if err != nil {
return "", err
}
}
} else {
// There's a specific file
name = path.Base(parsed.Path)
if p.Mediatype == structs.TextGemini && !strings.HasSuffix(name, ".gmi") && !strings.HasSuffix(name, ".gemini") {
name += ".gmi"
}
name, err = getSafeDownloadName(name, false, 0)
if err != nil {
return "", err
}
}
savePath := filepath.Join(config.DownloadsDir, name)
err = ioutil.WriteFile(savePath, []byte(p.Raw), 0644)
if err != nil {
// Just in case
os.Remove(savePath)
return "", err
}
return savePath, err
}