Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
nl.vpro.api.client.utils.MediaRestClientUtils Maven / Gradle / Ivy
package nl.vpro.api.client.utils;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.time.Instant;
import java.util.*;
import java.util.function.Supplier;
import javax.validation.constraints.NotNull;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import org.apache.commons.io.IOUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import com.google.common.collect.Lists;
import nl.vpro.api.client.frontend.NpoApiClients;
import nl.vpro.api.rs.v3.media.MediaRestService;
import nl.vpro.api.rs.v3.subtitles.SubtitlesRestService;
import nl.vpro.domain.api.*;
import nl.vpro.domain.api.media.*;
import nl.vpro.domain.media.*;
import nl.vpro.domain.subtitles.Subtitles;
import nl.vpro.domain.subtitles.SubtitlesId;
import nl.vpro.jackson2.JsonArrayIterator;
import nl.vpro.logging.simple.Level;
import nl.vpro.poms.shared.Headers;
import nl.vpro.util.*;
import static nl.vpro.api.client.utils.ChangesFeedParameters.changesParameters;
import static nl.vpro.logging.simple.Slf4jSimpleLogger.slf4j;
/**
* @author Michiel Meeuwissen
* @since 1.1
*/
@Slf4j
public class MediaRestClientUtils {
/**
* Similar to the v1 call for easier migration
*/
public static MediaForm getRecentPlayablePrograms(AVType avType) {
MediaFormBuilder formBuilder = MediaFormBuilder.form();
if (avType != null) {
formBuilder.avTypes(Match.MUST, avType);
}
List excludedTypes = new ArrayList<>();
for (GroupType type : GroupType.values()) {
excludedTypes.add(type.getMediaType());
}
excludedTypes.add(ProgramType.TRACK.getMediaType());
excludedTypes.add(SegmentType.SEGMENT.getMediaType());
MediaForm form = formBuilder.types(Match.NOT, excludedTypes.toArray(new MediaType[0])).build();
form.addSortField(new MediaSortOrder(MediaSortField.sortDate, Order.DESC));
return form;
}
public static List adapt(final MediaSearchResult result) {
return new AbstractList<>() {
@Override
public MediaObject get(int index) {
SearchResultItem extends MediaObject> object = result.getItems().get(index);
return object.getResult();
}
@Override
public int size() {
return result.getSize();
}
};
}
public static MediaObject loadOrNull(MediaRestService restService, String id) {
/*return wrapForOrNull(
() -> restService.load(id, null, null),
() -> id
);*/
return restService.loadMultiple(new IdList(id), null, null).getItems().get(0).getResult();
}
public static Subtitles loadOrNull(SubtitlesRestService restService, String mid, Locale language) throws IOException {
return wrapForOrNull(
() -> restService.get(mid, language),
() -> SubtitlesId.builder().mid(mid).language(language).build().toString()
);
}
/**
* Converts some exceptions to 'null', most noticably {@link NotFoundException}
*
* @param supplier The action to perform
* @param id Id to use for logging if exceptions happen
*/
private static T wrapForOrNull(
Supplier supplier,
Supplier id
) throws IOException {
try {
return supplier.get();
} catch (NotFoundException nfe) {
// not even log, this is not errorneous
return null;
} catch (ProcessingException pe) {
unwrapIO(pe);
log.warn(id.get() + " " + pe.getMessage());
return null;
} catch (RuntimeException ise) {
// Completely unexpected, this should remain to be an exception!
throw ise;
} catch (Exception e) {
log.error(id.get() + " " + e.getClass().getName() + " " + e.getMessage());
return null;
}
}
public static void unwrapIO(ProcessingException pe) throws IOException {
Throwable t = pe.getCause();
if (t instanceof IOException) {
throw (IOException) t;
}
}
public static MediaObject[] load(MediaRestService restService, String... ids) {
return loadWithMultiple(restService, ids);
//loadWithSearch(restService, ids); // doesn't preserve order/duplicates, probable slower too.
}
private static MediaObject[] loadWithMultiple(MediaRestService restService, String... ids) {
List result = new ArrayList<>(ids.length);
if (ids.length > 0) {
for (List idList : Lists.partition(Arrays.asList(ids), 240)) {
MultipleMediaResult mediaResult = restService.loadMultiple(new IdList(idList), null, null);
result.addAll(Lists.transform(mediaResult.getItems(), MultipleEntry::getResult));
}
}
return result.toArray(new MediaObject[0]);
}
// unused for now
private static MediaObject[] loadWithSearch(MediaRestService restService, String... ids) {
List result = new ArrayList<>(ids.length);
/*
* Calling restService.find with max > Constants.MAX_RESULTS gets you a BadRequestException,
* so the list of ids is partitioned first and if needed multiple find calls are executed...
*/
for (List idList : Lists.partition(Arrays.asList(ids), Constants.MAX_RESULTS)) {
String[] partitionedIds = idList.toArray(new String[0]);
MediaForm mediaForm = MediaFormBuilder.form().mediaIds(partitionedIds).build();
MediaSearchResult mediaSearchResult = restService.find(mediaForm, null, null, 0L, idList.size());
result.addAll(adapt(mediaSearchResult));
}
return result.toArray(new MediaObject[0]);
}
@Deprecated
public static JsonArrayIterator changes(MediaRestService restService, String profile, long since, Order order, Integer max) throws IOException {
try {
final InputStream inputStream = toInputStream(restService.changes(profile, null, since, null, order, max, null, Tail.ALWAYS, null));
return new JsonArrayIterator<>(
inputStream,
MediaChange.class,
() -> IOUtils.closeQuietly(inputStream)
);
} catch (ProcessingException pi) {
Throwable t = pi.getCause();
throw new RuntimeException(t.getMessage(), t);
}
}
public static JsonArrayIterator changes(
@NotNull MediaRestService restService,
@Nullable String profile,
@NonNull Instant since,
@Nullable String mid,
@Nullable Order order,
@Nullable Integer max,
@Nullable Deletes deletes,
@Nullable Tail tail) throws IOException {
return changes(restService,
changesParameters()
.profile(profile)
.mediaSince(MediaSince.of(since, mid))
.order(order)
.max(max)
.deletes(deletes)
.tail(tail)
.build()
);
}
public static JsonArrayIterator changes(
@NotNull MediaRestService restService,
ChangesFeedParameters parameters
) throws IOException {
try {
final Response response = restService.changes(
parameters.getProfile(), null, null,
MediaSince.asQueryParam(parameters.getMediaSince()),
parameters.getOrder(),
parameters.getMax(),
parameters.getDeletes(),
parameters.getTail(),
parameters.getReasonFilter());
final InputStream inputStream = toInputStream(response);
return new JsonArrayIterator<>(
inputStream,
MediaChange.class,
() -> closeQuietly(inputStream, response)
);
} catch (ProcessingException pi) {
Throwable t = pi.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof IOException) {
throw (IOException) t;
} else {
throw new RuntimeException(t.getMessage(), t);
}
}
}
public static JsonArrayIterator changes(MediaRestService restService, String profile, Instant since, String mid, Order order, Integer max, Deletes deletes) throws IOException {
return changes(restService, profile, since, mid, order, max, deletes, Tail.IF_EMPTY);
}
/**
* @deprecated use {@code MediaSince#of(since, mid).asQueryParam()}
*/
@Deprecated
public static String sinceString(Instant since, String mid) {
String sinceString = since == null ? null : since.toString();
if (mid != null && sinceString != null) {
sinceString += "," + mid;
}
return sinceString;
}
public static CountedIterator iterate(MediaRestService restService, MediaForm form, String profile) {
return iterate(restService, form, profile, true);
}
public static CountedIterator iterate(MediaRestService restService, MediaForm form, String profile , boolean progressLogging) {
return iterate(restService, form, profile, progressLogging, Integer.MAX_VALUE);
}
/**
*
*/
public static CountedIterator iterate(MediaRestService restService, MediaForm form, String profile , boolean progressLogging, int max) {
return iterate(() -> restService.iterate(form, profile, null, 0L, max), progressLogging, "iterate-" + profile + "-");
}
public static CountedIterator iterate(Supplier response, boolean progressLogging, String filePrefix) {
return new LazyIterator<>(() -> {
try {
final Response res = response.get();
final InputStream inputStream = toInputStream(res);
// Cache the stream to a file first.
// If we don't do this, the stream seems to be inadvertedly truncated sometimes if the client doesn't consume the iterator fast enough.
final FileCachingInputStream cacheToFile = FileCachingInputStream.builder()
.filePrefix(filePrefix)
.batchSize(1000000L)
.logger(log)
.progressLogging(progressLogging)
.input(inputStream)
.build();
return JsonArrayIterator.builder()
.inputStream(cacheToFile)
.valueClass(MediaObject.class)
.callback(() -> closeQuietly(inputStream, cacheToFile, res))
.logger(log)
.build();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
public static void closeQuietly(AutoCloseable... closeables) {
for (AutoCloseable c : closeables) {
if (c != null) {
try {
c.close();
} catch (Throwable e) {
log.info(e.getMessage());
}
}
}
}
/**
* Converts response to inputstream
*/
public static InputStream toInputStream(Response response) {
// I would rather use some utility to map response status codes to proper exceptions, but I can't find one.
// This seems incomplete now
try {
if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
InputStream inputStream = response.readEntity(InputStream.class);
return inputStream;
} else if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
throw new NotFoundException(response);
} else if (response.getStatusInfo().getFamily() == Response.Status.Family.CLIENT_ERROR) {
throw new BadRequestException(response);
} else if (response.getStatusInfo().getFamily() == Response.Status.Family.SERVER_ERROR) {
throw new ServerErrorException(response);
} else {
throw new RuntimeException(response.readEntity(String.class));
}
} finally {
NpoApiClients.dealWithHeaders(slf4j(log), s -> s.equalsIgnoreCase(Headers.NPO_WARNING_HEADER) ? Level.WARN : Level.DEBUG);
}
}
}