Skip to content

Commit b8afa82

Browse files
committed
support multiple media links
1 parent 097a2da commit b8afa82

File tree

17 files changed

+291
-98
lines changed

17 files changed

+291
-98
lines changed

doc/changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- (new) Fever API support (thanks to @icefed)
44
- (new) editable feed link (thanks to @adaszko)
55
- (new) switch to feed by clicking the title in the article page (thanks to @tarasglek for suggestion)
6+
- (new) support multiple media links
67
- (fix) duplicate articles caused by the same feed addition (thanks to @adaszko)
78
- (fix) relative article links (thanks to @adazsko for the report)
89
- (fix) atom article links stored in id element (thanks to @adazsko for the report)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/nkanaev/yarr
22

3-
go 1.17
3+
go 1.18
44

55
require (
66
github.com/mattn/go-sqlite3 v1.14.7

makefile

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
11
VERSION=2.4
22
GITHASH=$(shell git rev-parse --short=8 HEAD)
33

4+
GO_TAGS = sqlite_foreign_keys sqlite_json
45
GO_LDFLAGS = -s -w -X 'main.Version=$(VERSION)' -X 'main.GitHash=$(GITHASH)'
56

67
export GOARCH ?= amd64
78
export CGO_ENABLED = 1
89

910
build_default:
1011
mkdir -p _output
11-
go build -tags "sqlite_foreign_keys" -ldflags="$(GO_LDFLAGS)" -o _output/yarr ./cmd/yarr
12+
go build -tags "$(GO_TAGS)" -ldflags="$(GO_LDFLAGS)" -o _output/yarr ./cmd/yarr
1213

1314
build_macos:
1415
mkdir -p _output/macos
15-
GOOS=darwin go build -tags "sqlite_foreign_keys macos" -ldflags="$(GO_LDFLAGS)" -o _output/macos/yarr ./cmd/yarr
16+
GOOS=darwin go build -tags "$(GO_TAGS) macos" -ldflags="$(GO_LDFLAGS)" -o _output/macos/yarr ./cmd/yarr
1617
cp src/platform/icon.png _output/macos/icon.png
1718
go run ./cmd/package_macos -outdir _output/macos -version "$(VERSION)"
1819

1920
build_linux:
2021
mkdir -p _output/linux
21-
GOOS=linux go build -tags "sqlite_foreign_keys linux" -ldflags="$(GO_LDFLAGS)" -o _output/linux/yarr ./cmd/yarr
22+
GOOS=linux go build -tags "$(GO_TAGS) linux" -ldflags="$(GO_LDFLAGS)" -o _output/linux/yarr ./cmd/yarr
2223

2324
build_windows:
2425
mkdir -p _output/windows
2526
go run ./cmd/generate_versioninfo -version "$(VERSION)" -outfile src/platform/versioninfo.rc
2627
windres -i src/platform/versioninfo.rc -O coff -o src/platform/versioninfo.syso
27-
GOOS=windows go build -tags "sqlite_foreign_keys windows" -ldflags="$(GO_LDFLAGS) -H windowsgui" -o _output/windows/yarr.exe ./cmd/yarr
28+
GOOS=windows go build -tags "$(GO_TAGS) windows" -ldflags="$(GO_LDFLAGS) -H windowsgui" -o _output/windows/yarr.exe ./cmd/yarr
2829

2930
serve:
30-
go run -tags "sqlite_foreign_keys" ./cmd/yarr -db local.db
31+
go run -tags "$(GO_TAGS)" ./cmd/yarr -db local.db
3132

3233
test:
33-
go test -tags "sqlite_foreign_keys" ./...
34+
go test -tags "$(GO_TAGS)" ./...

src/assets/index.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,14 @@ <h1><b>{{ itemSelectedDetails.title || 'untitled' }}</b></h1>
362362
</div>
363363
<hr>
364364
<div v-if="!itemSelectedReadability">
365-
<img :src="itemSelectedDetails.image" v-if="itemSelectedDetails.image" class="mb-3">
366-
<audio class="w-100" controls v-if="itemSelectedDetails.podcast_url" :src="itemSelectedDetails.podcast_url"></audio>
365+
<div v-if="contentImages.length">
366+
<figure v-for="media in contentImages">
367+
<img :src="media.url" loading="lazy">
368+
<figcaption v-if="media.description" v-html="media.description"></figcaption>
369+
</figure>
370+
</div>
371+
<audio class="w-100" controls v-for="media in contentAudios" :src="media.url"></audio>
372+
<video class="w-100" controls v-for="media in contentVideos" :src="media.url"></video>
367373
</div>
368374
<div v-html="itemSelectedContent"></div>
369375
</div>

src/assets/javascripts/app.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,18 @@ var vm = new Vue({
298298

299299
return this.itemSelectedDetails.content || ''
300300
},
301+
contentImages: function() {
302+
if (!this.itemSelectedDetails) return []
303+
return (this.itemSelectedDetails.media_links || []).filter(l => l.type === 'image')
304+
},
305+
contentAudios: function() {
306+
if (!this.itemSelectedDetails) return []
307+
return (this.itemSelectedDetails.media_links || []).filter(l => l.type === 'audio')
308+
},
309+
contentVideos: function() {
310+
if (!this.itemSelectedDetails) return []
311+
return (this.itemSelectedDetails.media_links || []).filter(l => l.type === 'video')
312+
}
301313
},
302314
watch: {
303315
'theme': {

src/parser/atom.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,16 @@ func ParseAtom(r io.Reader) (*Feed, error) {
8989
guidFromID = srcitem.ID + "::" + srcitem.Updated
9090
}
9191

92+
mediaLinks := srcitem.mediaLinks()
93+
9294
link := firstNonEmpty(srcitem.OrigLink, srcitem.Links.First("alternate"), srcitem.Links.First(""), linkFromID)
9395
dstfeed.Items = append(dstfeed.Items, Item{
94-
GUID: firstNonEmpty(guidFromID, srcitem.ID, link),
95-
Date: dateParse(firstNonEmpty(srcitem.Published, srcitem.Updated)),
96-
URL: link,
97-
Title: srcitem.Title.Text(),
98-
Content: firstNonEmpty(srcitem.Content.String(), srcitem.Summary.String(), srcitem.firstMediaDescription()),
99-
ImageURL: srcitem.firstMediaThumbnail(),
100-
AudioURL: "",
96+
GUID: firstNonEmpty(guidFromID, srcitem.ID, link),
97+
Date: dateParse(firstNonEmpty(srcitem.Published, srcitem.Updated)),
98+
URL: link,
99+
Title: srcitem.Title.Text(),
100+
Content: firstNonEmpty(srcitem.Content.String(), srcitem.Summary.String(), srcitem.firstMediaDescription()),
101+
MediaLinks: mediaLinks,
101102
})
102103
}
103104
return dstfeed, nil

src/parser/atom_test.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,11 @@ func TestAtom(t *testing.T) {
4040
SiteURL: "http://example.org/",
4141
Items: []Item{
4242
{
43-
GUID: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
44-
Date: time.Unix(1071340202, 0).UTC(),
45-
URL: "http://example.org/2003/12/13/atom03.html",
46-
Title: "Atom-Powered Robots Run Amok",
47-
Content: `<div xmlns="http://www.w3.org/1999/xhtml"><p>This is the entry content.</p></div>`,
48-
ImageURL: "",
49-
AudioURL: "",
43+
GUID: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a",
44+
Date: time.Unix(1071340202, 0).UTC(),
45+
URL: "http://example.org/2003/12/13/atom03.html",
46+
Title: "Atom-Powered Robots Run Amok",
47+
Content: `<div xmlns="http://www.w3.org/1999/xhtml"><p>This is the entry content.</p></div>`,
5048
},
5149
},
5250
}
@@ -141,9 +139,15 @@ func TestAtomImageLink(t *testing.T) {
141139
</entry>
142140
</feed>
143141
`))
144-
have := feed.Items[0].ImageURL
145-
want := `https://example.com/image.png?width=100&height=100`
146-
if want != have {
142+
if len(feed.Items[0].MediaLinks) != 1 {
143+
t.Fatalf("Expected 1 media link, got: %#v", feed.Items[0].MediaLinks)
144+
}
145+
have := feed.Items[0].MediaLinks[0]
146+
want := MediaLink{
147+
URL: `https://example.com/image.png?width=100&height=100`,
148+
Type: "image",
149+
}
150+
if !reflect.DeepEqual(want, have) {
147151
t.Fatalf("item.image_url doesn't match\nwant: %#v\nhave: %#v\n", want, have)
148152
}
149153
}
@@ -165,8 +169,8 @@ func TestAtomImageLinkDuplicated(t *testing.T) {
165169
if want != have {
166170
t.Fatalf("want: %#v\nhave: %#v\n", want, have)
167171
}
168-
if feed.Items[0].ImageURL != "" {
169-
t.Fatal("item.image_url must be unset if present in the content")
172+
if len(feed.Items[0].MediaLinks) != 0 {
173+
t.Fatal("item media link must be excluded if present in the content")
170174
}
171175
}
172176

src/parser/feed.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,14 @@ func (feed *Feed) cleanup() {
134134
feed.Items[i].Title = strings.TrimSpace(htmlutil.ExtractText(item.Title))
135135
feed.Items[i].Content = strings.TrimSpace(item.Content)
136136

137-
if item.ImageURL != "" && strings.Contains(item.Content, item.ImageURL) {
138-
feed.Items[i].ImageURL = ""
139-
}
140-
if item.AudioURL != "" && strings.Contains(item.Content, item.AudioURL) {
141-
feed.Items[i].AudioURL = ""
137+
if len(feed.Items[i].MediaLinks) > 0 {
138+
mediaLinks := make([]MediaLink, 0)
139+
for _, link := range item.MediaLinks {
140+
if !strings.Contains(item.Content, link.URL) {
141+
mediaLinks = append(mediaLinks, link)
142+
}
143+
}
144+
feed.Items[i].MediaLinks = mediaLinks
142145
}
143146
}
144147
}

src/parser/media.go

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package parser
22

3+
import (
4+
"strings"
5+
)
6+
37
type media struct {
48
MediaGroups []mediaGroup `xml:"http://search.yahoo.com/mrss/ group"`
59
MediaContents []mediaContent `xml:"http://search.yahoo.com/mrss/ content"`
@@ -8,21 +12,26 @@ type media struct {
812
}
913

1014
type mediaGroup struct {
15+
MediaContent []mediaContent `xml:"http://search.yahoo.com/mrss/ content"`
1116
MediaThumbnails []mediaThumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
1217
MediaDescriptions []mediaDescription `xml:"http://search.yahoo.com/mrss/ description"`
1318
}
1419

1520
type mediaContent struct {
16-
MediaThumbnails []mediaThumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
21+
MediaThumbnails []mediaThumbnail `xml:"http://search.yahoo.com/mrss/ thumbnail"`
22+
MediaType string `xml:"type,attr"`
23+
MediaMedium string `xml:"medium,attr"`
24+
MediaURL string `xml:"url,attr"`
25+
MediaDescription mediaDescription `xml:"http://search.yahoo.com/mrss/ description"`
1726
}
1827

1928
type mediaThumbnail struct {
2029
URL string `xml:"url,attr"`
2130
}
2231

2332
type mediaDescription struct {
24-
Type string `xml:"type,attr"`
25-
Description string `xml:",chardata"`
33+
Type string `xml:"type,attr"`
34+
Text string `xml:",chardata"`
2635
}
2736

2837
func (m *media) firstMediaThumbnail() string {
@@ -44,12 +53,59 @@ func (m *media) firstMediaThumbnail() string {
4453

4554
func (m *media) firstMediaDescription() string {
4655
for _, d := range m.MediaDescriptions {
47-
return plain2html(d.Description)
56+
return plain2html(d.Text)
4857
}
4958
for _, g := range m.MediaGroups {
5059
for _, d := range g.MediaDescriptions {
51-
return plain2html(d.Description)
60+
return plain2html(d.Text)
5261
}
5362
}
5463
return ""
5564
}
65+
66+
func (m *media) mediaLinks() []MediaLink {
67+
links := make([]MediaLink, 0)
68+
for _, thumbnail := range m.MediaThumbnails {
69+
links = append(links, MediaLink{URL: thumbnail.URL, Type: "image"})
70+
}
71+
for _, group := range m.MediaGroups {
72+
for _, thumbnail := range group.MediaThumbnails {
73+
links = append(links, MediaLink{
74+
URL: thumbnail.URL,
75+
Type: "image",
76+
})
77+
}
78+
}
79+
for _, content := range m.MediaContents {
80+
if content.MediaURL != "" {
81+
url := content.MediaURL
82+
description := content.MediaDescription.Text
83+
if strings.HasPrefix(content.MediaType, "image/") {
84+
links = append(links, MediaLink{URL: url, Type: "image", Description: description})
85+
} else if strings.HasPrefix(content.MediaType, "audio/") {
86+
links = append(links, MediaLink{URL: url, Type: "audio", Description: description})
87+
} else if strings.HasPrefix(content.MediaType, "video/") {
88+
links = append(links, MediaLink{URL: url, Type: "video", Description: description})
89+
} else if content.MediaMedium == "image" || content.MediaMedium == "audio" || content.MediaMedium == "video" {
90+
links = append(links, MediaLink{URL: url, Type: content.MediaMedium, Description: description})
91+
} else {
92+
if len(content.MediaThumbnails) > 0 {
93+
links = append(links, MediaLink{
94+
URL: content.MediaThumbnails[0].URL,
95+
Type: "image",
96+
})
97+
}
98+
}
99+
}
100+
for _, thumbnail := range content.MediaThumbnails {
101+
links = append(links, MediaLink{
102+
URL: thumbnail.URL,
103+
Type: "image",
104+
})
105+
}
106+
}
107+
if len(links) == 0 {
108+
return nil
109+
}
110+
return links
111+
}

src/parser/models.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ type Item struct {
1414
URL string
1515
Title string
1616

17-
Content string
18-
ImageURL string
19-
AudioURL string
17+
Content string
18+
MediaLinks []MediaLink
19+
}
20+
21+
type MediaLink struct {
22+
URL string
23+
Type string
24+
Description string
2025
}

0 commit comments

Comments
 (0)