feat: view media attachment information #30

Manually merged
dananglin merged 1 commit from view-media-information-in-timeline into main 2024-06-18 20:01:14 +01:00
7 changed files with 160 additions and 41 deletions
Showing only changes of commit e8114f8d22 - Show all commits

20
internal/client/media.go Normal file
View file

@ -0,0 +1,20 @@
package client
import (
"fmt"
"net/http"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
)
func (g *Client) GetMediaAttachment(mediaAttachmentID string) (model.Attachment, error) {
url := g.Authentication.Instance + "/api/v1/media/" + mediaAttachmentID
var attachment model.Attachment
if err := g.sendRequest(http.MethodGet, url, nil, &attachment); err != nil {
return model.Attachment{}, fmt.Errorf("received an error after sending the request to get the media attachment: %w", err)
}
return attachment, nil
}

View file

@ -14,6 +14,7 @@ import (
const ( const (
flagAddPoll = "add-poll" flagAddPoll = "add-poll"
flagAccountName = "account-name" flagAccountName = "account-name"
flagAttachmentID = "attachment-id"
flagBrowser = "browser" flagBrowser = "browser"
flagChoose = "choose" flagChoose = "choose"
flagContentType = "content-type" flagContentType = "content-type"

View file

@ -5,23 +5,25 @@
package executor package executor
const ( const (
resourceAccount = "account" resourceAccount = "account"
resourceBlocked = "blocked" resourceBlocked = "blocked"
resourceBookmarks = "bookmarks" resourceBookmarks = "bookmarks"
resourceBoost = "boost" resourceBoost = "boost"
resourceFollowers = "followers" resourceFollowers = "followers"
resourceFollowing = "following" resourceFollowing = "following"
resourceFollowRequest = "follow-request" resourceFollowRequest = "follow-request"
resourceInstance = "instance" resourceInstance = "instance"
resourceLike = "like" resourceLike = "like"
resourceLiked = "liked" resourceLiked = "liked"
resourceList = "list" resourceList = "list"
resourceMutedAccounts = "muted-accounts" resourceMedia = "media"
resourceNote = "note" resourceMediaAttachment = "media-attachment"
resourcePoll = "poll" resourceMutedAccounts = "muted-accounts"
resourceStatus = "status" resourceNote = "note"
resourceStar = "star" resourcePoll = "poll"
resourceStarred = "starred" resourceStatus = "status"
resourceTimeline = "timeline" resourceStar = "star"
resourceVote = "vote" resourceStarred = "starred"
resourceTimeline = "timeline"
resourceVote = "vote"
) )

View file

@ -30,6 +30,7 @@ type ShowExecutor struct {
listID string listID string
tag string tag string
pollID string pollID string
attachmentID string
limit int limit int
} }
@ -52,6 +53,7 @@ func NewShowExecutor(printer *printer.Printer, configDir, name, summary string)
showExe.StringVar(&showExe.listID, flagListID, "", "Specify the ID of the list to display") showExe.StringVar(&showExe.listID, flagListID, "", "Specify the ID of the list to display")
showExe.StringVar(&showExe.tag, flagTag, "", "Specify the name of the tag to use") showExe.StringVar(&showExe.tag, flagTag, "", "Specify the name of the tag to use")
showExe.StringVar(&showExe.pollID, flagPollID, "", "Specify the ID of the poll to display") showExe.StringVar(&showExe.pollID, flagPollID, "", "Specify the ID of the poll to display")
showExe.StringVar(&showExe.attachmentID, flagAttachmentID, "", "Specify the ID of the media attachment to display")
showExe.IntVar(&showExe.limit, flagLimit, 20, "Specify the limit of items to display") showExe.IntVar(&showExe.limit, flagLimit, 20, "Specify the limit of items to display")
showExe.Usage = commandUsageFunc(name, summary, showExe.FlagSet) showExe.Usage = commandUsageFunc(name, summary, showExe.FlagSet)
@ -65,20 +67,22 @@ func (s *ShowExecutor) Execute() error {
} }
funcMap := map[string]func(*client.Client) error{ funcMap := map[string]func(*client.Client) error{
resourceInstance: s.showInstance, resourceInstance: s.showInstance,
resourceAccount: s.showAccount, resourceAccount: s.showAccount,
resourceStatus: s.showStatus, resourceStatus: s.showStatus,
resourceTimeline: s.showTimeline, resourceTimeline: s.showTimeline,
resourceList: s.showList, resourceList: s.showList,
resourceFollowers: s.showFollowers, resourceFollowers: s.showFollowers,
resourceFollowing: s.showFollowing, resourceFollowing: s.showFollowing,
resourceBlocked: s.showBlocked, resourceBlocked: s.showBlocked,
resourceBookmarks: s.showBookmarks, resourceBookmarks: s.showBookmarks,
resourceLiked: s.showLiked, resourceLiked: s.showLiked,
resourceStarred: s.showLiked, resourceStarred: s.showLiked,
resourceFollowRequest: s.showFollowRequests, resourceFollowRequest: s.showFollowRequests,
resourcePoll: s.showPoll, resourcePoll: s.showPoll,
resourceMutedAccounts: s.showMutedAccounts, resourceMutedAccounts: s.showMutedAccounts,
resourceMedia: s.showMediaAttachment,
resourceMediaAttachment: s.showMediaAttachment,
} }
doFunc, ok := funcMap[s.resourceType] doFunc, ok := funcMap[s.resourceType]
@ -134,8 +138,8 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
} }
var ( var (
relationship *model.AccountRelationship = nil relationship *model.AccountRelationship
preferences *model.Preferences = nil preferences *model.Preferences
) )
if !s.myAccount && !s.skipAccountRelationship { if !s.myAccount && !s.skipAccountRelationship {
@ -402,3 +406,18 @@ func (s *ShowExecutor) showMutedAccounts(gtsClient *client.Client) error {
return nil return nil
} }
func (s *ShowExecutor) showMediaAttachment(gtsClient *client.Client) error {
if s.attachmentID == "" {
return FlagNotSetError{flagText: flagAttachmentID}
}
attachment, err := gtsClient.GetMediaAttachment(s.attachmentID)
if err != nil {
return fmt.Errorf("unable to retrieve the media attachment: %w", err)
}
s.printer.PrintMediaAttachment(attachment)
return nil
}

View file

@ -0,0 +1,45 @@
package printer
import (
"strconv"
"strings"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
)
func (p Printer) PrintMediaAttachment(attachement model.Attachment) {
var builder strings.Builder
// The ID of the media attachment
builder.WriteString("\n" + p.headerFormat("MEDIA ATTACHMENT ID:"))
builder.WriteString("\n" + attachement.ID)
// The media attachment type
builder.WriteString("\n\n" + p.headerFormat("TYPE:"))
builder.WriteString("\n" + attachement.Type)
// The description that came with the media attachment (if any)
description := attachement.Description
if description == "" {
description = noMediaDescription
}
builder.WriteString("\n\n" + p.headerFormat("DESCRIPTION:"))
builder.WriteString("\n" + description)
// The original size of the media attachment
builder.WriteString("\n\n" + p.headerFormat("ORIGINAL SIZE:"))
builder.WriteString("\n" + attachement.Meta.Original.Size)
// The media attachment's focus
builder.WriteString("\n\n" + p.headerFormat("FOCUS:"))
builder.WriteString("\n" + p.fieldFormat("x:") + " " + strconv.FormatFloat(attachement.Meta.Focus.X, 'f', 1, 64))
builder.WriteString("\n" + p.fieldFormat("y:") + " " + strconv.FormatFloat(attachement.Meta.Focus.Y, 'f', 1, 64))
// The URL to the source of the media attachment
builder.WriteString("\n\n" + p.headerFormat("URL:"))
builder.WriteString("\n" + attachement.URL)
builder.WriteString("\n\n")
p.print(builder.String())
}

View file

@ -13,7 +13,8 @@ import (
) )
const ( const (
minTerminalWidth = 40 minTerminalWidth = 40
noMediaDescription = "This media attachment has no description."
) )
type theme struct { type theme struct {
@ -39,6 +40,7 @@ type Printer struct {
failureSymbol string failureSymbol string
dateFormat string dateFormat string
dateTimeFormat string dateTimeFormat string
imageIcon string
} }
func NewPrinter( func NewPrinter(
@ -62,6 +64,7 @@ func NewPrinter(
} }
return &Printer{ return &Printer{
theme: theme,
noColor: noColor, noColor: noColor,
maxTerminalWidth: maxTerminalWidth, maxTerminalWidth: maxTerminalWidth,
pager: pager, pager: pager,
@ -72,7 +75,7 @@ func NewPrinter(
failureSymbol: "\u2717", failureSymbol: "\u2717",
dateFormat: "02 Jan 2006", dateFormat: "02 Jan 2006",
dateTimeFormat: "02 Jan 2006, 15:04 (MST)", dateTimeFormat: "02 Jan 2006, 15:04 (MST)",
theme: theme, imageIcon: "\uf03e",
} }
} }

View file

@ -18,19 +18,37 @@ func (p Printer) PrintStatus(status model.Status) {
// The account information // The account information
builder.WriteString("\n" + p.fullDisplayNameFormat(status.Account.DisplayName, status.Account.Acct)) builder.WriteString("\n" + p.fullDisplayNameFormat(status.Account.DisplayName, status.Account.Acct))
// The ID of the status
builder.WriteString("\n\n" + p.headerFormat("STATUS ID:"))
builder.WriteString("\n" + status.ID)
// The content of the status. // The content of the status.
builder.WriteString("\n\n" + p.headerFormat("CONTENT:")) builder.WriteString("\n\n" + p.headerFormat("CONTENT:"))
builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", p.maxTerminalWidth)) builder.WriteString(utilities.WrapLines(utilities.ConvertHTMLToText(status.Content), "\n", p.maxTerminalWidth))
// Details of media attachments (if any).
if len(status.MediaAttachments) > 0 {
builder.WriteString("\n\n" + p.headerFormat("MEDIA ATTACHMENTS:"))
for ind, media := range status.MediaAttachments {
builder.WriteString("\n\n[" + strconv.Itoa(ind) + "] " + p.fieldFormat("ID:") + " " + media.ID)
builder.WriteString("\n " + p.fieldFormat("Type:") + " " + media.Type)
description := media.Description
if description == "" {
description = noMediaDescription
}
builder.WriteString("\n " + p.fieldFormat("Description:") + " " + description)
builder.WriteString("\n " + p.fieldFormat("Media URL:") + " " + media.URL)
}
}
// If a poll exists in a status, write the contents to the builder. // If a poll exists in a status, write the contents to the builder.
if status.Poll != nil { if status.Poll != nil {
builder.WriteString(p.pollOptions(*status.Poll)) builder.WriteString(p.pollOptions(*status.Poll))
} }
// The ID of the status
builder.WriteString("\n\n" + p.headerFormat("STATUS ID:"))
builder.WriteString("\n" + status.ID)
// Status creation time // Status creation time
builder.WriteString("\n\n" + p.headerFormat("CREATED AT:")) builder.WriteString("\n\n" + p.headerFormat("CREATED AT:"))
builder.WriteString("\n" + p.formatDateTime(status.CreatedAt)) builder.WriteString("\n" + p.formatDateTime(status.CreatedAt))
@ -79,6 +97,17 @@ func (p Printer) PrintStatusList(list model.StatusList) {
builder.WriteString(p.pollOptions(*status.Poll)) builder.WriteString(p.pollOptions(*status.Poll))
} }
for _, media := range status.MediaAttachments {
builder.WriteString("\n\n" + p.imageIcon + " Media (" + media.ID + ")" + "\n ")
description := media.Description
if description == "" {
description = noMediaDescription
}
builder.WriteString(utilities.WrapLines(description, "\n ", p.maxTerminalWidth-3))
}
builder.WriteString( builder.WriteString(
"\n\n" + "\n\n" +
p.fieldFormat("Status ID:") + " " + statusID + "\t" + p.fieldFormat("Status ID:") + " " + statusID + "\t" +