-* A custom fields API kellhet? Ott be lehet állítani a custom filed-re pl. azt, hogy kötelező e, így amíg nincs értéke
+actors_talent, Actors/Talent, , string, null required: false
+director, Director, , string, null required: false
+episode, Episode, ,
+enum, [1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 22, 23, 24, 25, 26, 3, 4, 5, 6, 7, 8, 9] required: false
+genre, Genre, , enum, [Novel, Spektrum saját gyártás, Thriller, action, adnimation, adult, adventure, animals, comedy]
+required: false
+notes, Notes, , string, null required: false
+parent_network, Parent Network, , enum, [AMC+ Hungary (Selekt), Selekt] required: false
+production_year, Production Year, , string, null required: false
+rating_tv, Rating TV, , enum, [18, 18+] required: false
+season, Season, , enum, [1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 3, 4, 5, 6, 7, 8, 9, Special] required: false
+show, Show, , string, null required: false
+tms_episode_id, TMS Episode ID, , string, null required: false
+tms_movie_id, TMS Movie ID, , string, null required: false
+tms_series_id, TMS Series ID, , string, null required: false
+tms_season_id, TMS Season ID, , string, null required: false
+video_category, Video Category, , enum, [Movie, Series] required: false
+video_type, Video Type, , enum, [Full Episode, Full Movie, Sports] required: false
+s3_captions_url, S3 Captions URL, , string, null required: false
+s3_video_url, S3 Video URL, , string, null required: false
+downloadable, Downloadable, , enum, [NO, YES] required: false
+MaxCustomFields: 30
+
+- Az age_rating jelenleg 18,18+. Adjam hozzá az újakat, vagy dobjam el a nem jó értékeket? A többi enumra is ez a
+ kérdés (episode, season)
+- Mit jelent a season Special?
+- A playlist neve nem kötelezően egyedi, de kezelhető egyedinek? Az update miatt érdekes (episode, season)
+- A custom fields API kellhet? Ott be lehet állítani a custom filed-re pl. azt, hogy kötelező e, így amíg nincs értéke
addig a videó nem aktív. Ennek használata nélkül is létrehozható video?
-*
+
* A sunset kezelése is kell, tehát jöhet adat későbbi sunset-el?
* A sunset és sunrise formátuma legyen egyforma (mindegy melyik) minden sorra!
* Mik a kötelező mezők? Most: playlist, sunset, sunrise, title
logging:
level:
org.hibernate.engine.jdbc.spi.SqlExceptionHelper: ERROR
+# org.springframework.web.client.RestTemplate: DEBUG
+# org.apache.http: DEBUG
+# httpclient.wire: DEBUG
mc-vod-sync:
- scheduler:
- import: true
- export: true
- sunrise-checker: true
+ service:
+ target-video-insert-enabled: true
+ target-playlist-insert-enabled: true
+ scheduler:
+ import-enabled: false
+ export-enabled: false
+ sunrise-checker-enabled: false
+ import-cron: 0 0 4 1/1 * ? *
+ export-cron: 0 0 4 1/1 * ? *
+ sunrise-checker-cron: 0 0 4 1/1 * ? *
application:
user-name: user
password: password
package hu.user.mcvodsync;
+import com.brightcove.cms.client.model.Playlist;
+import com.brightcove.cms.client.model.Video;
+import hu.user.mcvodsync.brightcove.BrightCoveClient;
+import hu.user.mcvodsync.brightcove.PagedSearch;
+import hu.user.mcvodsync.db.Asset;
import hu.user.mcvodsync.db.PlaylistSync;
import hu.user.mcvodsync.db.SyncType;
import hu.user.mcvodsync.db.repository.AssetRepository;
import hu.user.mcvodsync.db.repository.AssetSyncRepository;
import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
+import hu.user.mcvodsync.service.in.AssetImportService;
import hu.user.mcvodsync.service.in.ImportSummary;
import hu.user.mcvodsync.service.in.VodXlsProcessor;
+import hu.user.mcvodsync.service.out.AssetExportService;
+import hu.user.mcvodsync.service.out.VideoMapper;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
@Log4j2
@Autowired
private PlaylistSyncRepository playlistSyncRepository;
+ @Autowired
+ private AssetImportService assetImportService;
+
+ @Autowired
+ private AssetExportService assetExportService;
+
+ @Autowired
+ private BrightCoveClient brightCoveClient;
+
+ @Autowired
+ private VideoMapper videoMapper;
+
protected String getPlaylistSyncIds(String name) {
List<PlaylistSync> playlists = playlistSyncRepository.findAllByName(name);
assertEquals(1, playlists.size());
}
protected void cleanup() {
- cleanupSync();
+ cleanupLocal();
+ cleanupRemote();
assetRepository.deleteAllInBatch();
}
- protected void cleanupSync() {
+ protected void cleanupRemote() {
+ log.info("cleanupRemote");
+ cleanupRemotePlaylists();
+ cleanupRemoteVideos();
+ log.info("cleanupRemote OK");
+ }
+
+ private void cleanupRemoteVideos() {
+ PagedSearch<Video> page = PagedSearch.<Video>builder()
+ .query("tags:mc-vod-sync")
+ .sort("reference_id")
+ .build();
+ while (brightCoveClient.getVideosPaged(page)) {
+ page.getData().forEach(this::deleteVideo);
+ }
+
+ }
+
+ private void deleteVideo(Video video) {
+ try {
+ brightCoveClient.deleteVideo(video.getId());
+ } catch (Exception e) {
+ log.error(e);
+ }
+ }
+
+ private void cleanupRemotePlaylists() {
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query("description:mc-vod-sync")
+ .sort("name")
+ .build();
+ while (brightCoveClient.getPlaylistsPaged(page)) {
+ page.getData().forEach(this::deletePlayList);
+ }
+ }
+
+ private void deletePlayList(Playlist playlist) {
+ try {
+ brightCoveClient.deletePlaylist(playlist.getId());
+ } catch (Exception e) {
+ log.error(e);
+ }
+ }
+
+ protected void cleanupLocal() {
+ log.info("cleanupLocal");
playlistSyncRepository.deleteAllInBatch();
assetSyncRepository.deleteAllInBatch();
+ log.info("cleanupLocal OK");
}
- protected ImportSummary process(String xlsFile) {
- ImportSummary summary = null;
+ protected ImportSummary importFile(String xlsFile) {
+ ImportSummary summary;
try {
Path input = Paths.get(xlsFile);
summary = vodXlsProcessor.process(input.getFileName().toString(), Files.readAllBytes(input));
return summary;
}
+ protected void exportData() {
+ assetExportService.export();
+ }
+
+
+ protected void assertSynchronized() {
+ List<Asset> assets = assetRepository.findAll();
+ assets.forEach(this::assertVideoSynchronized);
+ Map<String, List<String>> playLists = assetImportService.getPlayLists();
+ playLists.forEach(this::assertPlaylistSynchronized);
+ }
+
+ protected void assertVideoSynchronized(Asset asset) {
+ if (Objects.isNull(asset.getReferenceId())) {
+ throw new IllegalStateException(String.format("Asset reference ID is null for %s", asset.getCatalogId()));
+ }
+ Video video = brightCoveClient.getVideoById(asset.getReferenceId());
+ Asset remoteAsset = videoMapper.toAsset(video);
+ assertEquals(asset.getHubInfo(), remoteAsset.getHubInfo());
+ assertEquals(asset.getEpisodeNr(), remoteAsset.getEpisodeNr());
+ assertEquals(asset.getProgLocalTitle(), remoteAsset.getProgLocalTitle());
+ assertEquals(asset.getReferenceId(), remoteAsset.getReferenceId());
+ assertEquals(asset.getCatalogId(), remoteAsset.getCatalogId());
+ assertEquals(asset.getSeasonLocalTitle(), remoteAsset.getSeasonLocalTitle());
+ assertEquals(asset.getProductionYear(), remoteAsset.getProductionYear());
+ }
+
+ protected void assertPlaylistSynchronized(String playlistName, List<String> ids) {
+ Playlist playlist = null;
+ try {
+ playlist = brightCoveClient.getPlaylistsByName(playlistName);
+ } catch (Exception e) {
+ }
+ assertNotNull(playlist);
+ assertNotNull(playlist.getVideoIds());
+ List<String> remoteIds = playlist.getVideoIds().stream().map(assetRepository::findCatalogIdByReferenceId).collect(Collectors.toList());
+ assertEquals(ids, remoteIds);
+ }
+
protected void checkSummary(ImportSummary summary, long expectedAll, long expectedInsert, long expectedUpdate, long expectedDelete) {
assertEquals(expectedAll, summary.getAll());
assertEquals(expectedAll, summary.getSuccess());
assertEquals(expectedAllCount, playlistSyncRepository.count());
}
-
}
// Path xlsFile = Paths.get("src/test/resources/testdata-big.xlsx");
log.info("initialLoadTest");
cleanup();
- ImportSummary summary = process("src/test/resources/testdata.xlsx");
+ ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
checkSummary(summary, 9, 9, 0, 0);
checkAsset(9, SyncType.INSERT, 9);
checkPlaylist(3, SyncType.INSERT, 3);
assertEquals("CCEM110180,CCEM110183,CCEM110068", getPlaylistSyncIds("Playlist1"));
assertEquals("CCEM128963,CCEM128965,CCEM128967", getPlaylistSyncIds("Playlist2"));
assertEquals("CCEF008678,CCEM001148,CCEM072086", getPlaylistSyncIds("Playlist3"));
+
+ exportData();
+ assertSynchronized();
}
@Test
public void changeVideoTest() throws IOException {
log.info("changeVideoTest");
- ImportSummary summary = process("src/test/resources/testdata.xlsx");
- cleanupSync();
- summary = process("src/test/resources/testdata-change-video.xlsx");
+ ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
+ cleanupLocal();
+ summary = importFile("src/test/resources/testdata-change-video.xlsx");
checkSummary(summary, 9, 0, 1, 0);
checkAsset(1, SyncType.UPDATE, 1);
checkPlaylist(0, null, 0);
@Test
public void deleteVideoTest() {
log.info("deleteVideoTest");
- ImportSummary summary = process("src/test/resources/testdata.xlsx");
- cleanupSync();
- summary = process("src/test/resources/testdata-delete-video.xlsx");
+ ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
+ cleanupLocal();
+ summary = importFile("src/test/resources/testdata-delete-video.xlsx");
checkSummary(summary, 9, 0, 1, 1);
checkAsset(1, SyncType.UPDATE, 2);
checkAsset(1, SyncType.DELETE, 2);
@Test
public void changePlaylistOrderTest() {
log.info("changePlaylistOrderTest");
- ImportSummary summary = process("src/test/resources/testdata.xlsx");
- cleanupSync();
- summary = process("src/test/resources/testdata-change-playlist-order.xlsx");
+ ImportSummary summary = importFile("src/test/resources/testdata.xlsx");
+ cleanupLocal();
+ summary = importFile("src/test/resources/testdata-change-playlist-order.xlsx");
checkSummary(summary, 8, 0, 1, 0);
checkAsset(1, SyncType.UPDATE, 1);
checkPlaylist(1, SyncType.UPDATE, 1);
package hu.user.mcvodsync;
-import com.brightcove.cms.client.model.Playlist;
-import com.brightcove.cms.client.model.Video;
+import com.brightcove.cms.client.model.*;
import com.google.common.collect.ImmutableMap;
import hu.user.mcvodsync.brightcove.BrightCoveClient;
import hu.user.mcvodsync.brightcove.PagedSearch;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.stream.Collectors;
import static org.junit.Assert.assertNotNull;
@Test
public void testCreate5PlayLists() {
- try {
- List<String> ids = getVideos();
-
- for (int i = 1; i < 6; i++) {
- Playlist playlist = new Playlist();
- playlist.setName("Playlist 0000" + i);
- playlist.setVideoIds(ids);
- playlist.setDescription("mc-vod-sync");
- Playlist playList = bcClient.createPlaylist(playlist);
- logPlaylist(playList);
- }
-
- } catch (Exception e) {
- log.error(e);
+ List<String> ids = getVideos();
+
+ for (int i = 1; i < 6; i++) {
+ Playlist playlist = new Playlist();
+ playlist.setName("Playlist 0000");
+ playlist.setVideoIds(ids);
+ playlist.setDescription("mc-vod-sync");
+ Playlist playList = bcClient.createPlaylist(playlist);
+ logPlaylist(playList);
}
}
@Test
public void testDeleteTestPlayLists() {
- try {
- PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
- .query("description:mc-vod-sync")
- .sort("name")
- .build();
- while (bcClient.getPlaylistsPaged(page)) {
- page.getData().forEach(this::deletePlayList);
- }
-
- } catch (Exception e) {
- log.error(e);
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query("description:mc-vod-sync")
+ .sort("name")
+ .build();
+ while (bcClient.getPlaylistsPaged(page)) {
+ page.getData().forEach(this::deletePlayList);
}
}
private void deletePlayList(Playlist playlist) {
- try {
- logPlaylist(playlist);
- bcClient.deletePlaylist(playlist.getId());
- } catch (Exception e) {
- log.error(e);
- }
+ logPlaylist(playlist);
+ bcClient.deletePlaylist(playlist.getId());
}
@Test
public void updatePlayLists() {
- try {
- PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
- .query("description:mc-vod-sync")
- .sort("name")
- .build();
- bcClient.getPlaylistsPaged(page);
- Playlist playlist = page.getData().get(0);
- playlist.setVideoIds(getVideos());
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query("description:mc-vod-sync")
+ .sort("name")
+ .build();
+ bcClient.getPlaylistsPaged(page);
+ Playlist playlist = page.getData().get(0);
+ playlist.setVideoIds(getVideos());
- List<String> videoIds = playlist.getVideoIds();
- assertNotNull(videoIds);
- String id1 = videoIds.get(0);
- String id2 = videoIds.get(videoIds.size() - 1);
- videoIds.set(0, id2);
- videoIds.set(videoIds.size() - 1, id1);
+ List<String> videoIds = playlist.getVideoIds();
+ assertNotNull(videoIds);
+ String id1 = videoIds.get(0);
+ String id2 = videoIds.get(videoIds.size() - 1);
+ videoIds.set(0, id2);
+ videoIds.set(videoIds.size() - 1, id1);
- Playlist playList = bcClient.updatePlaylist(playlist);
- logPlaylist(playList);
+ Playlist playList = bcClient.updatePlaylist(playlist);
+ logPlaylist(playList);
+ }
- } catch (Exception e) {
- log.error(e);
- }
+ @Test
+ public void testGetPlayListCount() {
+ log.info(bcClient.getPlaylistCount(null));
+ log.info(bcClient.getPlaylistCount("description:mc-vod-sync"));
+ log.info(bcClient.getPlaylistCount("renegát"));
}
@Test
public void testGetPlayLists() {
- try {
- PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
- .query("description:mc-vod-sync")
- .sort("name")
- .build();
- while (bcClient.getPlaylistsPaged(page)) {
- page.getData().forEach(this::logPlaylist);
- }
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query("description:mc-vod-sync")
+ .sort("name")
+ .build();
- } catch (Exception e) {
- log.error(e);
+ while (bcClient.getPlaylistsPaged(page)) {
+ page.getData().forEach(this::logPlaylistName);
+ }
+ }
+
+ @Test
+ public void testGetPlayListsByName() {
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query("name:Playlist 0000")
+ .sort("name")
+ .build();
+
+ while (bcClient.getPlaylistsPaged(page)) {
+ page.getData().forEach(this::logPlaylist);
}
+
}
@Test
public void testGetVideos() {
- try {
- PagedSearch<Video> page = PagedSearch.<Video>builder()
- .query("tags:mc-vod-sync")
- .sort("reference_id")
- .build();
- while (bcClient.getVideosPaged(page)) {
- page.getData().forEach(this::logVideo);
- }
- } catch (Exception e) {
- log.error(e);
+ PagedSearch<Video> page = PagedSearch.<Video>builder()
+ .query("tags:mc-vod-sync")
+ .sort("reference_id")
+ .build();
+ while (bcClient.getVideosPaged(page)) {
+ page.getData().forEach(this::logVideo);
}
}
@Test
public void testGetVideoById() {
- try {
- Video video = bcClient.getVideoById("6328122715112");
- logVideo(video);
- } catch (Exception e) {
- log.error(e);
- }
+ Video video = bcClient.getVideoById("6328122715112");
+ logVideo(video);
}
@Test
public void testGetVideosById() {
- try {
- List<Video> videos = bcClient.getVideosById("6328122715112,6339727091112");
- videos.forEach(this::logVideo);
- } catch (Exception e) {
- log.error(e);
- }
+ List<Video> videos = bcClient.getVideosById("6328122715112,6339727091112");
+ videos.forEach(this::logVideo);
}
@Test
public void testCreateVideo() {
- try {
+ Video video = new Video();
+ video.setName("TEST video 00001");
+ video.setReferenceId("MCVODSYNC10001");
+ video.setTags(Collections.singletonList("mc-vod-sync"));
+ video.setCustomFields(ImmutableMap.of("Notes", "notes"));
+ Video createdVideo = bcClient.createVideo(video);
+ logVideo(createdVideo);
+
+ log.info("Search result");
+ List<Video> videos = bcClient.getVideos(0, 10, "tags:mc-vod-sync");
+ videos.forEach(this::logVideo);
+
+ }
+
+ @Test
+ public void testCreate13Videos() {
+ int i = 13;
+ while (i > 0) {
Video video = new Video();
- video.setName("TEST video 00001");
- video.setReferenceId("MCVODSYNC10001");
+ video.setName("TEST video 0000" + i);
+ video.setReferenceId("MCVODSYNC0000" + i);
video.setTags(Collections.singletonList("mc-vod-sync"));
- video.setCustomFields(ImmutableMap.of("Notes", "notes"));
Video createdVideo = bcClient.createVideo(video);
logVideo(createdVideo);
-
- log.info("Search result");
- List<Video> videos = bcClient.getVideos(0, 10, "tags:mc-vod-sync");
- videos.forEach(this::logVideo);
-
-
- } catch (Exception e) {
- log.error(e);
+ i--;
}
}
@Test
- public void testCreate13Videos() {
- try {
+ public void testCreateVideoWithCustomFields() {
+ Video video = new Video();
+ video.setName("TEST video 0000 custoom attribute");
+ video.setReferenceId("MCVODSYNC00011");
+ video.setTags(Collections.singletonList("mc-vod-sync"));
+ video.putCustomFieldsItem("rating_tv", "19");
+ Video createdVideo = bcClient.createVideo(video);
+ logVideo(createdVideo);
+ }
- int i = 13;
- while (i > 0) {
- Video video = new Video();
- video.setName("TEST video 0000" + i);
- video.setReferenceId("MCVODSYNC0000" + i);
- video.setTags(Collections.singletonList("mc-vod-sync"));
- Video createdVideo = bcClient.createVideo(video);
- logVideo(createdVideo);
- i--;
- }
- } catch (Exception e) {
- log.error(e);
+ @Test
+ public void testGetCustomFields() {
+ VideoFields videoFields = bcClient.getVideoCustomFields();
+ if (Objects.nonNull(videoFields.getCustomFields())) {
+ videoFields.getCustomFields().forEach(this::logVideofieldsCustomField);
}
+// if (Objects.nonNull(videoFields.getStandardFields())) {
+// videoFields.getStandardFields().forEach(this::logVideofieldsStandardField);
+// }
+ log.info("MaxCustomFields: {}", videoFields.getMaxCustomFields());
}
@Test
@Test
public void deleteVideos() {
- try {
- List<Video> videos = bcClient.getVideos(0, 10, "tags:mc-vod-sync");
- videos.forEach(video -> {
- bcClient.deleteVideo(video.getId());
- });
-
- } catch (Exception e) {
- log.error(e);
- }
+ List<Video> videos = bcClient.getVideos(0, 10, "tags:mc-vod-sync");
+ videos.forEach(video -> {
+ bcClient.deleteVideo(video.getId());
+ });
}
@Test
public void testDeleteVideo() {
- try {
-// bcClient.deleteVideo("ref:MCVODSYNC00001");
- bcClient.deleteVideo("6340725850112,6340726442112,6340725849112,6340727220112");
- } catch (Exception e) {
- log.error(e);
- }
+ bcClient.deleteVideo("6340725850112,6340726442112,6340725849112,6340727220112");
}
private void logVideo(Video video) {
log.info("{} {} {} {}\r\n{}", playlist.getId(), playlist.getName(), playlist.getType(), playlist.getDescription(), playlist.getVideoIds());
}
+ private void logPlaylistName(Playlist playlist) {
+ log.info("{} {}", playlist.getId(), playlist.getName());
+ }
+
+ private void logVideofieldsCustomField(VideofieldsCustomField field) {
+ log.info("{}, {}, {}, {}, {} required: {}", field.getId(), field.getDisplayName(), field.getDescription(), field.getType(), field.getEnumValues(), field.getRequired());
+ }
+
+ private void logVideofieldsStandardField(VideofieldsStandardField field) {
+ log.info("{},{}, required: {}", field.getId(), field.getDescription(), field.getRequired());
+ }
+
}
package hu.user.mcvodsync;
+import com.google.common.collect.Lists;
import hu.user.mcvodsync.db.AssetSync;
import hu.user.mcvodsync.db.PlaylistSync;
import hu.user.mcvodsync.db.SyncType;
@Test
public void queryForExport() {
+ assetSyncRepository.deleteAllInBatch();
List<AssetSync> assetSyncs = new ArrayList<>();
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < 10; i++) {
assetSyncs.add(AssetSync.builder()
.syncType(SyncType.INSERT)
.catalogId("XXX" + i)
.build());
}
assetSyncRepository.saveAllAndFlush(assetSyncs);
+ List<Long> ids = assetSyncRepository.queryIdsForExport();
+ assertEquals(30, ids.size());
+ int limit = 20;
- while (true) {
- List<AssetSync> result = assetSyncRepository.findFirst100ByExportedSuccessIsNullOrderByCreated();
- if (result.isEmpty()) {
- break;
- }
+ List<List<Long>> partitions = Lists.partition(ids, limit);
+ partitions.forEach(currentIds -> {
+ List<AssetSync> result = assetSyncRepository.findAllByIdInOrderByCreated(currentIds);
result.forEach(this::processAssetSync);
- }
+ });
- List<AssetSync> result = assetSyncRepository.findFirst100ByExportedSuccessIsNullOrderByCreated();
- assertTrue(result.isEmpty());
+ ids = assetSyncRepository.queryIdsForExport();
+ assertTrue(ids.isEmpty());
assetSyncRepository.deleteAllInBatch();
}
package hu.user.mcvodsync.brightcove;
import com.brightcove.cms.client.ApiClient;
+import com.brightcove.cms.client.api.CustomFieldsApi;
import com.brightcove.cms.client.api.PlaylistsApi;
import com.brightcove.cms.client.api.VideosApi;
import com.brightcove.cms.client.model.*;
@Service
public class BrightCoveClient {
private String authToken;
+ private long tokenExpires;
+
public static final String BEARER = "Bearer ";
private final ApiClient apiClient = new ExtendedApiClient();
private final PlaylistsApi playListApi = new PlaylistsApi();
private final VideosApi videosApi = new VideosApi();
+ private final CustomFieldsApi customFieldsApi = new CustomFieldsApi();
@Autowired
private ApiProperties apiProperties;
public BrightCoveClient() {
playListApi.setApiClient(apiClient);
videosApi.setApiClient(apiClient);
+ customFieldsApi.setApiClient(apiClient);
}
private String createToken() {
.setClientSecret(apiProperties.getClientSecret())
.buildQueryMessage();
- token = client.accessToken(request, OAuthJSONAccessTokenResponse.class).getAccessToken();
-
+ OAuthJSONAccessTokenResponse accessToken = client.accessToken(request, OAuthJSONAccessTokenResponse.class);
+ token = accessToken.getAccessToken();
+ tokenExpires = System.currentTimeMillis() + accessToken.getExpiresIn() * 1000;
} catch (Exception e) {
log.error(e.getMessage());
}
}
private String getToken() {
- if (StringUtils.isBlank(authToken)) {
+ if (StringUtils.isBlank(authToken) || System.currentTimeMillis() > tokenExpires) {
authToken = BEARER + createToken();
}
return authToken;
return playListApi.createPlaylist(apiProperties.getAccountId(), MediaType.APPLICATION_JSON_VALUE, getToken(), fields);
}
- public List<Playlist> getPlaylists(int offset, int limit) throws RestClientException {
- String q = null;
- String sort = null;
- return playListApi.getPlaylists(apiProperties.getAccountId(), MediaType.APPLICATION_JSON_VALUE, getToken(), limit, offset, q, sort);
+ public List<Playlist> getPlaylists(int offset, int limit, String query) throws RestClientException {
+ String sort = "name";
+ return playListApi.getPlaylists(apiProperties.getAccountId(), MediaType.APPLICATION_JSON_VALUE, getToken(), limit, offset, query, sort);
}
public int getPlaylistCount(String query) {
int result = 0;
- PlaylistCount playlistCount = playListApi.getPlaylistCount(apiProperties.getAccountId(), MediaType.APPLICATION_JSON_VALUE, getToken(), query);
+ PlaylistCount playlistCount = getPlaylistCountWithHttpInfo(apiProperties.getAccountId(), MediaType.APPLICATION_JSON_VALUE, getToken(), query).getBody();
if (Objects.nonNull(playlistCount) && Objects.nonNull(playlistCount.getCount())) {
result = playlistCount.getCount();
}
return result;
}
+ public ResponseEntity<PlaylistCount> getPlaylistCountWithHttpInfo(String accountId, String contentType, String authorization, String query) throws RestClientException {
+ Object postBody = null;
+
+ // verify the required parameter 'accountId' is set
+ if (accountId == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'accountId' when calling getPlaylistCount");
+ }
+
+ // verify the required parameter 'contentType' is set
+ if (contentType == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'contentType' when calling getPlaylistCount");
+ }
+
+ // verify the required parameter 'authorization' is set
+ if (authorization == null) {
+ throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'authorization' when calling getPlaylistCount");
+ }
+
+ // create path and map variables
+ final Map<String, Object> uriVariables = new HashMap<String, Object>();
+ uriVariables.put("account_id", accountId);
+
+ final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
+ final HttpHeaders headerParams = new HttpHeaders();
+ final MultiValueMap<String, String> cookieParams = new LinkedMultiValueMap<String, String>();
+ final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
+
+ queryParams.putAll(apiClient.parameterToMultiValueMap(null, "q", query));
+
+ if (contentType != null)
+ headerParams.add("Content-Type", apiClient.parameterToString(contentType));
+ if (authorization != null)
+ headerParams.add("Authorization", apiClient.parameterToString(authorization));
+
+ final String[] localVarAccepts = {
+ "application/json"
+ };
+ final List<MediaType> localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
+ final String[] contentTypes = {};
+ final MediaType localVarContentType = apiClient.selectHeaderContentType(contentTypes);
+
+ String[] authNames = new String[]{"BC_OAuth2"};
+
+ ParameterizedTypeReference<PlaylistCount> returnType = new ParameterizedTypeReference<PlaylistCount>() {
+ };
+ return apiClient.invokeAPI("/v1/accounts/{account_id}/counts/playlists", HttpMethod.GET, uriVariables, queryParams, postBody, headerParams, cookieParams, formParams, localVarAccept, localVarContentType, authNames, returnType);
+ }
+
+
+ public Playlist getPlaylistsByName(String name) throws Exception {
+ Playlist result = null;
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query(String.format("name:%s", name))
+ .sort("name")
+ .build();
+ if (getPlaylistsPaged(page) && Objects.nonNull(page.getData())) {
+ if (page.getData().size() > 1) {
+ throw new Exception(String.format("Multiple playlists found for name %s", name));
+ }
+ if (!page.getData().isEmpty()) {
+ result = page.getData().get(0);
+ }
+ }
+ return result;
+ }
+
+
public boolean getPlaylistsPaged(PagedSearch<Playlist> page) throws RestClientException {
if (page.getCount() == 0) {
int count = getPlaylistCount(page.getQuery());
return videosApi.getApiClient().invokeAPI("/v1/accounts/{account_id}/videos{video_ids}", HttpMethod.GET, uriVariables, queryParams, postBody, headerParams, cookieParams, formParams, localVarAccept, localVarContentType, authNames, returnType);
}
+
public void deletePlaylist(String id) {
playListApi.deletePlaylists(apiProperties.getAccountId(), "/" + id, MediaType.APPLICATION_JSON_VALUE, getToken());
}
+
+ public VideoFields getVideoCustomFields() {
+ return customFieldsApi.getVideoFields(apiProperties.getAccountId(), MediaType.APPLICATION_JSON_VALUE, getToken());
+ }
}
CREATE TABLE upload_file (
id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
- created TIMESTAMP,
+ created TIMESTAMP NOT NULL,
name VARCHAR(255),
size INTEGER NOT NULL,
file BLOB,
package hu.user.mcvodsync.db;
import lombok.*;
+import org.hibernate.annotations.CreationTimestamp;
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
+import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
+ @CreationTimestamp
+ @Column(updatable = false)
Date created;
String name;
@Modifying
@Query("DELETE FROM Asset a WHERE a.sunset <= :currentDate")
void deleteExpired(Date currentDate);
+
+ @Query("SELECT a.referenceId FROM Asset a WHERE a.catalogId = :catalogId")
+ String findReferenceIdByCatalogId(String catalogId);
+
+ @Query("SELECT a.catalogId FROM Asset a WHERE a.referenceId = :referenceId")
+ String findCatalogIdByReferenceId(String referenceId);
}
package hu.user.mcvodsync.db.repository;
import hu.user.mcvodsync.db.AssetSync;
-import hu.user.mcvodsync.db.PlaylistSync;
import hu.user.mcvodsync.db.SyncType;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
import java.util.List;
long countBySyncType(SyncType syncType);
- Page<AssetSync> findAllByExportedSuccessIsNullOrderByCreated(Pageable page);
+ @Query("SELECT a.id FROM AssetSync a WHERE a.exportedSuccess IS NULL ORDER BY a.created")
+ List<Long> queryIdsForExport();
- List<AssetSync> findFirst100ByExportedSuccessIsNullOrderByCreated();
+ List<AssetSync> findAllByIdInOrderByCreated(List<Long> id);
- List<PlaylistSync> findAllByOrderByCreated();
}
import hu.user.mcvodsync.db.PlaylistSync;
import hu.user.mcvodsync.db.SyncType;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
import java.util.List;
long countBySyncType(SyncType syncType);
List<PlaylistSync> findAllByName(String name);
+
+ @Query("SELECT p.id FROM PlaylistSync p WHERE p.exportedSuccess IS NULL ORDER BY p.created")
+ List<Long> queryIdsForExport();
+
+ List<PlaylistSync> findAllByIdInOrderByCreated(List<Long> id);
}
import hu.user.mcvodsync.db.UploadFile;
import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.Optional;
+
public interface UploadFileRepository extends JpaRepository<UploadFile, Long>, UploadFileRepositorySearch {
+ Optional<UploadFile> findFirstByOrderByCreatedDesc();
}
private boolean targetVideoInsertEnabled;
private boolean targetPlaylistInsertEnabled;
+
+ private Scheduler scheduler;
+
+ @Getter
+ @Setter
+ public static final class Scheduler {
+
+ private boolean importEnabled;
+
+ private String importCron;
+
+ private boolean exportEnabled;
+
+ private String exportCron;
+
+ private boolean sunriseCheckerEnabled;
+
+ private String sunriseCheckerCron;
+ }
}
package hu.user.mcvodsync.service.config;
+import hu.user.mcvodsync.service.ServiceProperties;
+import hu.user.mcvodsync.service.event.ImportCompletedEvent;
+import hu.user.mcvodsync.service.event.ImportStartedEvent;
+import hu.user.mcvodsync.service.in.ImportSummary;
import hu.user.mcvodsync.service.schedule.ScheduledExport;
import hu.user.mcvodsync.service.schedule.ScheduledImport;
import hu.user.mcvodsync.service.schedule.ScheduledSunriseChacker;
+import lombok.Getter;
+import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
import org.springframework.scheduling.TaskScheduler;
-import org.springframework.scheduling.support.PeriodicTrigger;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
-import java.util.concurrent.TimeUnit;
+import java.time.Instant;
+@Log4j2
@Component
public class ScheduledTasks {
+
+ @Getter
+ private ImportSummary importSummary;
+
+ public ScheduledTasks() {
+ log.info("ScheduledTasks created");
+ }
+
+ @Autowired
+ private ServiceProperties serviceProperties;
+
@Autowired
private TaskScheduler taskScheduler;
@PostConstruct
public void scheduleTasks() {
//new CronTrigger("0 0 4 1/1 * ? *")
+ //new PeriodicTrigger(1, TimeUnit.SECONDS)
// https://www.baeldung.com/spring-task-scheduler
- taskScheduler.schedule(scheduledExport, new PeriodicTrigger(1, TimeUnit.SECONDS));
- taskScheduler.schedule(scheduledImport, new PeriodicTrigger(1, TimeUnit.SECONDS));
- //taskScheduler.schedule(scheduledSunriseChacker, new PeriodicTrigger(1, TimeUnit.SECONDS));
+ ServiceProperties.Scheduler scheduler = serviceProperties.getScheduler();
+ if (scheduler.isExportEnabled()) {
+ taskScheduler.schedule(scheduledExport, new CronTrigger(scheduler.getImportCron()));
+ }
+ if (scheduler.isImportEnabled()) {
+ taskScheduler.schedule(scheduledImport, new CronTrigger(scheduler.getExportCron()));
+ }
+ if (scheduler.isSunriseCheckerEnabled()) {
+ taskScheduler.schedule(scheduledSunriseChacker, new CronTrigger(scheduler.getSunriseCheckerCron()));
+ }
}
+ public void startImport() {
+ taskScheduler.schedule(scheduledImport, Instant.now());
+ }
+
+ public void startExport() {
+ taskScheduler.schedule(scheduledExport, Instant.now());
+ }
+
+ @Async
+ @EventListener
+ public void handleEvent(ImportStartedEvent evt) {
+ log.info("ImportStartedEvent handle");
+ importSummary = null;
+ }
+
+ @Async
+ @EventListener
+ public void handleEvent(ImportCompletedEvent evt) {
+ log.info("ImportCompletedEvent handle");
+ importSummary = evt.getSummary();
+ }
}
--- /dev/null
+package hu.user.mcvodsync.service.event;
+
+import hu.user.mcvodsync.service.in.ImportSummary;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+@Setter
+public class ExportCompletedEvent extends ApplicationEvent {
+
+ private final ImportSummary summary;
+
+ public ExportCompletedEvent(ImportSummary summary, Object source) {
+ super(source);
+ this.summary = summary;
+ }
+}
--- /dev/null
+package hu.user.mcvodsync.service.event;
+
+import hu.user.mcvodsync.service.in.ImportSummary;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+@Setter
+public class ImportCompletedEvent extends ApplicationEvent {
+
+ private final ImportSummary summary;
+
+ public ImportCompletedEvent(ImportSummary summary, Object source) {
+ super(source);
+ this.summary = summary;
+ }
+}
--- /dev/null
+package hu.user.mcvodsync.service.event;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+@Setter
+public class ImportStartedEvent extends ApplicationEvent {
+
+ public ImportStartedEvent(Object source) {
+ super(source);
+ }
+}
currentPlaylists = getPlayLists();
}
- private Map<String, List<String>> getPlayLists() {
+ public Map<String, List<String>> getPlayLists() {
Map<String, List<String>> result = new HashMap<>();
List<String> playlistNames = assetRepository.queryDistinctPlaylists();
playlistNames.forEach(playlist -> {
package hu.user.mcvodsync.service.out;
+import com.brightcove.cms.client.model.Playlist;
import com.brightcove.cms.client.model.Video;
+import com.google.common.collect.Lists;
import hu.user.mcvodsync.brightcove.BrightCoveClient;
import hu.user.mcvodsync.db.Asset;
import hu.user.mcvodsync.db.AssetSync;
import javax.persistence.EntityNotFoundException;
import java.time.Instant;
+import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
@Log4j2
@Service
@Autowired
private ServiceProperties serviceProperties;
+ private static final int ASSET_PROCESSING_LIMIT = 100;
+ private static final int PLAYLIST_PROCESSING_LIMIT = 10;
+
public void export() {
exportAssets();
exportPlaylists();
}
private void exportAssets() {
- while (true) {
- List<AssetSync> result = assetSyncRepository.findFirst100ByExportedSuccessIsNullOrderByCreated();
- if (result.isEmpty()) {
- log.info("There are no video changes to export");
- break;
- }
-
+ List<Long> ids = assetSyncRepository.queryIdsForExport();
+ if (ids.isEmpty()) {
+ log.info("There are no video changes to export");
+ return;
+ }
+ List<List<Long>> partitions = Lists.partition(ids, ASSET_PROCESSING_LIMIT);
+ partitions.forEach(currentIds -> {
+ List<AssetSync> result = assetSyncRepository.findAllByIdInOrderByCreated(currentIds);
if (serviceProperties.isTargetVideoInsertEnabled()) {
result.forEach(this::processAssetSync);
} else {
result.forEach(this::processAssetSyncWithoutInsert);
}
- }
+ });
}
@Transactional
} else {
Asset asset = assetRepository.findById(assetSync.getCatalogId()).orElseThrow(EntityNotFoundException::new);
Video assetVideo = videoMapper.toVideo(asset);
+ assetVideo.setTags(Collections.singletonList("mc-vod-sync"));
if (SyncType.INSERT.equals(assetSync.getSyncType())) {
Video createdVideo = bcClient.createVideo(assetVideo);
asset.setReferenceId(createdVideo.getId());
}
private void exportPlaylists() {
- while (true) {
- List<PlaylistSync> result = assetSyncRepository.findAllByOrderByCreated();
- if (result.isEmpty()) {
- log.info("There are no playlist changes to export");
- break;
- }
-
+ List<Long> ids = playlistSyncRepository.queryIdsForExport();
+ if (ids.isEmpty()) {
+ log.info("There are no playlist changes to export");
+ return;
+ }
+ List<List<Long>> partitions = Lists.partition(ids, PLAYLIST_PROCESSING_LIMIT);
+ partitions.forEach(currentIds -> {
+ List<PlaylistSync> result = playlistSyncRepository.findAllByIdInOrderByCreated(currentIds);
if (serviceProperties.isTargetPlaylistInsertEnabled()) {
result.forEach(this::processPlaylistSync);
} else {
result.forEach(this::processPlaylistSyncWithoutInsert);
}
- }
+ });
}
+
@Transactional
public void processPlaylistSync(PlaylistSync playlistSync) {
try {
if (SyncType.DELETE.equals(playlistSync.getSyncType())) {
playlistSync.setExportedSuccess(Date.from(Instant.now()));
} else {
-
-// Playlist playlist = playlistRepository.findById(assetSync.getCatalogId()).orElseThrow(EntityNotFoundException::new);
-// Video assetVideo = videoMapper.toVideo(asset);
-// if (SyncType.INSERT.equals(assetSync.getSyncType())) {
-// Video createdVideo = bcClient.createVideo(assetVideo);
-// asset.setReferenceId(createdVideo.getId());
-// assetRepository.save(asset);
-// }
-// if (SyncType.UPDATE.equals(assetSync.getSyncType())) {
-// if (StringUtils.isBlank(asset.getReferenceId())) {
-// throw new IllegalArgumentException("Can not update Video when Asset entity reference ID is empty");
-// }
-// bcClient.updateVideo(asset.getReferenceId(), assetVideo);
-// }
-// assetSync.setExportedSuccess(Date.from(Instant.now()));
+ Playlist playlist = bcClient.getPlaylistsByName(playlistSync.getName());
+ //TODO check if all Asset has rferenceId
+ if (Objects.nonNull(playlist)) {
+ playlist.setVideoIds(toApiIds(playlistSync.getIds()));
+ bcClient.updatePlaylist(playlist);
+ } else {
+ playlist = new Playlist();
+ playlist.setName(playlistSync.getName());
+ playlist.setDescription("mc-vod-sync");
+ playlist.setVideoIds(toApiIds(playlistSync.getIds()));
+ bcClient.createPlaylist(playlist);
+ }
+ playlistSync.setExportedSuccess(Date.from(Instant.now()));
}
} catch (Exception e) {
log.error(e);
-// assetSync.setExportedError(Date.from(Instant.now()));
+ playlistSync.setExportedError(Date.from(Instant.now()));
+ } finally {
+ playlistSyncRepository.save(playlistSync);
}
}
+ private List<String> toApiIds(List<String> ids) {
+ return ids.stream().map(assetRepository::findReferenceIdByCatalogId).collect(Collectors.toList());
+ }
+
@Transactional
public void processPlaylistSyncWithoutInsert(PlaylistSync playlistSync) {
+ try {
+ if (SyncType.DELETE.equals(playlistSync.getSyncType())) {
+ playlistSync.setExportedSuccess(Date.from(Instant.now()));
+ } else {
+ Playlist playlist = bcClient.getPlaylistsByName(playlistSync.getName());
+ if (Objects.nonNull(playlist)) {
+ playlist.setVideoIds(toApiIds(playlistSync.getIds()));
+ bcClient.updatePlaylist(playlist);
+ } else {
+ log.warn("Playlist not found: {}", playlistSync.getName());
+ }
+ playlistSync.setExportedSuccess(Date.from(Instant.now()));
+ }
+ } catch (Exception e) {
+ log.error(e);
+ playlistSync.setExportedError(Date.from(Instant.now()));
+ }
}
}
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
+import java.util.*;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface VideoMapper {
- String NOTES = "Notes";
- String SHOW = "Show";
- String SEASON = "Season";
- String EPISODE = "Episode";
- String RATING_TV = "Rating TV";
- String PRODUCTION_YEAR = "Production Year";
+ String NOTES = "notes";
+ String SHOW = "show";
+ String SEASON = "season";
+ String EPISODE = "episode";
+ String RATING_TV = "rating_tv";
+ String PRODUCTION_YEAR = "production_year";
+ String ACTORS_TALENT = "actors_talent";
+ String DIRECTOR = "director";
+ String GENRE = "genre";
+ String PARENT_NETWORK = "parent_network";
+ List<String> valid_AgeRating = Arrays.asList("18", "18+");
+
+ /*
+ tms_episode_id, TMS Episode ID, , string, null required: false
+ tms_movie_id, TMS Movie ID, , string, null required: false
+ tms_series_id, TMS Series ID, , string, null required: false
+ tms_season_id, TMS Season ID, , string, null required: false
+ video_category, Video Category, , enum, [Movie, Series] required: false
+ video_type, Video Type, , enum, [Full Episode, Full Movie, Sports] required: false
+ s3_captions_url, S3 Captions URL, , string, null required: false
+ s3_video_url, S3 Video URL, , string, null required: false
+ downloadable, Downloadable, , enum, [NO, YES] required: false
+ */
default Video toVideo(Asset asset) {
Video result = new Video();
result.setReferenceId(asset.getCatalogId());
if (Objects.nonNull(asset.getEpisodeNr())) {
result.putCustomFieldsItem(EPISODE, asset.getEpisodeNr().toString());
}
- result.putCustomFieldsItem(RATING_TV, asset.getAgeRating());
+ if (Objects.nonNull(asset.getAgeRating())
+ && valid_AgeRating.contains(asset.getAgeRating())) {
+ result.putCustomFieldsItem(RATING_TV, asset.getAgeRating());
+ }
if (Objects.nonNull(asset.getProductionYear())) {
result.putCustomFieldsItem(PRODUCTION_YEAR, asset.getProductionYear().toString());
}
package hu.user.mcvodsync.service.schedule;
+import hu.user.mcvodsync.service.event.ExportCompletedEvent;
+import hu.user.mcvodsync.service.out.AssetExportService;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
-import java.util.concurrent.ThreadLocalRandom;
-
@Log4j2
@Component
-//@ConditionalOnExpression("'${mc-vod-sync.scheduler.export}'=='true'")
public class ScheduledExport implements Runnable {
- // @Scheduled(initialDelay = 2000, fixedDelay = 200)
+ @Autowired
+ private ApplicationEventPublisher applicationEventPublisher;
+
+ @Autowired
+ private AssetExportService assetExportService;
+
@SneakyThrows
@Override
public void run() {
try {
- log.info("Start 1");
- log.info("Execute 1");
- int randomNum = ThreadLocalRandom.current().nextInt(100, 2000);
- Thread.sleep(randomNum);
- log.info("Finish 1");
-
+ log.info("ScheduledExport started");
+ assetExportService.export();
} catch (Exception e) {
- log.error(e);
+ log.error("ScheduledExport error!", e);
+ } finally {
+ applicationEventPublisher.publishEvent(new ExportCompletedEvent(null, this));
+ log.info("ScheduledImport finished");
}
-
}
}
package hu.user.mcvodsync.service.schedule;
-import lombok.SneakyThrows;
+import hu.user.mcvodsync.db.UploadFile;
+import hu.user.mcvodsync.db.repository.UploadFileRepository;
+import hu.user.mcvodsync.service.event.ImportCompletedEvent;
+import hu.user.mcvodsync.service.event.ImportStartedEvent;
+import hu.user.mcvodsync.service.in.ImportSummary;
+import hu.user.mcvodsync.service.in.VodXlsProcessor;
import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
-import java.util.concurrent.ThreadLocalRandom;
+import java.util.Optional;
@Log4j2
@Component
-//@ConditionalOnExpression("'${mc-vod-sync.scheduler.import}'=='true'")
public class ScheduledImport implements Runnable {
- @SneakyThrows
+ @Autowired
+ private ApplicationEventPublisher applicationEventPublisher;
+
+ @Autowired
+ private UploadFileRepository uploadFileRepository;
+
+ @Autowired
+ private VodXlsProcessor vodXlsProcessor;
+
@Override
public void run() {
+ applicationEventPublisher.publishEvent(new ImportStartedEvent(this));
+ ImportSummary summary = null;
try {
- log.info("Start 2");
- log.info("Execute 2");
- int randomNum = ThreadLocalRandom.current().nextInt(100, 2000);
- Thread.sleep(randomNum);
- log.info("Finish 2");
-
+ log.info("ScheduledImport started");
+ Optional<UploadFile> opUploadFile = uploadFileRepository.findFirstByOrderByCreatedDesc();
+ if (opUploadFile.isPresent()) {
+ summary = vodXlsProcessor.process(opUploadFile.get().getName(), opUploadFile.get().getFile());
+ }
} catch (Exception e) {
- log.error(e);
+ log.error("ScheduledImport error!", e);
+ } finally {
+ applicationEventPublisher.publishEvent(new ImportCompletedEvent(summary, this));
+ log.info("ScheduledImport finished");
}
}
public class Constants {
public static final String APPLICATION_NAME = "MC-VOD-SYNC";
- public static final String NAV_UPLOAD_FILES = "/upload-files";
+ public static final String NAV_IMPORT_FILE = "/import-file";
+ public static final String NAV_BROWSE_REMOTE = "/browse-remote";
+ public static final String NAV_EXPORT_DATA = "/export-data";
public static final String NAV_ASSOCIATES = "/associates";
public static final String NAV_LOGIN = "/login";
public static final String NAV_LOGOUT = "/logout";
return "login";
}
- @GetMapping({Constants.NAV_UPLOAD_FILES})
+ @GetMapping({Constants.NAV_IMPORT_FILE, Constants.NAV_BROWSE_REMOTE, Constants.NAV_EXPORT_DATA, Constants.NAV_ASSOCIATES})
public String index() {
return "index";
}
.anyRequest().authenticated()
.and()
.formLogin()
- .loginPage(Constants.NAV_LOGIN).defaultSuccessUrl(Constants.NAV_UPLOAD_FILES)
+ .loginPage(Constants.NAV_LOGIN).defaultSuccessUrl(Constants.NAV_IMPORT_FILE)
.and()
.logout().logoutUrl(Constants.NAV_LOGOUT).logoutSuccessUrl(Constants.NAV_LOGIN)
.and()
--- /dev/null
+package hu.user.mcvodsync.ui.data;
+
+import com.brightcove.cms.client.model.Playlist;
+import hu.user.mcvodsync.brightcove.BrightCoveClient;
+import hu.user.mcvodsync.ui.data.common.CachedSpringDataModel;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.zkoss.zul.FieldComparator;
+
+import java.util.List;
+
+@Component
+@Log4j2
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class BrightCovePlaylistDataModel extends CachedSpringDataModel<Playlist> {
+ @Autowired
+ private BrightCoveClient brightCoveClient;
+
+ @Override
+ public List<Playlist> getResultSet(int page, int pageSize, FieldComparator sortComparator) {
+ return brightCoveClient.getPlaylists(page * pageSize, pageSize, "description:mc-vod-sync");
+ }
+
+ @Override
+ public int getResultSetCount() {
+ return brightCoveClient.getPlaylistCount("description:mc-vod-sync");
+ }
+
+ public void refresh() {
+
+ super.reset();
+ }
+}
--- /dev/null
+package hu.user.mcvodsync.ui.data;
+
+import com.brightcove.cms.client.model.Playlist;
+import com.brightcove.cms.client.model.Video;
+import com.google.common.collect.Lists;
+import hu.user.mcvodsync.brightcove.BrightCoveClient;
+import hu.user.mcvodsync.ui.data.common.CachedSpringDataModel;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.zkoss.zul.FieldComparator;
+
+import java.util.List;
+import java.util.Objects;
+
+@Component
+@Log4j2
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class BrightCoveVideoDataModel extends CachedSpringDataModel<Video> {
+ @Autowired
+ private BrightCoveClient brightCoveClient;
+ private Playlist playlist;
+
+ @Override
+ public List<Video> getResultSet(int page, int pageSize, FieldComparator sortComparator) {
+ if (Objects.isNull(playlist) || Objects.isNull(playlist.getVideoIds())) {
+ return null;
+ }
+ List<List<String>> partitions = Lists.partition(playlist.getVideoIds(), pageSize);
+ String query = StringUtils.join(partitions.get(page), ",");
+ return brightCoveClient.getVideos(pageSize * page, pageSize, query);
+ }
+
+ @Override
+ public int getResultSetCount() {
+ if (Objects.isNull(playlist) || Objects.isNull(playlist.getVideoIds())) {
+ return 0;
+ }
+ return playlist.getVideoIds().size();
+ }
+
+ public void refresh(Playlist playlist) {
+ this.playlist = playlist;
+ super.reset();
+ }
+}
@Component
@Log4j2
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class UploadFileDataModel extends CachedSpringDataModel<UploadFile> {
+public class ImportFileDataModel extends CachedSpringDataModel<UploadFile> {
@Autowired
UploadFileRepository uploadFileRepository;
--- /dev/null
+package hu.user.mcvodsync.ui.view;
+
+import com.brightcove.cms.client.model.Playlist;
+import hu.user.mcvodsync.ui.Constants;
+import hu.user.mcvodsync.ui.data.BrightCovePlaylistDataModel;
+import hu.user.mcvodsync.ui.data.BrightCoveVideoDataModel;
+import lombok.Getter;
+import lombok.extern.log4j.Log4j2;
+import org.zkoss.bind.annotation.Command;
+import org.zkoss.bind.annotation.Init;
+import org.zkoss.zk.ui.select.annotation.VariableResolver;
+import org.zkoss.zk.ui.select.annotation.WireVariable;
+import org.zkoss.zk.ui.util.Clients;
+import org.zkoss.zkplus.spring.DelegatingVariableResolver;
+
+@Getter
+@Log4j2
+@VariableResolver(DelegatingVariableResolver.class)
+public class BrowseRemoteViewModel {
+
+ @WireVariable
+ private BrightCovePlaylistDataModel brightCovePlaylistDataModel;
+
+ @WireVariable
+ private BrightCoveVideoDataModel brightCoveVideoDataModel;
+
+ private Playlist selectedPlaylist;
+
+ @Init
+ public void init() {
+ Clients.evalJavaScript(String.format("pushNav('%s')", Constants.NAV_BROWSE_REMOTE));
+ }
+
+ @Command
+ public void onRefresh() {
+ log.info("Refresh");
+ brightCovePlaylistDataModel.refresh();
+ brightCoveVideoDataModel.refresh(null);
+ }
+
+ public void setSelectedPlaylist(Playlist selectedPlaylist) {
+ this.selectedPlaylist = selectedPlaylist;
+ log.info("Selected {}", selectedPlaylist);
+ brightCoveVideoDataModel.refresh(selectedPlaylist);
+ }
+}
--- /dev/null
+package hu.user.mcvodsync.ui.view;
+
+import hu.user.mcvodsync.service.config.ScheduledTasks;
+import hu.user.mcvodsync.service.event.ExportCompletedEvent;
+import hu.user.mcvodsync.ui.Constants;
+import lombok.Getter;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.context.ApplicationListener;
+import org.zkoss.bind.annotation.Command;
+import org.zkoss.bind.annotation.Init;
+import org.zkoss.zk.ui.select.annotation.VariableResolver;
+import org.zkoss.zk.ui.select.annotation.WireVariable;
+import org.zkoss.zk.ui.util.Clients;
+import org.zkoss.zkplus.spring.DelegatingVariableResolver;
+
+@Getter
+@Log4j2
+@VariableResolver(DelegatingVariableResolver.class)
+public class ExportDataViewModel implements ApplicationListener<ExportCompletedEvent> {
+
+ @WireVariable
+ private ScheduledTasks scheduledTasks;
+
+ @Init
+ public void init() {
+ Clients.evalJavaScript(String.format("pushNav('%s')", Constants.NAV_EXPORT_DATA));
+ onRefresh();
+ }
+
+ @Command
+ public void onRefresh() {
+ log.info("Refresh");
+ }
+
+ @Command
+ public void onStartExport() {
+ log.info("Starting export");
+ scheduledTasks.startExport();
+ }
+
+ @Override
+ public void onApplicationEvent(ExportCompletedEvent event) {
+ log.info("Export completed");
+ }
+}
--- /dev/null
+package hu.user.mcvodsync.ui.view;
+
+import hu.user.mcvodsync.service.event.ImportCompletedEvent;
+import hu.user.mcvodsync.service.event.ImportStartedEvent;
+import lombok.Setter;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+@Log4j2
+@Component
+public class ImportEventHandler {
+
+ @Setter
+ private Consumer<ImportStartedEvent> startedConsumer;
+
+ @Setter
+ private Consumer<ImportCompletedEvent> completedConsumer;
+
+ @Async
+ @EventListener
+ public void handleEvent(ImportStartedEvent evt) {
+ log.info("ImportStartedEvent handle");
+ Optional.ofNullable(startedConsumer).ifPresent(c -> c.accept(evt));
+ }
+
+ @Async
+ @EventListener
+ public void handleEvent(ImportCompletedEvent evt) {
+ log.info("ImportCompletedEvent handle");
+ Optional.ofNullable(completedConsumer).ifPresent(c -> c.accept(evt));
+ }
+}
import com.google.common.collect.ImmutableMap;
import hu.user.mcvodsync.db.UploadFile;
+import hu.user.mcvodsync.service.config.ScheduledTasks;
import hu.user.mcvodsync.ui.Constants;
-import hu.user.mcvodsync.ui.data.UploadFileDataModel;
+import hu.user.mcvodsync.ui.data.ImportFileDataModel;
import hu.user.mcvodsync.ui.data.common.CachedSpringDataModel;
import hu.user.mcvodsync.ui.view.common.FilterActiveViewModel;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import org.zkoss.bind.BindContext;
+import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ContextParam;
import org.zkoss.bind.annotation.ContextType;
@Getter
@Log4j2
-public class UploadFilesViewModel extends FilterActiveViewModel<UploadFile> {
+public class ImportFileViewModel extends FilterActiveViewModel<UploadFile> {
+
+ @WireVariable
+ ImportFileDataModel importFileDataModel;
+
+ @WireVariable
+ private ScheduledTasks scheduledTasks;
+
@WireVariable
- UploadFileDataModel uploadFileDataModel;
+ private ImportEventHandler importEventHandler;
@Override
protected CachedSpringDataModel<UploadFile> getDataModel() {
- return uploadFileDataModel;
+ return importFileDataModel;
}
@Override
protected String getNavigation() {
- return Constants.NAV_UPLOAD_FILES;
+ return Constants.NAV_IMPORT_FILE;
}
@Init
"name", NATURAL,
"size", NATURAL
));
+
+ }
+
+ @Command
+ public void onTimer() {
+ BindUtils.postNotifyChange(scheduledTasks, "importSummary");
}
protected void refresh() {
- uploadFileDataModel.refresh();
+ importFileDataModel.refresh();
}
@Command
UploadEvent evt = (UploadEvent) ctx.getTriggerEvent();
String lowerCaseFileName = evt.getMedia().getName().toLowerCase();
if (!lowerCaseFileName.endsWith(".xls") && !lowerCaseFileName.endsWith(".xlsx")) {
- Messagebox.show("Excel file upload is expected!", "Error", Messagebox.OK, Messagebox.ERROR);
+ Messagebox.show("Excel file is expected!", "Error", Messagebox.OK, Messagebox.ERROR);
return;
}
UploadFile uploadFile = UploadFile.builder()
.created(Date.from(Instant.now()))
.build();
- uploadFileDataModel.save(uploadFile);
- uploadFileDataModel.refresh();
+ importFileDataModel.save(uploadFile);
+ importFileDataModel.refresh();
+ scheduledTasks.startImport();
}
}
package hu.user.mcvodsync.ui.view;
+import com.brightcove.cms.client.model.Playlist;
+import com.brightcove.cms.client.model.Video;
+import hu.user.mcvodsync.brightcove.BrightCoveClient;
+import hu.user.mcvodsync.brightcove.PagedSearch;
+import hu.user.mcvodsync.db.repository.AssetRepository;
+import hu.user.mcvodsync.db.repository.AssetSyncRepository;
+import hu.user.mcvodsync.db.repository.PlaylistSyncRepository;
+import hu.user.mcvodsync.db.repository.UploadFileRepository;
import hu.user.mcvodsync.ui.Constants;
import hu.user.mcvodsync.ui.auth.CurrentProfile;
import hu.user.mcvodsync.ui.session.SessionSettings;
import org.zkoss.bind.annotation.Init;
import org.zkoss.zk.ui.select.annotation.VariableResolver;
import org.zkoss.zk.ui.select.annotation.WireVariable;
+import org.zkoss.zul.Messagebox;
@Log4j2
public class IndexViewModel {
@WireVariable
- BuildProperties buildProperties;
+ private BuildProperties buildProperties;
@WireVariable
- SessionSettings sessionSettings;
+ private SessionSettings sessionSettings;
@WireVariable
- CurrentProfile currentProfile;
+ private CurrentProfile currentProfile;
- String page;
+ @WireVariable
+ private AssetSyncRepository assetSyncRepository;
+
+ @WireVariable
+ private PlaylistSyncRepository playlistSyncRepository;
+
+ @WireVariable
+ private BrightCoveClient brightCoveClient;
+
+ @WireVariable
+ private AssetRepository assetRepository;
+
+ @WireVariable
+ private UploadFileRepository uploadFileRepository;
+
+ private String page;
@Init
public void init() {
log.info("Init {}", path);
}
- private void route(String path) {
+ @Command
+ public void route(String path) {
if (Constants.NAV_ROOT.equals(path))
- path = Constants.NAV_UPLOAD_FILES;
+ path = Constants.NAV_IMPORT_FILE;
setPage("~." + path + ".zul");
+ BindUtils.postNotifyChange(this, "page");
}
BindUtils.postNotifyChange(this, "page");
}
+ @Command
+ public void onReset() {
+ cleanupLocal();
+ cleanupRemote();
+ Messagebox.show("Cleanup completed.", "Info", Messagebox.OK, Messagebox.INFORMATION);
+ }
+
+ private void cleanupLocal() {
+ log.info("cleanupLocal");
+ playlistSyncRepository.deleteAllInBatch();
+ assetSyncRepository.deleteAllInBatch();
+ assetRepository.deleteAllInBatch();
+ uploadFileRepository.deleteAllInBatch();
+ log.info("cleanupLocal OK");
+ }
+
+ protected void cleanupRemote() {
+ log.info("cleanupRemote");
+ cleanupRemotePlaylists();
+ cleanupRemoteVideos();
+ log.info("cleanupRemote OK");
+ }
+
+ private void cleanupRemotePlaylists() {
+ PagedSearch<Playlist> page = PagedSearch.<Playlist>builder()
+ .query("description:mc-vod-sync")
+ .sort("name")
+ .build();
+ while (brightCoveClient.getPlaylistsPaged(page)) {
+ page.getData().forEach(this::deletePlayList);
+ }
+ }
+
+ private void cleanupRemoteVideos() {
+ PagedSearch<Video> page = PagedSearch.<Video>builder()
+ .query("tags:mc-vod-sync")
+ .sort("reference_id")
+ .build();
+ while (brightCoveClient.getVideosPaged(page)) {
+ page.getData().forEach(this::deleteVideo);
+ }
+
+ }
+
+ private void deletePlayList(Playlist playlist) {
+ try {
+ brightCoveClient.deletePlaylist(playlist.getId());
+ } catch (Exception e) {
+ log.error(e);
+ }
+ }
+
+ private void deleteVideo(Video video) {
+ try {
+ brightCoveClient.deleteVideo(video.getId());
+ } catch (Exception e) {
+ log.error(e);
+ }
+ }
+
}
<name>org.zkoss.zul.nativebar</name>
<value>true</value>
</library-property>
+ <language-addon>
+ <component>
+ <component-name>menuitem</component-name>
+ <extends>menuitem</extends>
+ <property>
+ <property-name>autodisable</property-name>
+ <property-value>self</property-value>
+ </property>
+ </component>
+ </language-addon>
<!-- PROD -->
<!-- <desktop-config>-->
<!-- <file-check-period>600</file-check-period><!– unit: seconds –>-->
--- /dev/null
+<zk>
+ <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.BrowseRemoteViewModel')">
+ <caption label="Browse BrightCove"/>
+ <borderlayout>
+ <north hflex="true">
+ <toolbar>
+ <toolbarbutton label="Reload" iconSclass="z-icon-retweet" onClick="@command('onRefresh')"/>
+ </toolbar>
+ </north>
+ <center border="none" hflex="true" vflex="true">
+
+ <hbox spacing="0" hflex="true" vflex="true">
+ <listbox hflex="true" vflex="true" model="@load(vm.brightCovePlaylistDataModel)"
+ autopaging="true" mold="paging" pagingPosition="top" multiple="false"
+ selectedItem="@bind(vm.selectedPlaylist)">
+ <listhead sizable="true">
+ <listheader label="ID" align="left"/>
+ <listheader label="Name" align="left"/>
+ <listheader label="Video count" align="left"/>
+ </listhead>
+ <template name="model">
+ <listitem>
+ <listcell label="@load(each.id)"/>
+ <listcell label="@load(each.name)"/>
+ <listcell label="@load(each.getVideoIds().size())"/>
+ </listitem>
+ </template>
+ </listbox>
+ <splitter collapse="before"/>
+ <listbox hflex="true" vflex="true" model="@load(vm.brightCoveVideoDataModel)"
+ autopaging="true" mold="paging" pagingPosition="top" multiple="false">
+ <listhead sizable="true">
+ <listheader label="ID" align="left"/>
+ <listheader label="Catalog ID" align="left"/>
+ </listhead>
+ <template name="model">
+ <listitem>
+ <listcell label="@load(each.id)"/>
+ <listcell label="@load(each.referenceId)"/>
+ </listitem>
+ </template>
+ </listbox>
+ </hbox>
+
+ </center>
+ </borderlayout>
+ </window>
+</zk>
\ No newline at end of file
--- /dev/null
+<zk>
+ <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.ExportDataViewModel')">
+ <caption label="Export data"/>
+ <borderlayout>
+ <north hflex="true">
+ <toolbar>
+ <toolbarbutton label="Synchronize" iconSclass="z-icon-refresh" onClick="@command('onStartExport')"/>
+ </toolbar>
+ </north>
+ <center border="none" flex="true">
+ </center>
+ </borderlayout>
+ </window>
+</zk>
\ No newline at end of file
--- /dev/null
+<zk>
+ <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.ImportFileViewModel')">
+ <timer delay="2000" onTimer="@command('onTimer')" repeats="true"/>
+ <caption label="Import file"/>
+ <borderlayout>
+ <north hflex="true">
+ <toolbar>
+ <toolbarbutton label="Upload" iconSclass="z-icon-plus" upload="true"
+ onUpload="@command('onUploadFile')"/>
+ <!-- <toolbarbutton label="Törlés" iconSclass="z-icon-remove" onClick="@command('onDelete')"-->
+ <!-- disabled="@load(empty vm.selectedEntity)"/>-->
+ </toolbar>
+ </north>
+ <center border="none" hflex="true" vflex="true">
+
+ <hbox spacing="0" hflex="true" vflex="true">
+ <listbox id="partnersList" hflex="2" vflex="true" model="@load(vm.importFileDataModel)"
+ autopaging="true" mold="paging" pagingPosition="top" multiple="false"
+ onSelect="@command('onListSelection')" onDoubleClick="@command('onEdit')">
+ <listhead sizable="true">
+ <listheader label="Created" sort="auto(created)" align="left"
+ sortDirection="@load(vm.cols['created'].sortDirection)"/>
+ <listheader label="Name" sort="auto(name)" align="left"
+ sortDirection="@load(vm.cols['name'].sortDirection)"/>
+ <listheader label="Size" sort="auto(size)" align="left"
+ sortDirection="@load(vm.cols['size'].sortDirection)"/>
+ </listhead>
+ <template name="model">
+ <listitem>
+ <listcell label="@load(each.created)"/>
+ <listcell label="@load(each.name)"/>
+ <listcell label="@load(each.size)"/>
+ </listitem>
+ </template>
+ </listbox>
+ <splitter collapse="before"/>
+ <window title="Last import statistics" hflex="true" vflex="true">
+ <vlayout>
+ <grid span="true">
+ <columns>
+ <column hflex="min"/>
+ <column hflex="1"/>
+ <column hflex="min"/>
+ <column hflex="1"/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="left" valign="bottom">
+ <label value="Started:"/>
+ </cell>
+ <cell align="center" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.started)"
+ sclass="hightlight"/>
+ </cell>
+ <cell align="left" valign="bottom">
+ <label value="Finished:"/>
+ </cell>
+ <cell align="center" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.finished)"
+ sclass="hightlight"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+ <grid span="true">
+ <columns>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ <column/>
+ </columns>
+ <rows>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="All:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.all)"
+ sclass="hightlight"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Success:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.success)"
+ sclass="hightlight"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Error:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.error)"
+ sclass="hightlight"/>
+ </cell>
+ </row>
+ <row>
+ <cell align="right" valign="bottom">
+ <label value="Inserted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.inserted)"
+ sclass="hightlight"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Updated:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.updated)"
+ sclass="hightlight"/>
+ </cell>
+ <cell align="right" valign="bottom">
+ <label value="Deleted:"/>
+ </cell>
+ <cell align="left" valign="middle">
+ <label value="@load(vm.scheduledTasks.importSummary.deleted)"
+ sclass="hightlight"/>
+ </cell>
+ </row>
+ </rows>
+ </grid>
+
+ </vlayout>
+ </window>
+ </hbox>
+
+ </center>
+ </borderlayout>
+ </window>
+</zk>
\ No newline at end of file
<div style="display: block">
<div style="display: inline; float: left">
<hlayout valign="middle">
- <image width="24px" height="24px" src="~./static/images/logo.png"/>
+ <!-- <image width="24px" height="24px" src="~./static/images/logo.png"/>-->
<label value="${Constants.APPLICATION_NAME}"/>
<separator orient="vertical"/>
<label style="font-size: 0.8em" value="@load(vm.buildProperties.version)"/>
<vlayout>
<hlayout valign="middle">
<menubar autodrop="true" hflex="true">
- <menuitem iconSclass="z-icon-group" label="Partnerek"
- onClick="@command(vm.selectPage('~./partners.zul'))"/>
- <menuseparator/>
- <menuitem iconSclass="z-icon-user" label="Munkatársak"
- onClick="@command(vm.selectPage('~./associates.zul'))"/>
+ <menuitem iconSclass="z-icon-cloud-download" label="Import file"
+ onClick="@command(vm.route(Constants.NAV_IMPORT_FILE))"/>
+ <menuitem iconSclass="z-icon-cloud-upload" label="Export data"
+ onClick="@command(vm.route(Constants.NAV_EXPORT_DATA))"/>
+ <menuitem image="~./static/images/bc.ico" label="Browse BrightCove"
+ onClick="@command(vm.route(Constants.NAV_BROWSE_REMOTE))"/>
<menuseparator/>
+ <menuitem iconSclass="z-icon-user" label="Users"
+ onClick="@command(vm.route(Constants.NAV_ASSOCIATES))"/>
+ <menuitem iconSclass="z-icon-trash" label="Reset data"
+ onClick="@command('onReset')"/>
</menubar>
<hbox hflex="min" pack="right">
</hbox>
<center border="none" hflex="true">
<include src="@load(vm.page)" hflex="true" vflex="true"/>
</center>
+ <south height="50px">
+ <hlayout>
+ <div width="50px" height="50px" style="background: blue"></div>
+ </hlayout>
+ </south>
</borderlayout>
</window>
</zk>
+++ /dev/null
-<zk>
- <window vflex="true" viewModel="@id('vm') @init('hu.user.mcvodsync.ui.view.UploadFilesViewModel')">
- <caption label="Files"/>
- <borderlayout>
- <north flex="true">
- <toolbar>
- <toolbarbutton label="Upload" iconSclass="z-icon-plus" upload="true"
- onUpload="@command('onUploadFile')"/>
- <toolbarbutton label="Törlés" iconSclass="z-icon-remove" onClick="@command('onDelete')"
- disabled="@load(empty vm.selectedEntity)"/>
- </toolbar>
- </north>
- <center border="none" flex="true">
- <listbox id="partnersList" vflex="true" model="@load(vm.uploadFileDataModel)"
- autopaging="true" mold="paging" pagingPosition="top" multiple="false"
- onSelect="@command('onListSelection')" onDoubleClick="@command('onEdit')">
- <listhead sizable="true">
- <listheader label="Created" sort="auto(created)" align="left"
- sortDirection="@load(vm.cols['created'].sortDirection)"/>
- <listheader label="Name" sort="auto(name)" align="left"
- sortDirection="@load(vm.cols['name'].sortDirection)"/>
- <listheader label="Size" sort="auto(size)" align="left"
- sortDirection="@load(vm.cols['size'].sortDirection)"/>
- </listhead>
- <template name="model">
- <listitem>
- <listcell label="@load(each.created)"/>
- <listcell label="@load(each.name)"/>
- <listcell label="@load(each.size)"/>
- </listitem>
- </template>
- </listbox>
- </center>
- </borderlayout>
- </window>
-</zk>
\ No newline at end of file