mirror of
https://github.com/makew0rld/amfora.git
synced 2024-09-27 23:05:55 -04: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 len(os.Args) > 1 {
|
||||||
if os.Args[1] == "--version" || os.Args[1] == "-v" {
|
if os.Args[1] == "--version" || os.Args[1] == "-v" {
|
||||||
fmt.Print(version + "\r\n")
|
fmt.Println(version)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if os.Args[1] == "--help" || os.Args[1] == "-h" {
|
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.Println("Amfora is a fancy terminal browser for the Gemini protocol.")
|
||||||
fmt.Print("Usage:\r\namfora [URL]\r\namfora --version, -v\r\n")
|
fmt.Println()
|
||||||
|
fmt.Println("Usage:")
|
||||||
|
fmt.Println("amfora [URL]")
|
||||||
|
fmt.Println("amfora --version, -v")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := config.Init()
|
err := config.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
fmt.Printf("Config error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
display.Init()
|
|
||||||
|
|
||||||
|
display.Init()
|
||||||
display.NewTab()
|
display.NewTab()
|
||||||
display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping
|
display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping
|
||||||
display.CloseTab()
|
display.CloseTab()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -24,10 +25,13 @@ var BkmkStore = viper.New()
|
|||||||
var bkmkDir string
|
var bkmkDir string
|
||||||
var bkmkPath string
|
var bkmkPath string
|
||||||
|
|
||||||
|
// For other pkgs to use
|
||||||
|
var DownloadsDir string
|
||||||
|
|
||||||
func Init() error {
|
func Init() error {
|
||||||
home, err := homedir.Dir()
|
home, err := homedir.Dir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
// Store AppData path
|
// Store AppData path
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
@ -144,6 +148,7 @@ func Init() error {
|
|||||||
viper.SetDefault("a-general.bullets", true)
|
viper.SetDefault("a-general.bullets", true)
|
||||||
viper.SetDefault("a-general.left_margin", 0.15)
|
viper.SetDefault("a-general.left_margin", 0.15)
|
||||||
viper.SetDefault("a-general.max_width", 100)
|
viper.SetDefault("a-general.max_width", 100)
|
||||||
|
viper.SetDefault("a-general.downloads", "")
|
||||||
viper.SetDefault("cache.max_size", 0)
|
viper.SetDefault("cache.max_size", 0)
|
||||||
viper.SetDefault("cache.max_pages", 20)
|
viper.SetDefault("cache.max_pages", 20)
|
||||||
|
|
||||||
@ -154,6 +159,37 @@ func Init() error {
|
|||||||
return err
|
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
|
// Setup cache from config
|
||||||
cache.SetMaxSize(viper.GetInt("cache.max_size"))
|
cache.SetMaxSize(viper.GetInt("cache.max_size"))
|
||||||
cache.SetMaxPages(viper.GetInt("cache.max_pages"))
|
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.
|
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.
|
# 'downloads' is the path to a downloads folder.
|
||||||
# An empty value means the code will find the default downloads folder for your system.
|
# 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 = ""
|
downloads = ""
|
||||||
# Options for page cache - which is only for text/gemini pages
|
# Options for page cache - which is only for text/gemini pages
|
||||||
# Increase the cache size to speed up browsing at the expense of memory
|
# 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.
|
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.
|
# 'downloads' is the path to a downloads folder.
|
||||||
# An empty value means the code will find the default downloads folder for your system.
|
# 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 = ""
|
downloads = ""
|
||||||
# Options for page cache - which is only for text/gemini pages
|
# Options for page cache - which is only for text/gemini pages
|
||||||
# Increase the cache size to speed up browsing at the expense of memory
|
# Increase the cache size to speed up browsing at the expense of memory
|
||||||
|
@ -274,6 +274,18 @@ func Init() {
|
|||||||
case tcell.KeyPgDn:
|
case tcell.KeyPgDn:
|
||||||
tabs[curTab].pageDown()
|
tabs[curTab].pageDown()
|
||||||
return nil
|
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:
|
case tcell.KeyRune:
|
||||||
// Regular key was sent
|
// Regular key was sent
|
||||||
switch string(event.Rune()) {
|
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