shithub: hell

ref: 5dccb5b06dda55efa1ceab2fb260eb895fd8e809
dir: /filehandler.go/

View raw version
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)
}