mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-23 17:01:01 +00:00
[feature/frontend] Add player for audio files; use thumbnail for poster
(#3099)
* [feature/frontend] Audio player for audio media types * use video preview images for previews instead of video itself * don't preload * update tests for new zork status * collapse media gallery into single row when small
This commit is contained in:
parent
16421f7576
commit
9efb11d848
26 changed files with 327 additions and 95 deletions
|
@ -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())
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
||||
// Additional fields not exposed via JSON
|
||||
// (used only internally for templating etc).
|
||||
// WebAttachment is like Attachment, but with
|
||||
// additional fields not exposed via JSON;
|
||||
// used only internally for templating etc.
|
||||
//
|
||||
// swagger:ignore
|
||||
type WebAttachment struct {
|
||||
*Attachment
|
||||
|
||||
// Parent status of this media is sensitive.
|
||||
Sensitive bool `json:"-"`
|
||||
// Parent status of this
|
||||
// media is sensitive.
|
||||
Sensitive bool
|
||||
|
||||
// MIME type of
|
||||
// the attachment.
|
||||
MIMEType string
|
||||
}
|
||||
|
||||
// MediaMeta models media metadata.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: "Here's a bunch of HTML, read it and weep, weep then!

```html
<section class="about-user">
 <div class="col-header">
 <h2>About</h2>
 </div> 
 <div class="fields">
 <h3 class="sr-only">Fields</h3>
 <dl>
...</description>\n <content:encoded><![CDATA[<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\"><section class="about-user">\n <div class="col-header">\n <h2>About</h2>\n </div> \n <div class="fields">\n <h3 class="sr-only">Fields</h3>\n <dl>\n <div class="field">\n <dt>should you follow me?</dt>\n <dd>maybe!</dd>\n </div>\n <div class="field">\n <dt>age</dt>\n <dd>120</dd>\n </div>\n </dl>\n </div>\n <div class="bio">\n <h3 class="sr-only">Bio</h3>\n <p>i post about things that concern me</p>\n </div>\n <div class="sr-only" role="group">\n <h3 class="sr-only">Stats</h3>\n <span>Joined in Jun, 2022.</span>\n <span>8 posts.</span>\n <span>Followed by 1.</span>\n <span>Following 1.</span>\n </div>\n <div class="accountstats" aria-hidden="true">\n <b>Joined</b><time datetime="2022-06-04T13:12:00.000Z">Jun, 2022</time>\n <b>Posts</b><span>8</span>\n <b>Followed by</b><span>1</span>\n <b>Following</b><span>1</span>\n </div>\n</section>\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: "hello everyone!"</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: "Here's a bunch of HTML, read it and weep, weep then!

```html
<section class="about-user">
 <div class="col-header">
 <h2>About</h2>
 </div> 
 <div class="fields">
 <h3 class="sr-only">Fields</h3>
 <dl>
...</description>\n <content:encoded><![CDATA[<p>Here's a bunch of HTML, read it and weep, weep then!</p><pre><code class=\"language-html\"><section class="about-user">\n <div class="col-header">\n <h2>About</h2>\n </div> \n <div class="fields">\n <h3 class="sr-only">Fields</h3>\n <dl>\n <div class="field">\n <dt>should you follow me?</dt>\n <dd>maybe!</dd>\n </div>\n <div class="field">\n <dt>age</dt>\n <dd>120</dd>\n </div>\n </dl>\n </div>\n <div class="bio">\n <h3 class="sr-only">Bio</h3>\n <p>i post about things that concern me</p>\n </div>\n <div class="sr-only" role="group">\n <h3 class="sr-only">Stats</h3>\n <span>Joined in Jun, 2022.</span>\n <span>8 posts.</span>\n <span>Followed by 1.</span>\n <span>Following 1.</span>\n </div>\n <div class="accountstats" aria-hidden="true">\n <b>Joined</b><time datetime="2022-06-04T13:12:00.000Z">Jun, 2022</time>\n <b>Posts</b><span>8</span>\n <b>Followed by</b><span>1</span>\n <b>Following</b><span>1</span>\n </div>\n</section>\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: "hello everyone!"</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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
BIN
testrig/media/ghosts-original.mp3
Normal file
BIN
testrig/media/ghosts-original.mp3
Normal file
Binary file not shown.
BIN
testrig/media/ghosts-small.jpg
Normal file
BIN
testrig/media/ghosts-small.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 6 KiB |
|
@ -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",
|
||||
|
@ -2208,6 +2284,10 @@ func NewTestThreadToStatus() []*gtsmodel.ThreadToStatus {
|
|||
ThreadID: "01HCWDKKBWECZJQ93E262N36VN",
|
||||
StatusID: "01FCQSQ667XHJ9AV9T27SJJSX5",
|
||||
},
|
||||
{
|
||||
ThreadID: "01HCWDKKBWECZJQ93E262N36VN",
|
||||
StatusID: "01J2M1HPFSS54S60Y0KYV23KJE",
|
||||
},
|
||||
{
|
||||
ThreadID: "01HCWE71MGRRDSHBKXFD5DDSWR",
|
||||
StatusID: "01FN3VJGFH10KR7S2PB0GFJZYG",
|
||||
|
|
|
@ -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%;
|
||||
|
|
|
@ -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="video/mp4" src="{{- $media.URL -}}"/>
|
||||
<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="{{- $media.MIMEType -}}" src="{{- $media.URL -}}"/>
|
||||
</video>
|
||||
{{- else if eq .Type "image" }}
|
||||
<a
|
||||
|
|
Loading…
Reference in a new issue