mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-02 13:52:24 -06:00
[chore] media and emoji refactoring (#3000)
* start updating media manager interface ready for storing attachments / emoji right away
* store emoji and media as uncached immediately, then (re-)cache on Processing{}.Load()
* remove now unused media workers
* fix tests and issues
* fix another test!
* fix emoji activitypub uri setting behaviour, fix remainder of test compilation issues
* fix more tests
* fix (most of) remaining tests, add debouncing to repeatedly failing media / emojis
* whoops, rebase issue
* remove kim's whacky experiments
* do some reshuffling, ensure emoji uri gets set
* ensure marked as not cached on cleanup
* tweaks to media / emoji processing to handle context canceled better
* ensure newly fetched emojis actually get set in returned slice
* use different varnames to be a bit more obvious
* move emoji refresh rate limiting to dereferencer
* add exported dereferencer functions for remote media, use these for recaching in processor
* add check for nil attachment in updateAttachment()
* remove unused emoji and media fields + columns
* see previous commit
* fix old migrations expecting image_updated_at to exists (from copies of old models)
* remove freshness checking code (seems to be broken...)
* fix error arg causing nil ptr exception
* finish documentating functions with comments, slight tweaks to media / emoji deref error logic
* remove some extra unneeded boolean checking
* finish writing documentation (code comments) for exported media manager methods
* undo changes to migration snapshot gtsmodels, updated failing migration to have its own snapshot
* move doesColumnExist() to util.go in migrations package
This commit is contained in:
parent
fa710057c8
commit
21bb324156
48 changed files with 2578 additions and 1926 deletions
|
|
@ -33,7 +33,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
|
|
@ -536,12 +535,12 @@ func (d *Dereferencer) enrichStatus(
|
|||
}
|
||||
|
||||
// Ensure the status' media attachments are populated, passing in existing to check for changes.
|
||||
if err := d.fetchStatusAttachments(ctx, tsport, status, latestStatus); err != nil {
|
||||
if err := d.fetchStatusAttachments(ctx, requestUser, status, latestStatus); err != nil {
|
||||
return nil, nil, gtserror.Newf("error populating attachments for status %s: %w", uri, err)
|
||||
}
|
||||
|
||||
// Ensure the status' emoji attachments are populated, (changes are expected / okay).
|
||||
if err := d.fetchStatusEmojis(ctx, requestUser, latestStatus); err != nil {
|
||||
// Ensure the status' emoji attachments are populated, passing in existing to check for changes.
|
||||
if err := d.fetchStatusEmojis(ctx, status, latestStatus); err != nil {
|
||||
return nil, nil, gtserror.Newf("error populating emojis for status %s: %w", uri, err)
|
||||
}
|
||||
|
||||
|
|
@ -643,79 +642,12 @@ func (d *Dereferencer) isPermittedStatus(
|
|||
return onFail()
|
||||
}
|
||||
|
||||
// populateMentionTarget tries to populate the given
|
||||
// mention with the correct TargetAccount and (if not
|
||||
// yet set) TargetAccountURI, returning the populated
|
||||
// mention.
|
||||
//
|
||||
// Will check on the existing status if the mention
|
||||
// is already there and populated; if so, existing
|
||||
// mention will be returned along with `true`.
|
||||
//
|
||||
// Otherwise, this function will try to parse first
|
||||
// the Href of the mention, and then the namestring,
|
||||
// to see who it targets, and go fetch that account.
|
||||
func (d *Dereferencer) populateMentionTarget(
|
||||
func (d *Dereferencer) fetchStatusMentions(
|
||||
ctx context.Context,
|
||||
mention *gtsmodel.Mention,
|
||||
requestUser string,
|
||||
existing, status *gtsmodel.Status,
|
||||
) (
|
||||
*gtsmodel.Mention,
|
||||
bool, // True if mention already exists in the DB.
|
||||
error,
|
||||
) {
|
||||
// Mentions can be created using Name or Href.
|
||||
// Prefer Href (TargetAccountURI), fall back to Name.
|
||||
if mention.TargetAccountURI != "" {
|
||||
// Look for existing mention with this URI.
|
||||
// If we already have it we can return early.
|
||||
existingMention, ok := existing.GetMentionByTargetURI(mention.TargetAccountURI)
|
||||
if ok && existingMention.ID != "" {
|
||||
return existingMention, true, nil
|
||||
}
|
||||
|
||||
// Ensure that mention account URI is parseable.
|
||||
accountURI, err := url.Parse(mention.TargetAccountURI)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("invalid account uri %q: %w", mention.TargetAccountURI, err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Ensure we have the account of the mention target dereferenced.
|
||||
mention.TargetAccount, _, err = d.getAccountByURI(ctx, requestUser, accountURI)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("failed to dereference account %s: %w", accountURI, err)
|
||||
return nil, false, err
|
||||
}
|
||||
} else {
|
||||
// Href wasn't set. Find the target account using namestring.
|
||||
username, domain, err := util.ExtractNamestringParts(mention.NameString)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("failed to parse namestring %s: %w", mention.NameString, err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
mention.TargetAccount, _, err = d.getAccountByUsernameDomain(ctx, requestUser, username, domain)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("failed to dereference account %s: %w", mention.NameString, err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Look for existing mention with this URI.
|
||||
mention.TargetAccountURI = mention.TargetAccount.URI
|
||||
existingMention, ok := existing.GetMentionByTargetURI(mention.TargetAccountURI)
|
||||
if ok && existingMention.ID != "" {
|
||||
return existingMention, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, mention.TargetAccountURI
|
||||
// and mention.TargetAccount must be set.
|
||||
return mention, false, nil
|
||||
}
|
||||
|
||||
func (d *Dereferencer) fetchStatusMentions(ctx context.Context, requestUser string, existing, status *gtsmodel.Status) error {
|
||||
existing *gtsmodel.Status,
|
||||
status *gtsmodel.Status,
|
||||
) error {
|
||||
// Allocate new slice to take the yet-to-be created mention IDs.
|
||||
status.MentionIDs = make([]string, len(status.Mentions))
|
||||
|
||||
|
|
@ -728,10 +660,10 @@ func (d *Dereferencer) fetchStatusMentions(ctx context.Context, requestUser stri
|
|||
|
||||
mention, alreadyExists, err = d.populateMentionTarget(
|
||||
ctx,
|
||||
mention,
|
||||
requestUser,
|
||||
existing,
|
||||
status,
|
||||
mention,
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "failed to derive mention: %v", err)
|
||||
|
|
@ -845,7 +777,11 @@ func (d *Dereferencer) threadStatus(ctx context.Context, status *gtsmodel.Status
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Dereferencer) fetchStatusTags(ctx context.Context, existing, status *gtsmodel.Status) error {
|
||||
func (d *Dereferencer) fetchStatusTags(
|
||||
ctx context.Context,
|
||||
existing *gtsmodel.Status,
|
||||
status *gtsmodel.Status,
|
||||
) error {
|
||||
// Allocate new slice to take the yet-to-be determined tag IDs.
|
||||
status.TagIDs = make([]string, len(status.Tags))
|
||||
|
||||
|
|
@ -900,7 +836,11 @@ func (d *Dereferencer) fetchStatusTags(ctx context.Context, existing, status *gt
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Dereferencer) fetchStatusPoll(ctx context.Context, existing, status *gtsmodel.Status) error {
|
||||
func (d *Dereferencer) fetchStatusPoll(
|
||||
ctx context.Context,
|
||||
existing *gtsmodel.Status,
|
||||
status *gtsmodel.Status,
|
||||
) error {
|
||||
var (
|
||||
// insertStatusPoll generates ID and inserts the poll attached to status into the database.
|
||||
insertStatusPoll = func(ctx context.Context, status *gtsmodel.Status) error {
|
||||
|
|
@ -990,19 +930,24 @@ func (d *Dereferencer) fetchStatusPoll(ctx context.Context, existing, status *gt
|
|||
}
|
||||
}
|
||||
|
||||
func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transport.Transport, existing, status *gtsmodel.Status) error {
|
||||
func (d *Dereferencer) fetchStatusAttachments(
|
||||
ctx context.Context,
|
||||
requestUser string,
|
||||
existing *gtsmodel.Status,
|
||||
status *gtsmodel.Status,
|
||||
) error {
|
||||
// Allocate new slice to take the yet-to-be fetched attachment IDs.
|
||||
status.AttachmentIDs = make([]string, len(status.Attachments))
|
||||
|
||||
for i := range status.Attachments {
|
||||
attachment := status.Attachments[i]
|
||||
placeholder := status.Attachments[i]
|
||||
|
||||
// Look for existing media attachment with remote URL first.
|
||||
existing, ok := existing.GetAttachmentByRemoteURL(attachment.RemoteURL)
|
||||
existing, ok := existing.GetAttachmentByRemoteURL(placeholder.RemoteURL)
|
||||
if ok && existing.ID != "" {
|
||||
|
||||
// Ensure the existing media attachment is up-to-date and cached.
|
||||
existing, err := d.updateAttachment(ctx, tsport, existing, attachment)
|
||||
existing, err := d.updateAttachment(ctx, requestUser, existing, placeholder)
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "error updating existing attachment: %v", err)
|
||||
|
||||
|
|
@ -1019,25 +964,25 @@ func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transp
|
|||
}
|
||||
|
||||
// Load this new media attachment.
|
||||
attachment, err := d.loadAttachment(
|
||||
attachment, err := d.GetMedia(
|
||||
ctx,
|
||||
tsport,
|
||||
requestUser,
|
||||
status.AccountID,
|
||||
attachment.RemoteURL,
|
||||
&media.AdditionalMediaInfo{
|
||||
placeholder.RemoteURL,
|
||||
media.AdditionalMediaInfo{
|
||||
StatusID: &status.ID,
|
||||
RemoteURL: &attachment.RemoteURL,
|
||||
Description: &attachment.Description,
|
||||
Blurhash: &attachment.Blurhash,
|
||||
RemoteURL: &placeholder.RemoteURL,
|
||||
Description: &placeholder.Description,
|
||||
Blurhash: &placeholder.Blurhash,
|
||||
},
|
||||
)
|
||||
if err != nil && attachment == nil {
|
||||
log.Errorf(ctx, "error loading attachment: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// A non-fatal error occurred during loading.
|
||||
if attachment == nil {
|
||||
log.Errorf(ctx, "error loading attachment %s: %v", placeholder.RemoteURL, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// non-fatal error occurred during loading, still use it.
|
||||
log.Warnf(ctx, "partially loaded attachment: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -1061,22 +1006,108 @@ func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transp
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *Dereferencer) fetchStatusEmojis(ctx context.Context, requestUser string, status *gtsmodel.Status) error {
|
||||
// Fetch the full-fleshed-out emoji objects for our status.
|
||||
emojis, err := d.populateEmojis(ctx, status.Emojis, requestUser)
|
||||
func (d *Dereferencer) fetchStatusEmojis(
|
||||
ctx context.Context,
|
||||
existing *gtsmodel.Status,
|
||||
status *gtsmodel.Status,
|
||||
) error {
|
||||
// Fetch the updated emojis for our status.
|
||||
emojis, changed, err := d.fetchEmojis(ctx,
|
||||
existing.Emojis,
|
||||
status.Emojis,
|
||||
)
|
||||
if err != nil {
|
||||
return gtserror.Newf("failed to populate emojis: %w", err)
|
||||
return gtserror.Newf("error fetching emojis: %w", err)
|
||||
}
|
||||
|
||||
// Iterate over and get their IDs.
|
||||
emojiIDs := make([]string, 0, len(emojis))
|
||||
for _, e := range emojis {
|
||||
emojiIDs = append(emojiIDs, e.ID)
|
||||
if !changed {
|
||||
// Use existing status emoji objects.
|
||||
status.EmojiIDs = existing.EmojiIDs
|
||||
status.Emojis = existing.Emojis
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set known emoji details.
|
||||
// Set latest emojis.
|
||||
status.Emojis = emojis
|
||||
status.EmojiIDs = emojiIDs
|
||||
|
||||
// Iterate over and set changed emoji IDs.
|
||||
status.EmojiIDs = make([]string, len(emojis))
|
||||
for i, emoji := range emojis {
|
||||
status.EmojiIDs[i] = emoji.ID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populateMentionTarget tries to populate the given
|
||||
// mention with the correct TargetAccount and (if not
|
||||
// yet set) TargetAccountURI, returning the populated
|
||||
// mention.
|
||||
//
|
||||
// Will check on the existing status if the mention
|
||||
// is already there and populated; if so, existing
|
||||
// mention will be returned along with `true`.
|
||||
//
|
||||
// Otherwise, this function will try to parse first
|
||||
// the Href of the mention, and then the namestring,
|
||||
// to see who it targets, and go fetch that account.
|
||||
func (d *Dereferencer) populateMentionTarget(
|
||||
ctx context.Context,
|
||||
requestUser string,
|
||||
existing *gtsmodel.Status,
|
||||
status *gtsmodel.Status,
|
||||
mention *gtsmodel.Mention,
|
||||
) (
|
||||
*gtsmodel.Mention,
|
||||
bool, // True if mention already exists in the DB.
|
||||
error,
|
||||
) {
|
||||
// Mentions can be created using Name or Href.
|
||||
// Prefer Href (TargetAccountURI), fall back to Name.
|
||||
if mention.TargetAccountURI != "" {
|
||||
// Look for existing mention with this URI.
|
||||
// If we already have it we can return early.
|
||||
existingMention, ok := existing.GetMentionByTargetURI(mention.TargetAccountURI)
|
||||
if ok && existingMention.ID != "" {
|
||||
return existingMention, true, nil
|
||||
}
|
||||
|
||||
// Ensure that mention account URI is parseable.
|
||||
accountURI, err := url.Parse(mention.TargetAccountURI)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("invalid account uri %q: %w", mention.TargetAccountURI, err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Ensure we have the account of the mention target dereferenced.
|
||||
mention.TargetAccount, _, err = d.getAccountByURI(ctx, requestUser, accountURI)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("failed to dereference account %s: %w", accountURI, err)
|
||||
return nil, false, err
|
||||
}
|
||||
} else {
|
||||
// Href wasn't set. Find the target account using namestring.
|
||||
username, domain, err := util.ExtractNamestringParts(mention.NameString)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("failed to parse namestring %s: %w", mention.NameString, err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
mention.TargetAccount, _, err = d.getAccountByUsernameDomain(ctx, requestUser, username, domain)
|
||||
if err != nil {
|
||||
err = gtserror.Newf("failed to dereference account %s: %w", mention.NameString, err)
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
// Look for existing mention with this URI.
|
||||
mention.TargetAccountURI = mention.TargetAccount.URI
|
||||
existingMention, ok := existing.GetMentionByTargetURI(mention.TargetAccountURI)
|
||||
if ok && existingMention.ID != "" {
|
||||
return existingMention, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, mention.TargetAccountURI
|
||||
// and mention.TargetAccount must be set.
|
||||
return mention, false, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue