Skip to content

Commit

Permalink
Merge pull request #12 from CoreMedia/1907.2
Browse files Browse the repository at this point in the history
1907.2
  • Loading branch information
mfaust authored Oct 30, 2019
2 parents 35b28e3 + 210657e commit a07b740
Show file tree
Hide file tree
Showing 41 changed files with 470 additions and 195 deletions.
2 changes: 2 additions & 0 deletions documentation/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ __Connector Configuration__

If the native YouTube connector should be used, the corresponding YouTube
credentials have to be provided as part of the connector configuration.
Not that one YouTube configuration represents one playlist. If you want to push into different playlists,
different configurations are required.

See: https://developers.google.com/youtube/v3/quickstart/go

Expand Down
27 changes: 17 additions & 10 deletions documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@ It provides a separate tab that shows different social network feeds and message
scheduled for publishing.
The Social Hub is implemented as a Blueprint extension.

## CoreMedia Labs Prototypes

This is a CoreMedia Labs prototype which means it __does not provide a stable API__.
The current implementation won't match the one which will be released for a future CoreMedia workspace.
Therefore, we also don't provide a proper documentation for it.
If you are interested in a custom integration, feel free to contact us!


## Terms and Description


| Term | Description |
| ---- | ----------- |
| adapter | A SocialHubAdapter is a specific implementation for a social network, e.g. the YouTubeSocialHubAdapter or TwitterSocialHubAdapter adapter. |
| connector | The Connector implements the communication with the social network or a social media tool. |
| adapter | The adapter implementation is responsible for the message composing of a social network, e.g. the _YouTubeSocialHubAdapter_ implements the composing of YouTube messages. |
| connector | The connector implementation is responsible for the publication of the composed messages. It can be a native integration of a social network or a social media tool. |


## Versioning
Expand All @@ -45,13 +52,13 @@ The philosophy behind the Social Hub is to __prepare__ content for social media
items to social media tools which take care of the actual publication.


## Adapters and Connectors
## Features

The following table shows the currently supported adapters and connectors for the various social networks.
The following table shows the currently supported features for the various social networks.

| Social Network | Connector | Adapter |
| -------------- |:---------:|:--------:|
| Twitter | x | x |
| YouTube | x | x |
| Instagram | - | x |
| Pinterest | - | x |
| Social Network | Publication | Composing |
| -------------- |:-----------:|:---------:|
| Twitter | x | x |
| YouTube | x | x |
| Instagram | - | x |
| Pinterest | - | x |
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public InstagramSocialHubAdapter(SocialHubConnector connector, InstagramAdapterS
public List<MessageProperty> getMessageProperties() {
List<MessageProperty> result = new ArrayList<>();
result.add(new MessagePropertyImpl(MessagePropertyType.MARKUP, "caption", 2000));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "images", 1));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "images", null,1, true, "image/*"));
return result;
}

Expand Down
4 changes: 0 additions & 4 deletions social-hub-adapter-pinterest/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>com.coremedia.cms</groupId>
<artifactId>cap-unified-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public List<MessageProperty> getMessageProperties() {
List<MessageProperty> result = new ArrayList<>();
result.add(new MessagePropertyImpl(MessagePropertyType.MARKUP, "note", 2000));
// result.add(new MessagePropertyImpl(MessagePropertyType.CHOICE, "boards", Arrays.asList("public", "private"), "public"));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "images", 1));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "images", null,1, true, "image/*"));
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.coremedia.blueprint.social.api.MessagePropertyType;
import com.coremedia.blueprint.social.api.SocialNetworkType;
import edu.umd.cs.findbugs.annotations.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Optional;
Expand All @@ -16,6 +18,8 @@
*/
@Component("twitterMessageContainerDescriptorFactory")
public class TwitterMessageContainerDescriptorFactory implements MessageContainerDescriptorFactory<TwitterSocialHubAdapter> {
private static final Logger LOG = LoggerFactory.getLogger(TwitterMessageContainerDescriptorFactory.class);

@NonNull
@Override
public SocialNetworkType getAdapterType() {
Expand All @@ -37,15 +41,19 @@ public Optional<MessageContainerDescriptor> createSent(@NonNull TwitterSocialHub
@NonNull Message message,
@NonNull MessageProperty messageProperty) {
if (messageProperty.getName().equals("text") && adapter.getAdapterSettings() != null) {
String timeline = adapter.getAdapterSettings().getTimeline();
try {
String timeline = adapter.getAdapterSettings().getTimeline();

MessageContainerDescriptor descriptor = new MessageContainerDescriptor("text", MessagePropertyType.MARKUP);
String html = "<a target=\"_blank\" class=\"twitter-timeline\" href=\"" + timeline + "\" style=\"color:black;\">Loading Timeline...</a>";
descriptor.setValue(html);
descriptor.setShowLabel(false);
descriptor.addScript("https://platform.twitter.com/widgets.js");
descriptor.addScriplet("if(window.twttr) {window.twttr.widgets.load();}");
return Optional.of(descriptor);
MessageContainerDescriptor descriptor = new MessageContainerDescriptor("text", MessagePropertyType.MARKUP);
String html = "<a target=\"_blank\" class=\"twitter-timeline\" href=\"" + timeline + "\" style=\"color:black;\">Loading Timeline...</a>";
descriptor.setValue(html);
descriptor.setShowLabel(false);
descriptor.addScript("https://platform.twitter.com/widgets.js");
descriptor.addScriplet("if(window.twttr) {window.twttr.widgets.load();}");
return Optional.of(descriptor);
} catch (Exception e) {
LOG.error("Failed to retrieve messages descriptor for {}", adapter.getAdapterSettings(), e);
}
}
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class TwitterPublicationResult implements PublicationResult {
private boolean isRetryable;
private int secondsToWait;
private Optional<Message> message;
private String description;


TwitterPublicationResult(Message message) {
Expand All @@ -23,6 +24,7 @@ public class TwitterPublicationResult implements PublicationResult {
this.isRetryable = exception.isCausedByNetworkIssue() || exception.exceededRateLimitation();
this.secondsToWait = exception.getRetryAfter();
this.message = Optional.empty();
this.description = exception.getMessage();
}


Expand All @@ -45,4 +47,9 @@ public int secondsToWait() {
public Optional<Message> getMessage() {
return message;
}

@Override
public String getDescription() {
return description;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public boolean isNativeHistory() {
public List<MessageProperty> getMessageProperties() {
List<MessageProperty> result = new ArrayList<>();
result.add(new MessagePropertyImpl(MessagePropertyType.MARKUP, "text", 280));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "assets", 4, false));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "assets", null,4, false, "image/*"));
return result;
}

Expand All @@ -49,7 +49,11 @@ public List<? extends Message> getMessages(@NonNull MessageState state, Date sta
public Optional<Message> getMessage(@NonNull String id) {
Optional<Message> message = getScheduler().getMessage(MessageState.SCHEDULED, id);
if (!message.isPresent()) {
return Optional.of(getDummyMessage());
message = getScheduler().getMessage(MessageState.SEND_FAILED_PERMANENTLY, id);
}

if (!message.isPresent()) {
message = Optional.of(getDummyMessage());
}

return message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
import com.coremedia.blueprint.social.api.MessageProperty;
import com.coremedia.blueprint.social.api.SocialHubAdapter;
import com.coremedia.blueprint.social.api.SocialNetworkType;
import com.coremedia.cap.common.Blob;
import com.coremedia.cap.content.Content;
import com.coremedia.xml.Markup;
import com.coremedia.xml.MarkupUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
*
*/
Expand All @@ -28,7 +31,12 @@ public Object intercept(SocialHubAdapter model, MessageProperty messageProperty,
}

if(messageProperty.getName().equals("video")) {

if(content.getType().getDescriptor("data") != null) {
Blob data = content.getBlob("data");
if(data != null && data.getSize() > 0 && data.getContentType().getPrimaryType().equals("video")) {
return Arrays.asList(content);
}
}
}

if(messageProperty.getName().equals("description")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.google.api.services.youtube.model.ResourceId;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoListResponse;
import com.google.api.services.youtube.model.VideoRecordingDetails;
import com.google.api.services.youtube.model.VideoSnippet;
import com.google.api.services.youtube.model.VideoStatus;
import com.google.common.base.Strings;
Expand Down Expand Up @@ -52,10 +51,10 @@ public class YouTubeConnector extends AbstractConnector {
public static final String REQUEST_PART_STATISTICS = "statistics";
public static final String REQUEST_PART_STATUS = "status";
public static final String REQUEST_PART_CONTENT_DETAILS = "contentDetails";
public static final String REQUEST_PART_RECORDING_DETAILS = "recordingDetails";
// public static final String REQUEST_PART_RECORDING_DETAILS = "recordingDetails";

private static final String INSERT_VIDEO_PARTS = REQUEST_PART_SNIPPET + "," + REQUEST_PART_STATUS + "," + REQUEST_PART_RECORDING_DETAILS;
private static final String UPDATE_VIDEO_PARTS = REQUEST_PART_SNIPPET + "," + REQUEST_PART_STATUS + "," + REQUEST_PART_RECORDING_DETAILS;
private static final String INSERT_VIDEO_PARTS = REQUEST_PART_SNIPPET + "," + REQUEST_PART_STATUS;
private static final String UPDATE_VIDEO_PARTS = REQUEST_PART_SNIPPET + "," + REQUEST_PART_STATUS;

private static final String INSERT_PLAYLIST_PARTS = REQUEST_PART_SNIPPET;
private static final int CACHE_TIMEOUT = 60 * 24;
Expand Down Expand Up @@ -103,6 +102,7 @@ private synchronized YouTube getYouTube() {
LOG.error("Failed to initialize youtube: {}", e.getMessage(), e);
throw new RuntimeException(e);
}
LOG.info("Successfully initialized YouTube connector for Social Hub");
}
return youTube;
}
Expand All @@ -113,7 +113,7 @@ public Optional<Message> getMessage(@NonNull String id) {
YouTube youTube = getYouTube();
VideoListResponse videoListResponse = cache.get(new VideoListCacheKey(youTube, id, CACHE_TIMEOUT));
List<Video> videos = videoListResponse.getItems();
if (videos != null && videos.size() > 0) {
if (videos != null && !videos.isEmpty()) {
Message message = createMessage(videos.get(0));
return Optional.of(message);
}
Expand All @@ -125,7 +125,7 @@ public List<? extends Message> getMessages(@NonNull MessageState state, Date sta
try {
YouTube youTube = getYouTube();
List<Message> result = new ArrayList<>();
List<Video> videoResults;
List<String> videoResults;
String playlistId = settings.getPlaylistId();

if (Strings.isNullOrEmpty(playlistId)) {
Expand All @@ -135,11 +135,15 @@ public List<? extends Message> getMessages(@NonNull MessageState state, Date sta
videoResults = cache.get(new VideoPlaylistCacheKey(youTube, playlistId, CACHE_TIMEOUT));
}

for (Video video : videoResults) {
Date createdAt = YouTubeUtil.parseDate(video.getSnippet().getPublishedAt().toString());
if (isBetween(createdAt, startTime, endTime)) {
Message message = createMessage(video);
result.add(message);
for (String videoId : videoResults) {
VideoListResponse videoListResponse = cache.get(new VideoListCacheKey(youTube, videoId, CACHE_TIMEOUT));
if(!videoListResponse.isEmpty()) {
Video video = videoListResponse.getItems().get(0);
Date createdAt = YouTubeUtil.parseDate(video.getSnippet().getPublishedAt().toString());
if (isBetween(createdAt, startTime, endTime)) {
Message message = createMessage(video);
result.add(message);
}
}
}
return limitResult(result, offset, limit);
Expand All @@ -153,6 +157,7 @@ public List<? extends Message> getMessages(@NonNull MessageState state, Date sta

@Override
public PublicationResult publishMessage(@NonNull ComposerModel composerModel) {
LOG.info("Social Hub: publishing message for YouTube");
YouTube youTube = getYouTube();
String channelId = settings.getChannelId();
String playlistId = settings.getPlaylistId();
Expand Down Expand Up @@ -193,9 +198,6 @@ public PublicationResult publishMessage(@NonNull ComposerModel composerModel) {
}
video.setStatus(status);

VideoRecordingDetails recordingDetails = new VideoRecordingDetails();
video.setRecordingDetails(recordingDetails);

Blob blob = contentVideo.getBlob("data");
String mimeType = blob.getContentType().toString();
InputStreamContent mediaContent = new InputStreamContent(mimeType, blob.getInputStream());
Expand Down Expand Up @@ -232,6 +234,7 @@ public PublicationResult publishMessage(@NonNull ComposerModel composerModel) {

@Override
public Optional<Message> deleteMessage(@NonNull String id) {
LOG.info("Social Hub: deleting message from YouTube");
YouTube youTube = getYouTube();
try {
Optional<Message> message = getMessage(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.coremedia.blueprint.social.api.Message;
import com.coremedia.blueprint.social.api.PublicationResult;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;

import java.io.IOException;
import java.util.Optional;
Expand All @@ -10,14 +11,21 @@ public class YouTubePublicationResult implements PublicationResult {

private boolean isFailed;
private Optional<Message> message;

private String description;

YouTubePublicationResult(Message message) {
this.message = Optional.of(message);
}

YouTubePublicationResult(IOException exception) {
this.isFailed = true;
if(exception instanceof GoogleJsonResponseException) {
this.description = ((GoogleJsonResponseException)exception).getDetails().getMessage().replaceAll("\\<.*?>", "");
}
else {
this.description = exception.getMessage();
}

this.message = Optional.empty();
}

Expand All @@ -41,4 +49,9 @@ public int secondsToWait() {
public Optional<Message> getMessage() {
return message;
}

@Override
public String getDescription() {
return description;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public YouTubeSocialHubAdapter(SocialHubConnector connector, YouTubeAdapterSetti
public List<MessageProperty> getMessageProperties() {
List<MessageProperty> result = new ArrayList<>();
result.add(new MessagePropertyImpl(MessagePropertyType.TEXT, "title", 70));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "video", 1));
result.add(new MessagePropertyImpl(MessagePropertyType.ASSETLIST, "video", null,1, true, "video/*"));
result.add(new MessagePropertyImpl(MessagePropertyType.CHOICE, "privacy", Arrays.asList("public", "private"), "public"));
result.add(new MessagePropertyImpl(MessagePropertyType.MARKUP, "description", 5000));
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.SearchListResponse;
import com.google.api.services.youtube.model.SearchResult;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoListResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -18,7 +18,8 @@
/**
*
*/
public class VideoChannelSearchCacheKey extends CacheKey<List<Video>> {
public class VideoChannelSearchCacheKey extends CacheKey<List<String>> {
private static final Logger LOG = LoggerFactory.getLogger(VideoChannelSearchCacheKey.class);

private final YouTube youTube;
private final String channelId;
Expand All @@ -33,21 +34,17 @@ public VideoChannelSearchCacheKey(YouTube youTube, String channelId, String sear
}

@Override
public List<Video> evaluate(Cache cache) throws IOException {
public List<String> evaluate(Cache cache) throws IOException {
LOG.info("Social Hub: channel search for YouTube");
Cache.cacheFor(pollingIntervalMinutes, TimeUnit.MINUTES);
// special handling for video on root level
List<Video> videoResults = new ArrayList<>();
List<String> videoResults = new ArrayList<>();
SearchListResponse response = youTube.search().list(YouTubeConnector.REQUEST_PART_SNIPPET).setChannelId(channelId).setType(YouTubeConnector.SEARCH_VIDEO_TYPE_SNIPPET).setMaxResults(50l).setQ(searchTerm).execute();
List<SearchResult> searchResults = response.getItems();
if (searchResults != null) {
for (SearchResult searchResult : searchResults) {
String videoId = searchResult.getId().getVideoId();
//load all the details of the video
VideoListResponse videoListResponse = youTube.videos().list(YouTubeConnector.REQUEST_PART_SNIPPET + "," + YouTubeConnector.REQUEST_PART_STATISTICS + ", " + YouTubeConnector.REQUEST_PART_CONTENT_DETAILS).setId(videoId).execute();
List<Video> videos = videoListResponse.getItems();
if (videos != null && videos.size() > 0) {
videoResults.add(videos.get(0));
}
videoResults.add(videoId);
}
}

Expand Down
Loading

0 comments on commit a07b740

Please sign in to comment.