|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+package kr.co.swh.lecture.opensource.project.youtube;
|
|
|
2
|
+
|
|
|
3
|
+import org.apache.log4j.Logger;
|
|
|
4
|
+
|
|
|
5
|
+/**
|
|
|
6
|
+ * <pre>
|
|
|
7
|
+ * kr.co.swh.lecture.opensource.project.youtube
|
|
|
8
|
+ * AddFeaturedVideo.java
|
|
|
9
|
+ *
|
|
|
10
|
+ * 설명 :
|
|
|
11
|
+ * </pre>
|
|
|
12
|
+ *
|
|
|
13
|
+ * @since : 2021. 6. 6.
|
|
|
14
|
+ * @author : tobby48
|
|
|
15
|
+ * @version : v1.0
|
|
|
16
|
+ */
|
|
|
17
|
+import com.google.api.client.auth.oauth2.Credential;
|
|
|
18
|
+import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
|
|
|
19
|
+import com.google.api.client.extensions.java6.auth.oauth2.FileCredentialStore;
|
|
|
20
|
+import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
|
|
|
21
|
+import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
|
|
|
22
|
+import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
|
|
|
23
|
+import com.google.api.client.googleapis.json.GoogleJsonResponseException;
|
|
|
24
|
+import com.google.api.client.http.HttpTransport;
|
|
|
25
|
+import com.google.api.client.http.javanet.NetHttpTransport;
|
|
|
26
|
+import com.google.api.client.json.JsonFactory;
|
|
|
27
|
+import com.google.api.client.json.jackson2.JacksonFactory;
|
|
|
28
|
+import com.google.api.services.youtube.YouTube;
|
|
|
29
|
+import com.google.api.services.youtube.model.*;
|
|
|
30
|
+import com.google.common.collect.Lists;
|
|
|
31
|
+
|
|
|
32
|
+import java.io.File;
|
|
|
33
|
+import java.io.IOException;
|
|
|
34
|
+import java.math.BigInteger;
|
|
|
35
|
+import java.util.List;
|
|
|
36
|
+
|
|
|
37
|
+/**
|
|
|
38
|
+ * This program adds a featured video to a channel via the Invideo Programming API.
|
|
|
39
|
+ *
|
|
|
40
|
+ * @author Ikai Lan <ikai@google.com>
|
|
|
41
|
+ */
|
|
|
42
|
+public class AddFeaturedVideo {
|
|
|
43
|
+
|
|
|
44
|
+ /**
|
|
|
45
|
+ * Global instance of the HTTP transport.
|
|
|
46
|
+ */
|
|
|
47
|
+ private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
|
|
|
48
|
+
|
|
|
49
|
+ /**
|
|
|
50
|
+ * Global instance of the JSON factory.
|
|
|
51
|
+ */
|
|
|
52
|
+ private static final JsonFactory JSON_FACTORY = new JacksonFactory();
|
|
|
53
|
+
|
|
|
54
|
+ /**
|
|
|
55
|
+ * Global instance of Youtube object to make all API requests.
|
|
|
56
|
+ */
|
|
|
57
|
+ private static YouTube youtube;
|
|
|
58
|
+
|
|
|
59
|
+
|
|
|
60
|
+ /**
|
|
|
61
|
+ * Authorizes the installed application to access user's protected data.
|
|
|
62
|
+ *
|
|
|
63
|
+ * @param scopes list of scopes needed to run youtube upload.
|
|
|
64
|
+ */
|
|
|
65
|
+ private static Credential authorize(List<String> scopes) throws IOException {
|
|
|
66
|
+
|
|
|
67
|
+ // Load client secrets.
|
|
|
68
|
+ GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(
|
|
|
69
|
+ JSON_FACTORY, AddFeaturedVideo.class.getResourceAsStream("/client_secrets.json"));
|
|
|
70
|
+
|
|
|
71
|
+ // Checks that the defaults have been replaced (Default = "Enter X here").
|
|
|
72
|
+ if (clientSecrets.getDetails().getClientId().startsWith("Enter")
|
|
|
73
|
+ || clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
|
|
|
74
|
+ System.out.println(
|
|
|
75
|
+ "Enter Client ID and Secret from https://console.developers.google.com/project/_/apiui/credential"
|
|
|
76
|
+ + "into youtube-cmdline-addfeaturedvideo-sample/src/main/resources/client_secrets.json");
|
|
|
77
|
+ System.exit(1);
|
|
|
78
|
+ }
|
|
|
79
|
+
|
|
|
80
|
+ // Set up file credential store.
|
|
|
81
|
+ FileCredentialStore credentialStore = new FileCredentialStore(
|
|
|
82
|
+ new File(System.getProperty("user.home"), ".credentials/youtube-api-addfeaturedvideo.json"),
|
|
|
83
|
+ JSON_FACTORY);
|
|
|
84
|
+
|
|
|
85
|
+ // Set up authorization code flow.
|
|
|
86
|
+ GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
|
|
|
87
|
+ HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, scopes).setCredentialStore(credentialStore)
|
|
|
88
|
+ .build();
|
|
|
89
|
+
|
|
|
90
|
+ // Build the local server and bind it to port 8080
|
|
|
91
|
+ LocalServerReceiver localReceiver = new LocalServerReceiver.Builder().setPort(8080).build();
|
|
|
92
|
+
|
|
|
93
|
+ // Authorize.
|
|
|
94
|
+ return new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("user");
|
|
|
95
|
+ }
|
|
|
96
|
+
|
|
|
97
|
+ /**
|
|
|
98
|
+ * This is a very simple code sample that looks up a user's channel, then features the most recently
|
|
|
99
|
+ * uploaded video in the bottom left hand corner of every single video in the channel.
|
|
|
100
|
+ *
|
|
|
101
|
+ * @param args command line args (not used).
|
|
|
102
|
+ */
|
|
|
103
|
+ public static void main(String[] args) {
|
|
|
104
|
+
|
|
|
105
|
+ // An OAuth 2 access scope that allows for full read/write access.
|
|
|
106
|
+ List<String> scopes = Lists.newArrayList("https://www.googleapis.com/auth/youtube");
|
|
|
107
|
+
|
|
|
108
|
+ try {
|
|
|
109
|
+ // Authorization.
|
|
|
110
|
+ Credential credential = authorize(scopes);
|
|
|
111
|
+
|
|
|
112
|
+ // YouTube object used to make all API requests.
|
|
|
113
|
+ youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(
|
|
|
114
|
+ "youtube-cmdline-addfeaturedvideo-sample").build();
|
|
|
115
|
+
|
|
|
116
|
+ // Fetch the user's channel. We also fetch the uploads playlist so we can use this later
|
|
|
117
|
+ // to find the most recently uploaded video
|
|
|
118
|
+ ChannelListResponse channelListResponse = youtube.channels().list("id,contentDetails")
|
|
|
119
|
+ .setMine(true)
|
|
|
120
|
+ .setFields("items(contentDetails/relatedPlaylists/uploads,id)")
|
|
|
121
|
+ .execute();
|
|
|
122
|
+
|
|
|
123
|
+ // This assumes the user has a channel already. If the user does not have a channel, this should
|
|
|
124
|
+ // throw a GoogleJsonResponseException explaining the issue
|
|
|
125
|
+ Channel myChannel = channelListResponse.getItems().get(0);
|
|
|
126
|
+ String channelId = myChannel.getId();
|
|
|
127
|
+ String uploadsPlaylistId = myChannel.getContentDetails().getRelatedPlaylists().getUploads();
|
|
|
128
|
+
|
|
|
129
|
+ // Fetch the most recently uploaded video
|
|
|
130
|
+ PlaylistItemListResponse playlistItemListResponse = youtube.playlistItems().list("snippet")
|
|
|
131
|
+ .setPlaylistId(uploadsPlaylistId)
|
|
|
132
|
+ .setFields("items/snippet")
|
|
|
133
|
+ .execute();
|
|
|
134
|
+
|
|
|
135
|
+ String featuredVideoId;
|
|
|
136
|
+ if (playlistItemListResponse.getItems().isEmpty()) {
|
|
|
137
|
+ // There are no videos on the channel. Therefore, we cannot feature a video. Exit.
|
|
|
138
|
+ System.out.println("Channel contains no videos. Featuring a default video instead from the Google Developers channel.");
|
|
|
139
|
+ featuredVideoId = "w4eiUiauo2w";
|
|
|
140
|
+ } else {
|
|
|
141
|
+ // The latest video should be the first video in the playlist response
|
|
|
142
|
+ PlaylistItem featuredVideo = playlistItemListResponse.getItems().get(0);
|
|
|
143
|
+ featuredVideoId = featuredVideo.getSnippet()
|
|
|
144
|
+ .getResourceId()
|
|
|
145
|
+ .getVideoId();
|
|
|
146
|
+
|
|
|
147
|
+ System.out.println("Featuring video: " + featuredVideo.getSnippet().getTitle());
|
|
|
148
|
+ }
|
|
|
149
|
+
|
|
|
150
|
+ // Feature this video on the channel via the Invideo programming API
|
|
|
151
|
+ // This describes the position of the video. Valid positions are bottomLeft, bottomRight, topLeft and
|
|
|
152
|
+ // topRight
|
|
|
153
|
+ InvideoPosition invideoPosition = new InvideoPosition();
|
|
|
154
|
+ invideoPosition.setCornerPosition("bottomLeft");
|
|
|
155
|
+ invideoPosition.setType("corner");
|
|
|
156
|
+
|
|
|
157
|
+ // The allowed offsets are offsetFromEnd and offsetFromStart, with offsetMs being an offset in milliseconds
|
|
|
158
|
+ InvideoTiming invideoTiming = new InvideoTiming();
|
|
|
159
|
+ invideoTiming.setOffsetMs(BigInteger.valueOf(15000l));
|
|
|
160
|
+ invideoTiming.setType("offsetFromEnd");
|
|
|
161
|
+
|
|
|
162
|
+ // Represents the type of promotion. In this case, a video with a video ID
|
|
|
163
|
+ PromotedItemId promotedItemId = new PromotedItemId();
|
|
|
164
|
+ promotedItemId.setType("video");
|
|
|
165
|
+ promotedItemId.setVideoId(featuredVideoId);
|
|
|
166
|
+
|
|
|
167
|
+ // Construct the Invidideo promotion
|
|
|
168
|
+ InvideoPromotion invideoPromotion = new InvideoPromotion();
|
|
|
169
|
+ invideoPromotion.setPosition(invideoPosition);
|
|
|
170
|
+ invideoPromotion.setTiming(invideoTiming);
|
|
|
171
|
+ invideoPromotion.setItems(Lists.newArrayList(promotedItemId));
|
|
|
172
|
+
|
|
|
173
|
+ // Now let's add the invideo promotion to the channel
|
|
|
174
|
+ Channel channel = new Channel();
|
|
|
175
|
+ channel.setId(channelId);
|
|
|
176
|
+ channel.setInvideoPromotion(invideoPromotion);
|
|
|
177
|
+
|
|
|
178
|
+ // Make the API call
|
|
|
179
|
+ Channel updateChannelResponse = youtube.channels()
|
|
|
180
|
+ .update("invideoPromotion", channel)
|
|
|
181
|
+ .execute();
|
|
|
182
|
+
|
|
|
183
|
+ // Print out returned results.
|
|
|
184
|
+ System.out.println("\n================== Updated Channel Information ==================\n");
|
|
|
185
|
+ System.out.println("\t- Channel ID: " + updateChannelResponse.getId());
|
|
|
186
|
+
|
|
|
187
|
+ InvideoPromotion promotion = updateChannelResponse.getInvideoPromotion();
|
|
|
188
|
+ System.out.println("\t- Invideo promotion video ID: " + promotion.getItems()
|
|
|
189
|
+ .get(0)
|
|
|
190
|
+ .getVideoId());
|
|
|
191
|
+ System.out.println("\t- Promotion position: " + promotion.getPosition().getCornerPosition());
|
|
|
192
|
+ System.out.println("\t- Promotion timing: " + promotion.getTiming().getOffsetMs()
|
|
|
193
|
+ + " Offset: " + promotion.getTiming().getType());
|
|
|
194
|
+ } catch (GoogleJsonResponseException e) {
|
|
|
195
|
+ System.err.println("GoogleJsonResponseException code: " + e.getDetails().getCode() + " : "
|
|
|
196
|
+ + e.getDetails().getMessage());
|
|
|
197
|
+ e.printStackTrace();
|
|
|
198
|
+
|
|
|
199
|
+ } catch (IOException e) {
|
|
|
200
|
+ System.err.println("IOException: " + e.getMessage());
|
|
|
201
|
+ e.printStackTrace();
|
|
|
202
|
+ }
|
|
|
203
|
+ }
|
|
|
204
|
+
|
|
|
205
|
+}
|