mirror of
https://github.com/makew0rld/amfora.git
synced 2025-01-03 14:56:27 -05:00
🚧 Downloading pages works - #38
This commit is contained in:
parent
f00fbce7f9
commit
39fa7c6a8b
14
amfora.go
14
amfora.go
@ -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()
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
103
display/download.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user