ref: 6953f303b3c111e7cc180f421fd00b99dd781114
dir: /filehandler.go/
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
mastodon "codeberg.org/penny64/hellclient-go-mastodon"
"github.com/google/shlex"
)
func pickFilename(command string) (string, error) {
cmdargv, err := shlex.Split(command)
if err != nil {
return "", err
}
cmd := exec.Command(cmdargv[0], cmdargv[1:]...)
filepath, err := cmd.Output()
filepathstring := strings.TrimSpace(string(filepath))
filepathstring = path.Clean(filepathstring)
return string(filepathstring), err
}
func openItemInOS(command string, url string) {
cmdstring := fmt.Sprintf(command, url)
cmdargv, err := shlex.Split(cmdstring)
if err != nil {
fmt.Printf("Failed to parse browser command: %s\n", cmdstring)
return
}
cmd := exec.Command(cmdargv[0], cmdargv[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stdout
cmd.Run()
}
func (hc *Hellclient) previewPostImages(target *mastodon.Status, commandstring string) (err error) {
//Let go of the lock while we wait for return
hc.unlock()
defer hc.lock()
_, pathnames, err := downloadPostImages(target)
if len(pathnames) == 0 {
//We tolerate failing to download some items but if we have none then error out
return
}
for _, path := range pathnames {
go openItemInOS(commandstring, path)
}
return
}
func savePostImages(target *mastodon.Status, savedir string) error {
files, _, err := downloadPostImages(target)
if err != nil {
fmt.Printf("Failed to download post images:%v\n", err)
return err
}
if len(files) == 0 {
return nil
}
for _, file := range files {
defer file.Close()
destPath := filepath.Join(savedir, path.Base(file.Name()))
download, err := os.Create(destPath)
if err != nil {
fmt.Printf("Failed to create destination file %s: %s\n", destPath, err)
continue
}
_, err = io.Copy(download, file)
download.Close()
if err != nil {
fmt.Printf("Failed to save %s: %s\n", destPath, err)
continue
}
fmt.Printf("Downloaded: %v\n", destPath)
}
return nil
}
func downloadPostImages(target *mastodon.Status) ([]*os.File, []string, error) {
if len(target.MediaAttachments) == 0 {
fmt.Println("Status has no attachments.")
return nil, nil, nil
}
var files []*os.File
var pathnames []string
for _, media := range target.MediaAttachments {
mediafile, err := downloadImage(media.URL)
if err != nil {
fmt.Printf("Media download error: %v\n", err)
continue
}
files = append(files, mediafile)
pathnames = append(pathnames, mediafile.Name())
}
if len(files) == 0 && len(target.MediaAttachments) > 0 {
return nil, nil, fmt.Errorf("failed to download any attachments")
}
return files, pathnames, nil
}
func downloadImage(target string) (*os.File, error) {
response, err := http.Get(target)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("bad status: %s", response.Status)
}
parsedurl, err := url.Parse(target)
if err != nil {
return nil, err
}
filename := path.Base(parsedurl.Path)
file, err := os.CreateTemp("", "*"+filename)
if err != nil {
return nil, err
}
_, err = io.Copy(file, response.Body)
if err != nil {
file.Close()
os.Remove(file.Name())
return nil, err
}
return file, nil
}
type StatusAttachmentHolder struct {
*Hellclient
attachments []*mastodon.Attachment
}
// Return a *os.File and error given a filepath
// Expands ~ and environmental variables
func loadFile(path string) (*os.File, error) {
path = expandDir(path)
file, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("error opening %s: %s", path, err)
}
return file, nil
}
// Upload media by path to the mastodon server
func (sam *StatusAttachmentHolder) uploadAttachment(path string) error {
file, err := loadFile(path)
if err != nil {
return err
}
defer file.Close()
config := sam.rl.GetConfig()
config.Prompt = "Image description: "
description, _ := sam.rl.ReadLineWithConfig(config)
media := &mastodon.Media{File: file, Description: description}
attachment, err := sam.client.UploadMediaFromMedia(context.Background(), media)
sam.attachments = append(sam.attachments, attachment)
if err != nil {
return err
}
return nil
}
func (sam *StatusAttachmentHolder) clearAttachments() {
sam.attachments = nil
}
func (sam *StatusAttachmentHolder) consumeAttachments() []*mastodon.Attachment {
defer sam.clearAttachments()
return sam.attachments
}
func (sam *StatusAttachmentHolder) enqueuedAttachments() int {
return len(sam.attachments)
}