support for media thumbnail and description as a backup for missing content (#800)

This commit is contained in:
Athou
2022-01-02 12:45:56 +01:00
parent 2ee9084b91
commit 4fb95799f8
8 changed files with 265 additions and 129 deletions

View File

@@ -418,6 +418,11 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.rometools</groupId>
<artifactId>rome-modules</artifactId>
<version>${rome.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.rometools</groupId> <groupId>com.rometools</groupId>
<artifactId>rome-opml</artifactId> <artifactId>rome-opml</artifactId>

View File

@@ -1,271 +1,279 @@
.main-content { .main-content {
margin-left: 250px; margin-left: 250px;
padding-left: 15px; padding-left: 15px;
} }
.full-screen .main-content { .full-screen .main-content {
width: 100%; width: 100%;
margin-left: 0; margin-left: 0;
} }
.entryList { .entryList {
padding-top: 50px; padding-top: 50px;
} }
/* entry list*/ /* entry list*/
.entrylist-header { .entrylist-header {
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
} }
.entrylist-header h3 { .entrylist-header h3 {
margin: 0; margin: 0;
line-height: 40px; line-height: 40px;
} }
.entrylist-header a { .entrylist-header a {
color: inherit; color: inherit;
} }
.expanded .entry-header,.expanded .entry-body-content { .expanded .entry-header,
padding-left: 25px; .expanded .entry-body-content {
padding-right: 25px; padding-left: 25px;
padding-right: 25px;
} }
.expanded .entry-header { .expanded .entry-header {
max-width: 650px; max-width: 650px;
} }
.full-screen .expanded .entry-header { .full-screen .expanded .entry-header {
max-width: 100%; max-width: 100%;
} }
.rtl .entry-header,.rtl .entry-body-content,.rtl.entry-name { .rtl .entry-header,
direction: rtl; .rtl .entry-body-content,
.rtl.entry-name {
direction: rtl;
} }
.rtl ul,.rtl ol { .rtl ul,
padding: 0; .rtl ol {
margin: 0 25px 10px 0px; padding: 0;
margin: 0 25px 10px 0px;
} }
#feed-accordion .entry { #feed-accordion .entry {
border-bottom: 1px solid #CCCCCC; border-bottom: 1px solid #cccccc;
} }
#feed-accordion .no-entries { #feed-accordion .no-entries {
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
margin-top: 80px; margin-top: 80px;
} }
#feed-accordion .entry-heading { #feed-accordion .entry-heading {
background-color: #ebebeb; background-color: #ebebeb;
} }
#feed-accordion .unread .entry-heading:hover { #feed-accordion .unread .entry-heading:hover {
background-color: #ebebeb; background-color: #ebebeb;
} }
#feed-accordion .unread .entry-heading { #feed-accordion .unread .entry-heading {
background-color: #fff; background-color: #fff;
} }
#feed-accordion .current.closed .entry-heading { #feed-accordion .current.closed .entry-heading {
background-color: #ffc; background-color: #ffc;
} }
#feed-accordion .entry-heading-link { #feed-accordion .entry-heading-link {
color: black; color: black;
height: 32px; height: 32px;
display: block; display: block;
cursor: pointer; cursor: pointer;
padding: 6px 0px; padding: 6px 0px;
} }
#feed-accordion .entry-heading .feed-name { #feed-accordion .entry-heading .feed-name {
color: #555; color: #555;
display: block; display: block;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
position: absolute; position: absolute;
width: 145px; width: 145px;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#feed-accordion .entry-heading .entry-name { #feed-accordion .entry-heading .entry-name {
display: block; display: block;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
margin-right: 150px; margin-right: 150px;
} }
#feed-accordion .unread .entry-heading .entry-name { #feed-accordion .unread .entry-heading .entry-name {
font-weight: bold; font-weight: bold;
} }
#feed-accordion .entry-heading .shrink { #feed-accordion .entry-heading .shrink {
margin-left: 150px; margin-left: 150px;
} }
#feed-accordion .entry-heading .entry-date { #feed-accordion .entry-heading .entry-date {
display: block; display: block;
position: absolute; position: absolute;
right: 45px; right: 45px;
} }
#feed-accordion a.entry-heading-link:hover { #feed-accordion a.entry-heading-link:hover {
text-decoration: none; text-decoration: none;
} }
#feed-accordion .entry-external-link { #feed-accordion .entry-external-link {
position: absolute; position: absolute;
right: 25px; right: 25px;
margin-top: -25px; margin-top: -25px;
color: black; color: black;
} }
#feed-accordion .entry-external-link:hover { #feed-accordion .entry-external-link:hover {
text-decoration: none; text-decoration: none;
} }
#feed-accordion .entry-body .entry-title { #feed-accordion .entry-body .entry-title {
margin-top: 5px; margin-top: 5px;
margin-bottom: 10px; margin-bottom: 10px;
font-size: 130%; font-size: 130%;
font-weight: bold; font-weight: bold;
} }
#feed-accordion .entry-body .entry-subtitle { #feed-accordion .entry-body .entry-subtitle {
display: block; display: block;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
} }
#feed-accordion .entry-body-content { #feed-accordion .entry-body-content {
max-width: 650px; max-width: 650px;
color: black; color: black;
padding-bottom: 10px; padding-bottom: 10px;
} }
.full-screen #feed-accordion .entry-body-content { .full-screen #feed-accordion .entry-body-content {
max-width: 100%; max-width: 100%;
} }
#feed-accordion .entry-enclosure { #feed-accordion .entry-enclosure {
clear: both; clear: both;
padding-top: 10px; padding-top: 10px;
}
#feed-accordion .entry-media-description {
padding-top: 10px;
} }
#feed-accordion .entry-buttons { #feed-accordion .entry-buttons {
clear: both; clear: both;
background-color: #fafafa; background-color: #fafafa;
padding: 3px 0px; padding: 3px 0px;
border-top: 1px solid #ebebeb; border-top: 1px solid #ebebeb;
} }
#feed-accordion .entry-buttons .checkbox.inline { #feed-accordion .entry-buttons .checkbox.inline {
padding-top: 0px; padding-top: 0px;
margin-left: 5px; margin-left: 5px;
} }
#feed-accordion .entry-buttons .keep-unread label { #feed-accordion .entry-buttons .keep-unread label {
display: inline; display: inline;
font-weight: inherit; font-weight: inherit;
} }
#feed-accordion .share-buttons a { #feed-accordion .share-buttons a {
color: #333333; color: #333333;
padding-left: 5px; padding-left: 5px;
} }
#feed-accordion .share-buttons a:hover { #feed-accordion .share-buttons a:hover {
text-decoration: none; text-decoration: none;
} }
#feed-accordion .tags-panel { #feed-accordion .tags-panel {
margin-left: 30px; margin-left: 30px;
} }
#feed-accordion .tags-panel .label{ #feed-accordion .tags-panel .label {
margin-left: 5px; margin-left: 5px;
} }
.select2-container-multi .select2-choices .select2-search-field input { .select2-container-multi .select2-choices .select2-search-field input {
padding: 2px padding: 2px;
} }
#feed-accordion .tag-input { #feed-accordion .tag-input {
margin: 0 0 0 5px; margin: 0 0 0 5px;
padding: 0; padding: 0;
width: 200px; width: 200px;
} }
#feed-accordion .entry-buttons label { #feed-accordion .entry-buttons label {
margin-bottom: 0px; margin-bottom: 0px;
font-size: 12px; font-size: 12px;
} }
#feed-accordion a.mark-up-to { #feed-accordion a.mark-up-to {
color: #333333; color: #333333;
position: absolute; position: absolute;
right: 30px; right: 30px;
} }
#feed-accordion a.mark-up-to:hover { #feed-accordion a.mark-up-to:hover {
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
} }
#feed-accordion .star { #feed-accordion .star {
text-decoration: none; text-decoration: none;
padding: 0px 5px; padding: 0px 5px;
} }
#feed-accordion .icon-star-yellow { #feed-accordion .icon-star-yellow {
color: gold; color: gold;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
} }
#feed-accordion .icon-star-empty { #feed-accordion .icon-star-empty {
color: #555; color: #555;
} }
#feed-accordion.expanded .entry { #feed-accordion.expanded .entry {
margin-bottom: 40px; margin-bottom: 40px;
border: 1px solid #ddd; border: 1px solid #ddd;
box-shadow: 0 0 4px #e3e5eb; box-shadow: 0 0 4px #e3e5eb;
} }
#feed-accordion.expanded .current { #feed-accordion.expanded .current {
border-left: 1px solid rgb(77, 144, 240); border-left: 1px solid rgb(77, 144, 240);
} }
#feed-accordion .current.entry-font-size-0 { #feed-accordion .current.entry-font-size-0 {
font-size: 14px; font-size: 14px;
} }
#feed-accordion .current.entry-font-size-1 { #feed-accordion .current.entry-font-size-1 {
font-size: 15px; font-size: 15px;
} }
#feed-accordion .current.entry-font-size-2 { #feed-accordion .current.entry-font-size-2 {
font-size: 16px; font-size: 16px;
} }
#feed-accordion .current.entry-font-size-3 { #feed-accordion .current.entry-font-size-3 {
font-size: 17px; font-size: 17px;
} }
#feed-accordion .current.entry-font-size-4 { #feed-accordion .current.entry-font-size-4 {
font-size: 18px; font-size: 18px;
} }
#feed-accordion .current.entry-font-size-5 { #feed-accordion .current.entry-font-size-5 {
font-size: 19px; font-size: 19px;
} }
#feed-accordion .highlight-search { #feed-accordion .highlight-search {
background-color: rgba(255, 255, 140, 0.5); background-color: rgba(255, 255, 140, 0.5);
color: #333; color: #333;
} }

View File

@@ -101,20 +101,26 @@
></div> ></div>
<div <div
class="entry-enclosure" class="entry-enclosure"
ng-if="entry.enclosureType && (entry.enclosureUrl && entry.content && entry.content.indexOf(entry.enclosureUrl) == -1)" ng-if="entry.enclosureType && entry.enclosureUrl && (!entry.content || entry.content.indexOf(entry.enclosureUrl) == -1)"
> >
<video controls ng-if="entry.enclosureType && entry.enclosureType.indexOf('video') == 0"> <video controls ng-if="entry.enclosureType.indexOf('video') == 0">
<source ng-src="{{entry.enclosureUrl | trustUrl}}" type="{{entry.enclosureType}}" /> <source ng-src="{{entry.enclosureUrl | trustUrl}}" type="{{entry.enclosureType}}" />
</video> </video>
<audio controls ng-if="entry.enclosureType && entry.enclosureType.indexOf('audio') == 0"> <audio controls ng-if="entry.enclosureType.indexOf('audio') == 0">
<source ng-src="{{entry.enclosureUrl | trustUrl}}" type="{{entry.enclosureType}}" /> <source ng-src="{{entry.enclosureUrl | trustUrl}}" type="{{entry.enclosureType}}" />
</audio> </audio>
<div ng-if="entry.enclosureType && entry.enclosureType.indexOf('image') == 0"> <div ng-if="entry.enclosureType.indexOf('image') == 0">
<img ng-src="{{entry.enclosureUrl | trustUrl}}" /> <img ng-src="{{entry.enclosureUrl | trustUrl}}" />
</div> </div>
<a ng-href="{{entry.enclosureUrl | trustUrl}}" target="_blank" ng-if="entry.enclosureType" download> <a ng-href="{{entry.enclosureUrl | trustUrl}}" target="_blank" download> {{ 'global.download' | translate }} </a>
{{ 'global.download' | translate }} </div>
</a> <div class="entry-media" ng-if="!entry.content && entry.mediaThumbnailUrl">
<img
ng-src="{{entry.mediaThumbnailUrl | trustUrl}}"
ng-attr-width="{{entry.mediaThumbnailWidth}}"
ng-attr-height="{{entry.mediaThumbnailHeight}}"
/>
<div ng-if="entry.mediaDescription" class="entry-media-description">{{entry.mediaDescription}}</div>
</div> </div>
</div> </div>
<div class="entry-buttons form-inline"> <div class="entry-buttons form-inline">

View File

@@ -10,9 +10,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.ArrayUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jdom2.Element; import org.jdom2.Element;
import org.jdom2.Namespace; import org.jdom2.Namespace;
@@ -22,6 +20,13 @@ import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.rometools.modules.mediarss.MediaEntryModule;
import com.rometools.modules.mediarss.MediaModule;
import com.rometools.modules.mediarss.types.MediaGroup;
import com.rometools.modules.mediarss.types.Metadata;
import com.rometools.modules.mediarss.types.Thumbnail;
import com.rometools.rome.feed.synd.SyndCategory;
import com.rometools.rome.feed.synd.SyndContent;
import com.rometools.rome.feed.synd.SyndEnclosure; import com.rometools.rome.feed.synd.SyndEnclosure;
import com.rometools.rome.feed.synd.SyndEntry; import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.feed.synd.SyndFeed;
@@ -30,6 +35,10 @@ import com.rometools.rome.feed.synd.SyndLinkImpl;
import com.rometools.rome.io.FeedException; import com.rometools.rome.io.FeedException;
import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.SyndFeedInput;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }))
@Singleton @Singleton
@@ -86,15 +95,28 @@ public class FeedParser {
FeedEntryContent content = new FeedEntryContent(); FeedEntryContent content = new FeedEntryContent();
content.setContent(getContent(item)); content.setContent(getContent(item));
content.setCategories(FeedUtils.truncate( content.setCategories(FeedUtils
item.getCategories().stream().map(c -> c.getName()).collect(Collectors.joining(", ")), 4096)); .truncate(item.getCategories().stream().map(SyndCategory::getName).collect(Collectors.joining(", ")), 4096));
content.setTitle(getTitle(item)); content.setTitle(getTitle(item));
content.setAuthor(StringUtils.trimToNull(item.getAuthor())); content.setAuthor(StringUtils.trimToNull(item.getAuthor()));
SyndEnclosure enclosure = Iterables.getFirst(item.getEnclosures(), null); SyndEnclosure enclosure = Iterables.getFirst(item.getEnclosures(), null);
if (enclosure != null) { if (enclosure != null) {
content.setEnclosureUrl(FeedUtils.truncate(enclosure.getUrl(), 2048)); content.setEnclosureUrl(FeedUtils.truncate(enclosure.getUrl(), 2048));
content.setEnclosureType(enclosure.getType()); content.setEnclosureType(enclosure.getType());
} }
MediaEntryModule module = (MediaEntryModule) item.getModule(MediaModule.URI);
if (module != null) {
Media media = getMedia(module);
if (media != null) {
content.setMediaDescription(media.getDescription());
content.setMediaThumbnailUrl(FeedUtils.truncate(media.getThumbnailUrl(), 2048));
content.setMediaThumbnailWidth(media.getThumbnailWidth());
content.setMediaThumbnailHeight(media.getThumbnailHeight());
}
}
entry.setContent(content); entry.setContent(content);
entries.add(entry); entries.add(entry);
@@ -166,7 +188,7 @@ public class FeedParser {
if (item.getContents().isEmpty()) { if (item.getContents().isEmpty()) {
content = item.getDescription() == null ? null : item.getDescription().getValue(); content = item.getDescription() == null ? null : item.getDescription().getValue();
} else { } else {
content = item.getContents().stream().map(c -> c.getValue()).collect(Collectors.joining(System.lineSeparator())); content = item.getContents().stream().map(SyndContent::getValue).collect(Collectors.joining(System.lineSeparator()));
} }
return StringUtils.trimToNull(content); return StringUtils.trimToNull(content);
} }
@@ -184,6 +206,41 @@ public class FeedParser {
return StringUtils.trimToNull(title); return StringUtils.trimToNull(title);
} }
private Media getMedia(MediaEntryModule module) {
Media media = getMedia(module.getMetadata());
if (media == null && ArrayUtils.isNotEmpty(module.getMediaGroups())) {
MediaGroup group = module.getMediaGroups()[0];
media = getMedia(group.getMetadata());
}
return media;
}
private Media getMedia(Metadata metadata) {
if (metadata == null) {
return null;
}
Media media = new Media();
media.setDescription(metadata.getDescription());
if (ArrayUtils.isNotEmpty(metadata.getThumbnail())) {
Thumbnail thumbnail = metadata.getThumbnail()[0];
media.setThumbnailWidth(thumbnail.getWidth());
media.setThumbnailHeight(thumbnail.getHeight());
if (thumbnail.getUrl() != null) {
media.setThumbnailUrl(thumbnail.getUrl().toString());
}
}
if (media.isEmpty()) {
return null;
}
return media;
}
private String findHub(SyndFeed feed) { private String findHub(SyndFeed feed) {
for (SyndLink l : feed.getLinks()) { for (SyndLink l : feed.getLinks()) {
if ("hub".equalsIgnoreCase(l.getRel())) { if ("hub".equalsIgnoreCase(l.getRel())) {
@@ -204,4 +261,16 @@ public class FeedParser {
return null; return null;
} }
@Data
private static class Media {
private String description;
private String thumbnailUrl;
private Integer thumbnailWidth;
private Integer thumbnailHeight;
public boolean isEmpty() {
return description == null && thumbnailUrl == null;
}
}
} }

View File

@@ -43,6 +43,17 @@ public class FeedEntryContent extends AbstractModel {
@Column(length = 255) @Column(length = 255)
private String enclosureType; private String enclosureType;
@Lob
@Column(length = Integer.MAX_VALUE)
@Type(type = "org.hibernate.type.TextType")
private String mediaDescription;
@Column(length = 2048)
private String mediaThumbnailUrl;
private Integer mediaThumbnailWidth;
private Integer mediaThumbnailHeight;
@Column(length = 4096) @Column(length = 4096)
private String categories; private String categories;

View File

@@ -3,8 +3,6 @@ package com.commafeed.backend.service;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -12,6 +10,8 @@ import com.commafeed.backend.dao.FeedEntryContentDAO;
import com.commafeed.backend.feed.FeedUtils; import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(onConstructor = @__({ @Inject })) @RequiredArgsConstructor(onConstructor = @__({ @Inject }))
@Singleton @Singleton
public class FeedEntryContentService { public class FeedEntryContentService {
@@ -35,6 +35,7 @@ public class FeedEntryContentService {
content.setAuthor(FeedUtils.truncate(FeedUtils.handleContent(content.getAuthor(), baseUrl, true), 128)); content.setAuthor(FeedUtils.truncate(FeedUtils.handleContent(content.getAuthor(), baseUrl, true), 128));
content.setTitle(FeedUtils.truncate(FeedUtils.handleContent(content.getTitle(), baseUrl, true), 2048)); content.setTitle(FeedUtils.truncate(FeedUtils.handleContent(content.getTitle(), baseUrl, true), 2048));
content.setContent(FeedUtils.handleContent(content.getContent(), baseUrl, false)); content.setContent(FeedUtils.handleContent(content.getContent(), baseUrl, false));
content.setMediaDescription(FeedUtils.handleContent(content.getMediaDescription(), baseUrl, false));
result = content; result = content;
feedEntryContentDAO.saveOrUpdate(result); feedEntryContentDAO.saveOrUpdate(result);
} else { } else {

View File

@@ -12,6 +12,7 @@ import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent;
import com.commafeed.backend.model.FeedEntryStatus; import com.commafeed.backend.model.FeedEntryStatus;
import com.commafeed.backend.model.FeedEntryTag;
import com.commafeed.backend.model.FeedSubscription; import com.commafeed.backend.model.FeedSubscription;
import com.rometools.rome.feed.synd.SyndContent; import com.rometools.rome.feed.synd.SyndContent;
import com.rometools.rome.feed.synd.SyndContentImpl; import com.rometools.rome.feed.synd.SyndContentImpl;
@@ -49,17 +50,25 @@ public class Entry implements Serializable {
entry.setFeedUrl(sub.getFeed().getUrl()); entry.setFeedUrl(sub.getFeed().getUrl());
entry.setFeedLink(sub.getFeed().getLink()); entry.setFeedLink(sub.getFeed().getLink());
entry.setIconUrl(FeedUtils.getFaviconUrl(sub, publicUrl)); entry.setIconUrl(FeedUtils.getFaviconUrl(sub, publicUrl));
entry.setTags(status.getTags().stream().map(t -> t.getName()).collect(Collectors.toList())); entry.setTags(status.getTags().stream().map(FeedEntryTag::getName).collect(Collectors.toList()));
if (content != null) { if (content != null) {
entry.setRtl(FeedUtils.isRTL(feedEntry)); entry.setRtl(FeedUtils.isRTL(feedEntry));
entry.setTitle(content.getTitle()); entry.setTitle(content.getTitle());
entry.setContent(proxyImages ? FeedUtils.proxyImages(content.getContent(), publicUrl) : content.getContent()); entry.setContent(proxyImages ? FeedUtils.proxyImages(content.getContent(), publicUrl) : content.getContent());
entry.setAuthor(content.getAuthor()); entry.setAuthor(content.getAuthor());
entry.setEnclosureType(content.getEnclosureType()); entry.setEnclosureType(content.getEnclosureType());
entry.setEnclosureUrl(proxyImages && StringUtils.contains(content.getEnclosureType(), "image") entry.setEnclosureUrl(proxyImages && StringUtils.contains(content.getEnclosureType(), "image")
? FeedUtils.proxyImage(content.getEnclosureUrl(), publicUrl) ? FeedUtils.proxyImage(content.getEnclosureUrl(), publicUrl)
: content.getEnclosureUrl()); : content.getEnclosureUrl());
entry.setMediaDescription(content.getMediaDescription());
entry.setMediaThumbnailUrl(
proxyImages ? FeedUtils.proxyImage(content.getMediaThumbnailUrl(), publicUrl) : content.getMediaThumbnailUrl());
entry.setMediaThumbnailWidth(content.getMediaThumbnailWidth());
entry.setMediaThumbnailHeight(content.getMediaThumbnailHeight());
entry.setCategories(content.getCategories()); entry.setCategories(content.getCategories());
} }
@@ -116,6 +125,18 @@ public class Entry implements Serializable {
@ApiModelProperty(value = "entry enclosure mime type, if any") @ApiModelProperty(value = "entry enclosure mime type, if any")
private String enclosureType; private String enclosureType;
@ApiModelProperty(value = "entry media description, if any")
private String mediaDescription;
@ApiModelProperty(value = "entry media thumbnail url, if any")
private String mediaThumbnailUrl;
@ApiModelProperty(value = "entry media thumbnail width, if any")
private Integer mediaThumbnailWidth;
@ApiModelProperty(value = "entry media thumbnail height, if any")
private Integer mediaThumbnailHeight;
@ApiModelProperty(value = "entry publication date", dataType = "number", required = true) @ApiModelProperty(value = "entry publication date", dataType = "number", required = true)
private Date date; private Date date;

View File

@@ -8,4 +8,19 @@
<dropColumn tableName="USERSETTINGS" columnName="readability" /> <dropColumn tableName="USERSETTINGS" columnName="readability" />
</changeSet> </changeSet>
<changeSet id="add-content-media" author="athou">
<addColumn tableName="FEEDENTRYCONTENTS">
<column name="mediaDescription" type="CLOB" />
</addColumn>
<addColumn tableName="FEEDENTRYCONTENTS">
<column name="mediaThumbnailUrl" type="VARCHAR(2048)" />
</addColumn>
<addColumn tableName="FEEDENTRYCONTENTS">
<column name="mediaThumbnailWidth" type="INT" />
</addColumn>
<addColumn tableName="FEEDENTRYCONTENTS">
<column name="mediaThumbnailHeight" type="INT" />
</addColumn>
</changeSet>
</databaseChangeLog> </databaseChangeLog>