summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/api/activitypub/users/outboxget_test.go6
-rw-r--r--internal/api/client/accounts/accountverify_test.go2
-rw-r--r--internal/api/client/admin/accountsgetv2_test.go4
-rw-r--r--internal/api/client/instance/instancepatch_test.go12
-rw-r--r--internal/api/client/search/searchget_test.go6
-rw-r--r--internal/api/client/statuses/statushistory_test.go4
-rw-r--r--internal/api/client/statuses/statusmute_test.go8
-rw-r--r--internal/api/model/attachment.go19
-rw-r--r--internal/api/model/status.go4
-rw-r--r--internal/db/bundb/account_test.go6
-rw-r--r--internal/db/bundb/basic_test.go2
-rw-r--r--internal/db/bundb/instance_test.go2
-rw-r--r--internal/db/bundb/status_test.go7
-rw-r--r--internal/db/bundb/timeline_test.go10
-rw-r--r--internal/media/ffmpeg.go50
-rw-r--r--internal/media/processingmedia.go8
-rw-r--r--internal/processing/account/rss_test.go4
-rw-r--r--internal/timeline/get_test.go6
-rw-r--r--internal/timeline/prune_test.go8
-rw-r--r--internal/typeutils/internaltofrontend.go32
-rw-r--r--internal/typeutils/internaltofrontend_test.go58
-rw-r--r--testrig/media/ghosts-original.mp3bin0 -> 7483917 bytes
-rw-r--r--testrig/media/ghosts-small.jpgbin0 -> 6132 bytes
-rw-r--r--testrig/testmodels.go80
-rw-r--r--web/source/css/status.css14
-rw-r--r--web/template/status_attachments.tmpl70
26 files changed, 327 insertions, 95 deletions
diff --git a/internal/api/activitypub/users/outboxget_test.go b/internal/api/activitypub/users/outboxget_test.go
index 521af0ff0..cba1ef31d 100644
--- a/internal/api/activitypub/users/outboxget_test.go
+++ b/internal/api/activitypub/users/outboxget_test.go
@@ -82,7 +82,7 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
"@context": "https://www.w3.org/ns/activitystreams",
"first": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40",
"id": "http://localhost:8080/users/the_mighty_zork/outbox",
- "totalItems": 7,
+ "totalItems": 8,
"type": "OrderedCollection"
}`, dst.String())
@@ -161,7 +161,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
],
"partOf": "http://localhost:8080/users/the_mighty_zork/outbox",
"prev": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40\u0026min_id=01HH9KYNQPA416TNJ53NSATP40",
- "totalItems": 7,
+ "totalItems": 8,
"type": "OrderedCollectionPage"
}`, dst.String())
@@ -224,7 +224,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
"id": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40&max_id=01F8MHAMCHF6Y650WCRSCP4WMY",
"orderedItems": [],
"partOf": "http://localhost:8080/users/the_mighty_zork/outbox",
- "totalItems": 7,
+ "totalItems": 8,
"type": "OrderedCollectionPage"
}`, dst.String())
diff --git a/internal/api/client/accounts/accountverify_test.go b/internal/api/client/accounts/accountverify_test.go
index eccda8b2e..cedc11916 100644
--- a/internal/api/client/accounts/accountverify_test.go
+++ b/internal/api/client/accounts/accountverify_test.go
@@ -79,7 +79,7 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
suite.Equal("http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/header/small/01PFPMWK2FF0D9WMHEJHR07C3Q.jpg", apimodelAccount.HeaderStatic)
suite.Equal(2, apimodelAccount.FollowersCount)
suite.Equal(2, apimodelAccount.FollowingCount)
- suite.Equal(7, apimodelAccount.StatusesCount)
+ suite.Equal(8, apimodelAccount.StatusesCount)
suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy)
suite.Equal(testAccount.Settings.Language, apimodelAccount.Source.Language)
suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note)
diff --git a/internal/api/client/admin/accountsgetv2_test.go b/internal/api/client/admin/accountsgetv2_test.go
index 85d58cce8..ca9573be5 100644
--- a/internal/api/client/admin/accountsgetv2_test.go
+++ b/internal/api/client/admin/accountsgetv2_test.go
@@ -240,8 +240,8 @@ func (suite *AccountsGetTestSuite) TestAccountsGetFromTop() {
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"enable_rss": true,
diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go
index 605b056b9..5113e4c57 100644
--- a/internal/api/client/instance/instancepatch_test.go
+++ b/internal/api/client/instance/instancepatch_test.go
@@ -135,7 +135,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {
},
"stats": {
"domain_count": 2,
- "status_count": 19,
+ "status_count": 20,
"user_count": 4
},
"thumbnail": "http://localhost:8080/assets/logo.png",
@@ -256,7 +256,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {
},
"stats": {
"domain_count": 2,
- "status_count": 19,
+ "status_count": 20,
"user_count": 4
},
"thumbnail": "http://localhost:8080/assets/logo.png",
@@ -377,7 +377,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {
},
"stats": {
"domain_count": 2,
- "status_count": 19,
+ "status_count": 20,
"user_count": 4
},
"thumbnail": "http://localhost:8080/assets/logo.png",
@@ -549,7 +549,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() {
},
"stats": {
"domain_count": 2,
- "status_count": 19,
+ "status_count": 20,
"user_count": 4
},
"thumbnail": "http://localhost:8080/assets/logo.png",
@@ -692,7 +692,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() {
},
"stats": {
"domain_count": 2,
- "status_count": 19,
+ "status_count": 20,
"user_count": 4
},
"thumbnail": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/original/`+instanceAccount.AvatarMediaAttachment.ID+`.gif",`+`
@@ -850,7 +850,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() {
},
"stats": {
"domain_count": 2,
- "status_count": 19,
+ "status_count": 20,
"user_count": 4
},
"thumbnail": "http://localhost:8080/assets/logo.png",
diff --git a/internal/api/client/search/searchget_test.go b/internal/api/client/search/searchget_test.go
index 27e5f782d..ab4f46689 100644
--- a/internal/api/client/search/searchget_test.go
+++ b/internal/api/client/search/searchget_test.go
@@ -916,7 +916,7 @@ func (suite *SearchGetTestSuite) TestSearchAAny() {
}
suite.Len(searchResult.Accounts, 5)
- suite.Len(searchResult.Statuses, 6)
+ suite.Len(searchResult.Statuses, 7)
suite.Len(searchResult.Hashtags, 0)
}
@@ -959,7 +959,7 @@ func (suite *SearchGetTestSuite) TestSearchAAnyFollowingOnly() {
}
suite.Len(searchResult.Accounts, 2)
- suite.Len(searchResult.Statuses, 6)
+ suite.Len(searchResult.Statuses, 7)
suite.Len(searchResult.Hashtags, 0)
}
@@ -1002,7 +1002,7 @@ func (suite *SearchGetTestSuite) TestSearchAStatuses() {
}
suite.Len(searchResult.Accounts, 0)
- suite.Len(searchResult.Statuses, 6)
+ suite.Len(searchResult.Statuses, 7)
suite.Len(searchResult.Hashtags, 0)
}
diff --git a/internal/api/client/statuses/statushistory_test.go b/internal/api/client/statuses/statushistory_test.go
index a88abdb8f..0d9f6c979 100644
--- a/internal/api/client/statuses/statushistory_test.go
+++ b/internal/api/client/statuses/statushistory_test.go
@@ -114,8 +114,8 @@ func (suite *StatusHistoryTestSuite) TestGetHistory() {
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"enable_rss": true,
diff --git a/internal/api/client/statuses/statusmute_test.go b/internal/api/client/statuses/statusmute_test.go
index 83effd0c2..01bea4e5c 100644
--- a/internal/api/client/statuses/statusmute_test.go
+++ b/internal/api/client/statuses/statusmute_test.go
@@ -132,8 +132,8 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"enable_rss": true,
@@ -197,8 +197,8 @@ func (suite *StatusMuteTestSuite) TestMuteUnmuteStatus() {
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"enable_rss": true,
diff --git a/internal/api/model/attachment.go b/internal/api/model/attachment.go
index 7e81759f2..d0b0c81e5 100644
--- a/internal/api/model/attachment.go
+++ b/internal/api/model/attachment.go
@@ -90,12 +90,23 @@ type Attachment struct {
// A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.
// See https://github.com/woltapp/blurhash
Blurhash *string `json:"blurhash"`
+}
+
+// WebAttachment is like Attachment, but with
+// additional fields not exposed via JSON;
+// used only internally for templating etc.
+//
+// swagger:ignore
+type WebAttachment struct {
+ *Attachment
- // Additional fields not exposed via JSON
- // (used only internally for templating etc).
+ // Parent status of this
+ // media is sensitive.
+ Sensitive bool
- // Parent status of this media is sensitive.
- Sensitive bool `json:"-"`
+ // MIME type of
+ // the attachment.
+ MIMEType string
}
// MediaMeta models media metadata.
diff --git a/internal/api/model/status.go b/internal/api/model/status.go
index a9c668565..e469835bd 100644
--- a/internal/api/model/status.go
+++ b/internal/api/model/status.go
@@ -111,6 +111,10 @@ type Status struct {
type WebStatus struct {
*Status
+ // Web version of media
+ // attached to this status.
+ MediaAttachments []*WebAttachment `json:"media_attachments"`
+
// Template-ready language tag and
// string, based on *status.Language.
LanguageTag *language.Language
diff --git a/internal/db/bundb/account_test.go b/internal/db/bundb/account_test.go
index a9554e0d7..116ea19f0 100644
--- a/internal/db/bundb/account_test.go
+++ b/internal/db/bundb/account_test.go
@@ -46,7 +46,7 @@ type AccountTestSuite struct {
func (suite *AccountTestSuite) TestGetAccountStatuses() {
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, false)
suite.NoError(err)
- suite.Len(statuses, 7)
+ suite.Len(statuses, 8)
}
func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() {
@@ -69,7 +69,7 @@ func (suite *AccountTestSuite) TestGetAccountStatusesPageDown() {
if err != nil {
suite.FailNow(err.Error())
}
- suite.Len(statuses, 1)
+ suite.Len(statuses, 2)
// try to get the last page (should be empty)
statuses, err = suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 3, false, false, statuses[len(statuses)-1].ID, "", false, false)
@@ -187,7 +187,7 @@ func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesExcludesSelfR
func (suite *AccountTestSuite) TestGetAccountStatusesMediaOnly() {
statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", true, false)
suite.NoError(err)
- suite.Len(statuses, 1)
+ suite.Len(statuses, 2)
}
func (suite *AccountTestSuite) TestGetAccountBy() {
diff --git a/internal/db/bundb/basic_test.go b/internal/db/bundb/basic_test.go
index 6892291d2..f6647c1f5 100644
--- a/internal/db/bundb/basic_test.go
+++ b/internal/db/bundb/basic_test.go
@@ -114,7 +114,7 @@ func (suite *BasicTestSuite) TestGetAllStatuses() {
s := []*gtsmodel.Status{}
err := suite.db.GetAll(context.Background(), &s)
suite.NoError(err)
- suite.Len(s, 23)
+ suite.Len(s, 24)
}
func (suite *BasicTestSuite) TestGetAllNotNull() {
diff --git a/internal/db/bundb/instance_test.go b/internal/db/bundb/instance_test.go
index 4b8ec9962..13fd7f61c 100644
--- a/internal/db/bundb/instance_test.go
+++ b/internal/db/bundb/instance_test.go
@@ -47,7 +47,7 @@ func (suite *InstanceTestSuite) TestCountInstanceUsersRemote() {
func (suite *InstanceTestSuite) TestCountInstanceStatuses() {
count, err := suite.db.CountInstanceStatuses(context.Background(), config.GetHost())
suite.NoError(err)
- suite.Equal(19, count)
+ suite.Equal(20, count)
}
func (suite *InstanceTestSuite) TestCountInstanceStatusesRemote() {
diff --git a/internal/db/bundb/status_test.go b/internal/db/bundb/status_test.go
index 0111dc6e7..b64177c32 100644
--- a/internal/db/bundb/status_test.go
+++ b/internal/db/bundb/status_test.go
@@ -169,12 +169,7 @@ func (suite *StatusTestSuite) TestGetStatusChildren() {
targetStatus := suite.testStatuses["local_account_1_status_1"]
children, err := suite.db.GetStatusChildren(context.Background(), targetStatus.ID)
suite.NoError(err)
- suite.Len(children, 2)
- for _, c := range children {
- suite.Equal(targetStatus.URI, c.InReplyToURI)
- suite.Equal(targetStatus.AccountID, c.InReplyToAccountID)
- suite.Equal(targetStatus.ID, c.InReplyToID)
- }
+ suite.Len(children, 3)
}
func (suite *StatusTestSuite) TestDeleteStatus() {
diff --git a/internal/db/bundb/timeline_test.go b/internal/db/bundb/timeline_test.go
index 0a014d321..b944bd9b4 100644
--- a/internal/db/bundb/timeline_test.go
+++ b/internal/db/bundb/timeline_test.go
@@ -155,7 +155,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimeline() {
suite.FailNow(err.Error())
}
- suite.checkStatuses(s, id.Highest, id.Lowest, 19)
+ suite.checkStatuses(s, id.Highest, id.Lowest, 20)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() {
@@ -187,7 +187,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineNoFollowing() {
suite.FailNow(err.Error())
}
- suite.checkStatuses(s, id.Highest, id.Lowest, 7)
+ suite.checkStatuses(s, id.Highest, id.Lowest, 8)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() {
@@ -209,7 +209,7 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineWithFutureStatus() {
}
suite.NotContains(s, futureStatus)
- suite.checkStatuses(s, id.Highest, id.Lowest, 19)
+ suite.checkStatuses(s, id.Highest, id.Lowest, 20)
}
func (suite *TimelineTestSuite) TestGetHomeTimelineBackToFront() {
@@ -240,8 +240,8 @@ func (suite *TimelineTestSuite) TestGetHomeTimelineFromHighest() {
}
suite.checkStatuses(s, id.Highest, id.Lowest, 5)
- suite.Equal("01HH9KYNQPA416TNJ53NSATP40", s[0].ID)
- suite.Equal("01G20ZM733MGN8J344T4ZDDFY1", s[len(s)-1].ID)
+ suite.Equal("01J2M1HPFSS54S60Y0KYV23KJE", s[0].ID)
+ suite.Equal("01G36SF3V6Y6V5BF9P4R7PQG7G", s[len(s)-1].ID)
}
func (suite *TimelineTestSuite) TestGetListTimelineNoParams() {
diff --git a/internal/media/ffmpeg.go b/internal/media/ffmpeg.go
index eb94849f0..b97c8413f 100644
--- a/internal/media/ffmpeg.go
+++ b/internal/media/ffmpeg.go
@@ -18,6 +18,7 @@
package media
import (
+ "cmp"
"context"
"encoding/json"
"errors"
@@ -198,6 +199,30 @@ func (res *ffprobeResult) ImageMeta() (width int, height int, err error) {
return
}
+// EmbeddedImageMeta extracts embedded image metadata contained within ffprobe'd media result
+// streams, should be used for pulling album image (can be animated image) from audio files.
+func (res *ffprobeResult) EmbeddedImageMeta() (width int, height int, framerate float32, err error) {
+ for _, stream := range res.Streams {
+ if stream.Width > width {
+ width = stream.Width
+ }
+ if stream.Height > height {
+ height = stream.Height
+ }
+ if fr := stream.GetFrameRate(); fr > 0 {
+ if framerate == 0 || fr < framerate {
+ framerate = fr
+ }
+ }
+ }
+ // Need width + height but
+ // no framerate is fine.
+ if width == 0 || height == 0 {
+ err = errors.New("invalid image stream(s)")
+ }
+ return
+}
+
// VideoMeta extracts video metadata contained within ffprobe'd media result streams.
func (res *ffprobeResult) VideoMeta() (width, height int, framerate float32, err error) {
for _, stream := range res.Streams {
@@ -222,6 +247,7 @@ func (res *ffprobeResult) VideoMeta() (width, height int, framerate float32, err
type ffprobeStream struct {
CodecName string `json:"codec_name"`
AvgFrameRate string `json:"avg_frame_rate"`
+ RFrameRate string `json:"r_frame_rate"`
Width int `json:"width"`
Height int `json:"height"`
// + unused fields.
@@ -229,7 +255,7 @@ type ffprobeStream struct {
// GetFrameRate calculates float32 framerate value from stream json string.
func (str *ffprobeStream) GetFrameRate() float32 {
- if str.AvgFrameRate != "" {
+ numDen := func(strFR string) (float32, float32) {
var (
// numerator
num float32
@@ -239,7 +265,7 @@ func (str *ffprobeStream) GetFrameRate() float32 {
)
// Check for a provided inequality, i.e. numerator / denominator.
- if p := strings.SplitN(str.AvgFrameRate, "/", 2); len(p) == 2 {
+ if p := strings.SplitN(strFR, "/", 2); len(p) == 2 {
n, _ := strconv.ParseFloat(p[0], 32)
d, _ := strconv.ParseFloat(p[1], 32)
num, den = float32(n), float32(d)
@@ -248,8 +274,26 @@ func (str *ffprobeStream) GetFrameRate() float32 {
num = float32(n)
}
- return num / den
+ return num, den
+ }
+
+ var num, den float32
+ if str.AvgFrameRate != "" {
+ // Check if we have avg_frame_rate.
+ num, den = numDen(str.AvgFrameRate)
+ }
+
+ if num == 0 && str.RFrameRate != "" {
+ // Check if we have r_frame_rate.
+ num, den = numDen(str.RFrameRate)
}
+
+ if num != 0 {
+ // Found it.
+ // Avoid divide by zero.
+ return num / cmp.Or(den, 1)
+ }
+
return 0
}
diff --git a/internal/media/processingmedia.go b/internal/media/processingmedia.go
index 43e153a4d..8ee242749 100644
--- a/internal/media/processingmedia.go
+++ b/internal/media/processingmedia.go
@@ -299,8 +299,14 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
// Extract image metadata from streams (if any),
// this will only exist for embedded album art.
- width, height, _ := result.ImageMeta()
+ width, height, framerate, _ := result.EmbeddedImageMeta()
if width > 0 && height > 0 {
+ // Unlikely to need these but masto API includes them.
+ p.media.FileMeta.Original.Width = width
+ p.media.FileMeta.Original.Height = height
+ if framerate != 0 {
+ p.media.FileMeta.Original.Framerate = &framerate
+ }
// Determine thumbnail dimensions to use.
thumbWidth, thumbHeight := thumbSize(width, height)
diff --git a/internal/processing/account/rss_test.go b/internal/processing/account/rss_test.go
index 226e994cc..40eeaa328 100644
--- a/internal/processing/account/rss_test.go
+++ b/internal/processing/account/rss_test.go
@@ -41,11 +41,11 @@ func (suite *GetRSSTestSuite) TestGetAccountRSSAdmin() {
func (suite *GetRSSTestSuite) TestGetAccountRSSZork() {
getFeed, lastModified, err := suite.accountProcessor.GetRSSFeedForUsername(context.Background(), "the_mighty_zork")
suite.NoError(err)
- suite.EqualValues(1702200240, lastModified.Unix())
+ suite.EqualValues(1704878640, lastModified.Unix())
feed, err := getFeed()
suite.NoError(err)
- suite.Equal("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n <channel>\n <title>Posts from @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n <description>Posts from @the_mighty_zork@localhost:8080</description>\n <pubDate>Sun, 10 Dec 2023 09:24:00 +0000</pubDate>\n <lastBuildDate>Sun, 10 Dec 2023 09:24:00 +0000</lastBuildDate>\n <image>\n <url>http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.jpg</url>\n <title>Avatar for @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n </image>\n <item>\n <title>HTML in post</title>\n <link>http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40</link>\n <description>@the_mighty_zork@localhost:8080 made a new post: &#34;Here&#39;s a bunch of HTML, read it and weep, weep then!&#xA;&#xA;```html&#xA;&lt;section class=&#34;about-user&#34;&gt;&#xA; &lt;div class=&#34;col-header&#34;&gt;&#xA; &lt;h2&gt;About&lt;/h2&gt;&#xA; &lt;/div&gt; &#xA; &lt;div class=&#34;fields&#34;&gt;&#xA; &lt;h3 class=&#34;sr-only&#34;&gt;Fields&lt;/h3&gt;&#xA; &lt;dl&gt;&#xA;...</description>\n <content:encoded><![CDATA[<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\">&lt;section class=&#34;about-user&#34;&gt;\n &lt;div class=&#34;col-header&#34;&gt;\n &lt;h2&gt;About&lt;/h2&gt;\n &lt;/div&gt; \n &lt;div class=&#34;fields&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Fields&lt;/h3&gt;\n &lt;dl&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;should you follow me?&lt;/dt&gt;\n &lt;dd&gt;maybe!&lt;/dd&gt;\n &lt;/div&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;age&lt;/dt&gt;\n &lt;dd&gt;120&lt;/dd&gt;\n &lt;/div&gt;\n &lt;/dl&gt;\n &lt;/div&gt;\n &lt;div class=&#34;bio&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Bio&lt;/h3&gt;\n &lt;p&gt;i post about things that concern me&lt;/p&gt;\n &lt;/div&gt;\n &lt;div class=&#34;sr-only&#34; role=&#34;group&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Stats&lt;/h3&gt;\n &lt;span&gt;Joined in Jun, 2022.&lt;/span&gt;\n &lt;span&gt;8 posts.&lt;/span&gt;\n &lt;span&gt;Followed by 1.&lt;/span&gt;\n &lt;span&gt;Following 1.&lt;/span&gt;\n &lt;/div&gt;\n &lt;div class=&#34;accountstats&#34; aria-hidden=&#34;true&#34;&gt;\n &lt;b&gt;Joined&lt;/b&gt;&lt;time datetime=&#34;2022-06-04T13:12:00.000Z&#34;&gt;Jun, 2022&lt;/time&gt;\n &lt;b&gt;Posts&lt;/b&gt;&lt;span&gt;8&lt;/span&gt;\n &lt;b&gt;Followed by&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;b&gt;Following&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;/div&gt;\n&lt;/section&gt;\n</code></pre><p>There, hope you liked that!</p>]]></content:encoded>\n <author>@the_mighty_zork@localhost:8080</author>\n <guid isPermaLink=\"true\">http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40</guid>\n <pubDate>Sun, 10 Dec 2023 09:24:00 +0000</pubDate>\n <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n </item>\n <item>\n <title>introduction post</title>\n <link>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</link>\n <description>@the_mighty_zork@localhost:8080 made a new post: &#34;hello everyone!&#34;</description>\n <content:encoded><![CDATA[hello everyone!]]></content:encoded>\n <author>@the_mighty_zork@localhost:8080</author>\n <guid isPermaLink=\"true\">http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</guid>\n <pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>\n <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n </item>\n </channel>\n</rss>", feed)
+ suite.Equal("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n <channel>\n <title>Posts from @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n <description>Posts from @the_mighty_zork@localhost:8080</description>\n <pubDate>Wed, 10 Jan 2024 09:24:00 +0000</pubDate>\n <lastBuildDate>Wed, 10 Jan 2024 09:24:00 +0000</lastBuildDate>\n <image>\n <url>http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.jpg</url>\n <title>Avatar for @the_mighty_zork@localhost:8080</title>\n <link>http://localhost:8080/@the_mighty_zork</link>\n </image>\n <item>\n <title>HTML in post</title>\n <link>http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40</link>\n <description>@the_mighty_zork@localhost:8080 made a new post: &#34;Here&#39;s a bunch of HTML, read it and weep, weep then!&#xA;&#xA;```html&#xA;&lt;section class=&#34;about-user&#34;&gt;&#xA; &lt;div class=&#34;col-header&#34;&gt;&#xA; &lt;h2&gt;About&lt;/h2&gt;&#xA; &lt;/div&gt; &#xA; &lt;div class=&#34;fields&#34;&gt;&#xA; &lt;h3 class=&#34;sr-only&#34;&gt;Fields&lt;/h3&gt;&#xA; &lt;dl&gt;&#xA;...</description>\n <content:encoded><![CDATA[<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\">&lt;section class=&#34;about-user&#34;&gt;\n &lt;div class=&#34;col-header&#34;&gt;\n &lt;h2&gt;About&lt;/h2&gt;\n &lt;/div&gt; \n &lt;div class=&#34;fields&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Fields&lt;/h3&gt;\n &lt;dl&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;should you follow me?&lt;/dt&gt;\n &lt;dd&gt;maybe!&lt;/dd&gt;\n &lt;/div&gt;\n &lt;div class=&#34;field&#34;&gt;\n &lt;dt&gt;age&lt;/dt&gt;\n &lt;dd&gt;120&lt;/dd&gt;\n &lt;/div&gt;\n &lt;/dl&gt;\n &lt;/div&gt;\n &lt;div class=&#34;bio&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Bio&lt;/h3&gt;\n &lt;p&gt;i post about things that concern me&lt;/p&gt;\n &lt;/div&gt;\n &lt;div class=&#34;sr-only&#34; role=&#34;group&#34;&gt;\n &lt;h3 class=&#34;sr-only&#34;&gt;Stats&lt;/h3&gt;\n &lt;span&gt;Joined in Jun, 2022.&lt;/span&gt;\n &lt;span&gt;8 posts.&lt;/span&gt;\n &lt;span&gt;Followed by 1.&lt;/span&gt;\n &lt;span&gt;Following 1.&lt;/span&gt;\n &lt;/div&gt;\n &lt;div class=&#34;accountstats&#34; aria-hidden=&#34;true&#34;&gt;\n &lt;b&gt;Joined&lt;/b&gt;&lt;time datetime=&#34;2022-06-04T13:12:00.000Z&#34;&gt;Jun, 2022&lt;/time&gt;\n &lt;b&gt;Posts&lt;/b&gt;&lt;span&gt;8&lt;/span&gt;\n &lt;b&gt;Followed by&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;b&gt;Following&lt;/b&gt;&lt;span&gt;1&lt;/span&gt;\n &lt;/div&gt;\n&lt;/section&gt;\n</code></pre><p>There, hope you liked that!</p>]]></content:encoded>\n <author>@the_mighty_zork@localhost:8080</author>\n <guid isPermaLink=\"true\">http://localhost:8080/@the_mighty_zork/statuses/01HH9KYNQPA416TNJ53NSATP40</guid>\n <pubDate>Sun, 10 Dec 2023 09:24:00 +0000</pubDate>\n <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n </item>\n <item>\n <title>introduction post</title>\n <link>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</link>\n <description>@the_mighty_zork@localhost:8080 made a new post: &#34;hello everyone!&#34;</description>\n <content:encoded><![CDATA[hello everyone!]]></content:encoded>\n <author>@the_mighty_zork@localhost:8080</author>\n <guid isPermaLink=\"true\">http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</guid>\n <pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>\n <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n </item>\n </channel>\n</rss>", feed)
}
func (suite *GetRSSTestSuite) TestGetAccountRSSZorkNoPosts() {
diff --git a/internal/timeline/get_test.go b/internal/timeline/get_test.go
index 499a9f35d..6b01ca812 100644
--- a/internal/timeline/get_test.go
+++ b/internal/timeline/get_test.go
@@ -228,7 +228,7 @@ func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossible() {
if err != nil {
suite.FailNow(err.Error())
}
- suite.checkStatuses(statuses, id.Highest, id.Lowest, 19)
+ suite.checkStatuses(statuses, id.Highest, id.Lowest, 20)
}
func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() {
@@ -255,7 +255,7 @@ func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() {
if err != nil {
suite.FailNow(err.Error())
}
- suite.checkStatuses(statuses, id.Highest, id.Lowest, 19)
+ suite.checkStatuses(statuses, id.Highest, id.Lowest, 20)
}
func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() {
@@ -284,7 +284,7 @@ func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() {
if err != nil {
suite.FailNow(err.Error())
}
- suite.checkStatuses(statuses, id.Highest, id.Lowest, 7)
+ suite.checkStatuses(statuses, id.Highest, id.Lowest, 8)
for _, s := range statuses {
if s.GetAccountID() != testAccount.ID {
diff --git a/internal/timeline/prune_test.go b/internal/timeline/prune_test.go
index 047166e9e..2cc3ef71a 100644
--- a/internal/timeline/prune_test.go
+++ b/internal/timeline/prune_test.go
@@ -40,7 +40,7 @@ func (suite *PruneTestSuite) TestPrune() {
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
suite.NoError(err)
- suite.Equal(18, pruned)
+ suite.Equal(19, pruned)
suite.Equal(5, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
}
@@ -56,7 +56,7 @@ func (suite *PruneTestSuite) TestPruneTwice() {
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
suite.NoError(err)
- suite.Equal(18, pruned)
+ suite.Equal(19, pruned)
suite.Equal(5, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
// Prune same again, nothing should be pruned this time.
@@ -78,7 +78,7 @@ func (suite *PruneTestSuite) TestPruneTo0() {
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
suite.NoError(err)
- suite.Equal(23, pruned)
+ suite.Equal(24, pruned)
suite.Equal(0, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
}
@@ -95,7 +95,7 @@ func (suite *PruneTestSuite) TestPruneToInfinityAndBeyond() {
pruned, err := suite.state.Timelines.Home.Prune(ctx, testAccountID, desiredPreparedItemsLength, desiredIndexedItemsLength)
suite.NoError(err)
suite.Equal(0, pruned)
- suite.Equal(23, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
+ suite.Equal(24, suite.state.Timelines.Home.GetIndexedLength(ctx, testAccountID))
}
func TestPruneTestSuite(t *testing.T) {
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index 9d99205f6..d24ae3ea5 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -624,7 +624,7 @@ func (c *Converter) AttachmentToAPIAttachment(ctx context.Context, a *gtsmodel.M
Y: a.FileMeta.Focus.Y,
}
- case gtsmodel.FileTypeVideo:
+ case gtsmodel.FileTypeVideo, gtsmodel.FileTypeAudio:
if i := a.FileMeta.Original.Duration; i != nil {
apiAttachment.Meta.Original.Duration = *i
}
@@ -1062,14 +1062,36 @@ func (c *Converter) StatusToWebStatus(
webStatus.PollOptions = PollOptions
}
+ // Mark local.
+ webStatus.Local = *s.Local
+
// Set additional templating
// variables on media attachments.
- for _, a := range webStatus.MediaAttachments {
- a.Sensitive = webStatus.Sensitive
+
+ // Get gtsmodel attachments
+ // into a convenient map.
+ ogAttachments := make(
+ map[string]*gtsmodel.MediaAttachment,
+ len(s.Attachments),
+ )
+ for _, a := range s.Attachments {
+ ogAttachments[a.ID] = a
}
- // Mark this as a local status.
- webStatus.Local = *s.Local
+ // Convert each API attachment
+ // into a web attachment.
+ webStatus.MediaAttachments = make(
+ []*apimodel.WebAttachment,
+ len(apiStatus.MediaAttachments),
+ )
+ for i, apiAttachment := range apiStatus.MediaAttachments {
+ ogAttachment := ogAttachments[apiAttachment.ID]
+ webStatus.MediaAttachments[i] = &apimodel.WebAttachment{
+ Attachment: apiAttachment,
+ Sensitive: apiStatus.Sensitive,
+ MIMEType: ogAttachment.File.ContentType,
+ }
+ }
return webStatus, nil
}
diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go
index 7bbca8ae7..6429df4fa 100644
--- a/internal/typeutils/internaltofrontend_test.go
+++ b/internal/typeutils/internaltofrontend_test.go
@@ -63,8 +63,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontend() {
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"enable_rss": true,
@@ -116,8 +116,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendAliasedAndMoved()
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"source": {
@@ -209,8 +209,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendWithEmojiStruct()
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [
{
"shortcode": "rainbow",
@@ -259,8 +259,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendWithEmojiIDs() {
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [
{
"shortcode": "rainbow",
@@ -305,8 +305,8 @@ func (suite *InternalToFrontendTestSuite) TestAccountToFrontendSensitive() {
"header_description": "A very old-school screenshot of the original team fortress mod for quake",
"followers_count": 2,
"following_count": 2,
- "statuses_count": 7,
- "last_status_at": "2023-12-10T09:24:00.000Z",
+ "statuses_count": 8,
+ "last_status_at": "2024-01-10T09:24:00.000Z",
"emojis": [],
"fields": [],
"source": {
@@ -943,6 +943,18 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
"emojis": [],
"fields": []
},
+ "mentions": [
+ {
+ "id": "01F8MH17FWEB39HZJ76B6VXSKF",
+ "username": "admin",
+ "url": "http://localhost:8080/@admin",
+ "acct": "admin"
+ }
+ ],
+ "tags": [],
+ "emojis": [],
+ "card": null,
+ "poll": null,
"media_attachments": [
{
"id": "01HE7Y3C432WRSNS10EZM86SA5",
@@ -971,7 +983,9 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
}
},
"description": "Photograph of a sloth, Public Domain.",
- "blurhash": "LNEC{|w}0K9GsEtPM|j[NFbHoeof"
+ "blurhash": "LNEC{|w}0K9GsEtPM|j[NFbHoeof",
+ "Sensitive": true,
+ "MIMEType": "image/jpg"
},
{
"id": "01HE7ZFX9GKA5ZZVD4FACABSS9",
@@ -983,7 +997,9 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
"preview_remote_url": null,
"meta": null,
"description": "SVG line art of a sloth, public domain",
- "blurhash": "L26*j+~qE1RP?wxut7ofRlM{R*of"
+ "blurhash": "L26*j+~qE1RP?wxut7ofRlM{R*of",
+ "Sensitive": true,
+ "MIMEType": "image/svg"
},
{
"id": "01HE88YG74PVAB81PX2XA9F3FG",
@@ -995,21 +1011,11 @@ func (suite *InternalToFrontendTestSuite) TestStatusToWebStatus() {
"preview_remote_url": null,
"meta": null,
"description": "Jolly salsa song, public domain.",
- "blurhash": null
+ "blurhash": null,
+ "Sensitive": true,
+ "MIMEType": "audio/mpeg"
}
],
- "mentions": [
- {
- "id": "01F8MH17FWEB39HZJ76B6VXSKF",
- "username": "admin",
- "url": "http://localhost:8080/@admin",
- "acct": "admin"
- }
- ],
- "tags": [],
- "emojis": [],
- "card": null,
- "poll": null,
"LanguageTag": "en",
"PollOptions": null,
"Local": false,
@@ -1249,7 +1255,7 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() {
},
"stats": {
"domain_count": 2,
- "status_count": 19,
+ "status_count": 20,
"user_count": 4
},
"thumbnail": "http://localhost:8080/assets/logo.png",
diff --git a/testrig/media/ghosts-original.mp3 b/testrig/media/ghosts-original.mp3
new file mode 100644
index 000000000..5e5cc8d81
--- /dev/null
+++ b/testrig/media/ghosts-original.mp3
Binary files differ
diff --git a/testrig/media/ghosts-small.jpg b/testrig/media/ghosts-small.jpg
new file mode 100644
index 000000000..bd01b91a0
--- /dev/null
+++ b/testrig/media/ghosts-small.jpg
Binary files differ
diff --git a/testrig/testmodels.go b/testrig/testmodels.go
index 5f41ed190..efd4785a5 100644
--- a/testrig/testmodels.go
+++ b/testrig/testmodels.go
@@ -989,6 +989,53 @@ func NewTestAttachments() map[string]*gtsmodel.MediaAttachment {
Header: util.Ptr(true),
Cached: util.Ptr(true),
},
+ "local_account_1_status_8_attachment_1": {
+ ID: "01J2M20K6K9XQC4WSB961YJHV6",
+ StatusID: "01J2M1HPFSS54S60Y0KYV23KJE",
+ URL: "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/attachment/original/01J2M20K6K9XQC4WSB961YJHV6.mp3",
+ RemoteURL: "",
+ CreatedAt: TimeMustParse("2024-01-10T11:24:00+02:00"),
+ UpdatedAt: TimeMustParse("2024-01-10T11:24:00+02:00"),
+ Type: gtsmodel.FileTypeAudio,
+ FileMeta: gtsmodel.FileMeta{
+ Original: gtsmodel.Original{
+ Width: 500,
+ Height: 500,
+ Size: 0,
+ Aspect: 0,
+ },
+ Small: gtsmodel.Small{
+ Width: 500,
+ Height: 500,
+ Size: 250000,
+ Aspect: 1,
+ },
+ Focus: gtsmodel.Focus{
+ X: 0,
+ Y: 0,
+ },
+ },
+ AccountID: "01F8MH1H7YV1Z7D2C8K2730QBF",
+ Description: "This is a track from Nine Inch Nail's \"Ghosts I-V\" album. This is the third track from \"Ghosts II\".",
+ ScheduledStatusID: "",
+ Blurhash: "LeDvfpayIUof01j[xuayxuayaxj[",
+ Processing: 2,
+ File: gtsmodel.File{
+ Path: "01F8MH1H7YV1Z7D2C8K2730QBF/attachment/original/01J2M20K6K9XQC4WSB961YJHV6.mp3",
+ ContentType: "audio/mpeg",
+ FileSize: 7483917,
+ },
+ Thumbnail: gtsmodel.Thumbnail{
+ Path: "01F8MH1H7YV1Z7D2C8K2730QBF/attachment/small/01J2M20K6K9XQC4WSB961YJHV6.jpg",
+ ContentType: "image/jpeg",
+ FileSize: 6132,
+ URL: "http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/attachment/small/01J2M20K6K9XQC4WSB961YJHV6.jpg",
+ RemoteURL: "",
+ },
+ Avatar: util.Ptr(false),
+ Header: util.Ptr(false),
+ Cached: util.Ptr(true),
+ },
"remote_account_1_status_1_attachment_1": {
ID: "01FVW7RXPQ8YJHTEXYPE7Q8ZY0",
StatusID: "01FVW7JHQFSFK166WWKR8CBA6M",
@@ -1347,6 +1394,10 @@ func newTestStoredAttachments() map[string]filenames {
Original: "team-fortress-original.jpg",
Small: "team-fortress-small.jpg",
},
+ "local_account_1_status_8_attachment_1": {
+ Original: "ghosts-original.mp3",
+ Small: "ghosts-small.jpg",
+ },
"remote_account_1_status_1_attachment_1": {
Original: "thoughtsofdog-original.jpg",
Small: "thoughtsofdog-small.jpg",
@@ -1644,6 +1695,31 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
Federated: util.Ptr(true),
ActivityStreamsType: ap.ObjectNote,
},
+ "local_account_1_status_8": {
+ ID: "01J2M1HPFSS54S60Y0KYV23KJE",
+ URI: "http://localhost:8080/users/the_mighty_zork/statuses/01J2M1HPFSS54S60Y0KYV23KJE",
+ URL: "http://localhost:8080/@the_mighty_zork/statuses/01J2M1HPFSS54S60Y0KYV23KJE",
+ Content: "<p>Thanks! Here's a NIN track</p>",
+ Text: "Thanks! Here's a NIN track",
+ AttachmentIDs: []string{"01J2M20K6K9XQC4WSB961YJHV6"},
+ CreatedAt: TimeMustParse("2024-01-10T11:24:00+02:00"),
+ UpdatedAt: TimeMustParse("2024-01-10T11:24:00+02:00"),
+ Local: util.Ptr(true),
+ AccountURI: "http://localhost:8080/users/the_mighty_zork",
+ AccountID: "01F8MH1H7YV1Z7D2C8K2730QBF",
+ InReplyToID: "01FF25D5Q0DH7CHD57CTRS6WK0",
+ InReplyToAccountID: "01F8MH17FWEB39HZJ76B6VXSKF",
+ InReplyToURI: "http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0",
+ BoostOfID: "",
+ ThreadID: "01HCWDKKBWECZJQ93E262N36VN",
+ ContentWarning: "",
+ Visibility: gtsmodel.VisibilityPublic,
+ Sensitive: util.Ptr(false),
+ Language: "en",
+ CreatedWithApplicationID: "01F8MGY43H3N2C8EWPR2FPYEXG",
+ Federated: util.Ptr(true),
+ ActivityStreamsType: ap.ObjectNote,
+ },
"local_account_2_status_1": {
ID: "01F8MHBQCBTDKN6X5VHGMMN4MA",
URI: "http://localhost:8080/users/1happyturtle/statuses/01F8MHBQCBTDKN6X5VHGMMN4MA",
@@ -2209,6 +2285,10 @@ func NewTestThreadToStatus() []*gtsmodel.ThreadToStatus {
StatusID: "01FCQSQ667XHJ9AV9T27SJJSX5",
},
{
+ ThreadID: "01HCWDKKBWECZJQ93E262N36VN",
+ StatusID: "01J2M1HPFSS54S60Y0KYV23KJE",
+ },
+ {
ThreadID: "01HCWE71MGRRDSHBKXFD5DDSWR",
StatusID: "01FN3VJGFH10KR7S2PB0GFJZYG",
},
diff --git a/web/source/css/status.css b/web/source/css/status.css
index 249033e02..5c7400654 100644
--- a/web/source/css/status.css
+++ b/web/source/css/status.css
@@ -336,6 +336,10 @@ main {
grid-area: sensitive;
align-self: center;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+
.button {
cursor: pointer;
align-self: center;
@@ -401,10 +405,18 @@ main {
grid-column: span 2;
}
- &.odd .media-wrapper:first-child, &.double .media-wrapper {
+ &.odd .media-wrapper:first-child,
+ &.double .media-wrapper {
grid-row: span 2;
}
+ @media screen and (max-width: 42rem) {
+ .media-wrapper {
+ grid-column: span 2;
+ grid-row: span 2;
+ }
+ }
+
img {
width: 100%;
height: 100%;
diff --git a/web/template/status_attachments.tmpl b/web/template/status_attachments.tmpl
index b257f2211..5df3d1c5c 100644
--- a/web/template/status_attachments.tmpl
+++ b/web/template/status_attachments.tmpl
@@ -36,16 +36,42 @@
{{- end }}
{{- define "videoPreview" }}
-<video
+<img
+ src="{{- .PreviewURL -}}"
+ loading="lazy"
{{- if .Description }}
alt="{{- .Description -}}"
title="{{- .Description -}}"
{{- end }}
- width="{{- .Meta.Original.Width -}}"
- height="{{- .Meta.Original.Height -}}"
->
- <source type="video/mp4" src="{{- .URL -}}"/>
-</video>
+ width="{{- .Meta.Small.Width -}}"
+ height="{{- .Meta.Small.Height -}}"
+/>
+{{- end }}
+
+{{- define "audioPreview" }}
+{{- if and .PreviewURL .Meta.Small.Width }}
+<img
+ src="{{- .PreviewURL -}}"
+ loading="lazy"
+ {{- if .Description }}
+ alt="{{- .Description -}}"
+ title="{{- .Description -}}"
+ {{- end }}
+ width="{{- .Meta.Small.Width -}}"
+ height="{{- .Meta.Small.Height -}}"
+/>
+{{- else }}
+<img
+ src="/assets/logo.png"
+ loading="lazy"
+ {{- if .Description }}
+ alt="{{- .Description -}}"
+ title="{{- .Description -}}"
+ {{- end }}
+ width="518"
+ height="460"
+/>
+{{- end }}
{{- end }}
{{- /* Produces something like "1 attachment", "2 attachments", etc */ -}}
@@ -77,21 +103,47 @@ media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{
{{- include "videoPreview" $media | indent 4 }}
{{- else if eq .Type "image" }}
{{- include "imagePreview" $media | indent 4 }}
+ {{- else if eq .Type "audio" }}
+ {{- include "audioPreview" $media | indent 4 }}
{{- end }}
</summary>
{{- if eq .Type "video" }}
<video
+ preload="none"
class="plyr-video photoswipe-slide"
controls
data-pswp-index="{{- $index -}}"
- data-pswp-width="{{- $media.Meta.Original.Width -}}px"
- data-pswp-height="{{- $media.Meta.Original.Height -}}px"
+ poster="{{- .PreviewURL -}}"
+ data-pswp-width="{{- $media.Meta.Small.Width -}}px"
+ data-pswp-height="{{- $media.Meta.Small.Height -}}px"
+ {{- if .Description }}
+ alt="{{- $media.Description -}}"
+ title="{{- $media.Description -}}"
+ {{- end }}
+ >
+ <source type="{{- $media.MIMEType -}}" src="{{- $media.URL -}}"/>
+ </video>
+ {{- else if eq .Type "audio" }}
+ <video
+ preload="none"
+ class="plyr-video photoswipe-slide"
+ controls
+ data-pswp-index="{{- $index -}}"
+ {{- if and $media.PreviewURL $media.Meta.Small.Width }}
+ poster="{{- .PreviewURL -}}"
+ data-pswp-width="{{- $media.Meta.Small.Width -}}px"
+ data-pswp-height="{{- $media.Meta.Small.Height -}}px"
+ {{- else }}
+ poster="/assets/logo.png"
+ width="518px"
+ height="460px"
+ {{- end }}
{{- if .Description }}
alt="{{- $media.Description -}}"
title="{{- $media.Description -}}"
{{- end }}
>
- <source type="video/mp4" src="{{- $media.URL -}}"/>
+ <source type="{{- $media.MIMEType -}}" src="{{- $media.URL -}}"/>
</video>
{{- else if eq .Type "image" }}
<a