Search in sources :

Example 1 with PhotosLibraryClient

use of com.google.photos.library.v1.PhotosLibraryClient in project data-transfer-project by google.

the class GoogleVideosImporterTest method importTwoVideos.

@Test
public void importTwoVideos() throws Exception {
    PhotosLibraryClient photosLibraryClient = mock(PhotosLibraryClient.class);
    // Mock uploads
    when(photosLibraryClient.uploadMediaItem(any())).thenReturn(UploadMediaItemResponse.newBuilder().setUploadToken("token1").build(), UploadMediaItemResponse.newBuilder().setUploadToken("token2").build());
    // Mock creation response
    final NewMediaItemResult newMediaItemResult = NewMediaItemResult.newBuilder().setStatus(Status.newBuilder().setCode(Code.OK_VALUE).build()).setMediaItem(MediaItem.newBuilder().setId("RESULT_ID_1").build()).setUploadToken("token1").build();
    final NewMediaItemResult newMediaItemResult2 = NewMediaItemResult.newBuilder().setStatus(Status.newBuilder().setCode(Code.OK_VALUE).build()).setMediaItem(MediaItem.newBuilder().setId("RESULT_ID_2").build()).setUploadToken("token2").build();
    BatchCreateMediaItemsResponse response = BatchCreateMediaItemsResponse.newBuilder().addNewMediaItemResults(newMediaItemResult).addNewMediaItemResults(newMediaItemResult2).build();
    when(photosLibraryClient.batchCreateMediaItems(ArgumentMatchers.anyList())).thenReturn(response);
    InMemoryIdempotentImportExecutor executor = new InMemoryIdempotentImportExecutor(mock(Monitor.class));
    long length = googleVideosImporter.importVideoBatch(Lists.newArrayList(new VideoModel(VIDEO_TITLE, VIDEO_URI, VIDEO_DESCRIPTION, MP4_MEDIA_TYPE, VIDEO_ID, null, false), new VideoModel(VIDEO_TITLE, VIDEO_URI, VIDEO_DESCRIPTION, MP4_MEDIA_TYPE, "myId2", null, false)), photosLibraryClient, executor);
    assertEquals("Expected the number of bytes to be the two files of 32L.", 64L, length);
    assertEquals("Expected executor to have no errors.", 0, executor.getErrors().size());
}
Also used : Monitor(org.datatransferproject.api.launcher.Monitor) NewMediaItemResult(com.google.photos.library.v1.proto.NewMediaItemResult) BatchCreateMediaItemsResponse(com.google.photos.library.v1.proto.BatchCreateMediaItemsResponse) InMemoryIdempotentImportExecutor(org.datatransferproject.spi.transfer.idempotentexecutor.InMemoryIdempotentImportExecutor) PhotosLibraryClient(com.google.photos.library.v1.PhotosLibraryClient) VideoModel(org.datatransferproject.types.common.models.videos.VideoModel) Test(org.junit.Test)

Example 2 with PhotosLibraryClient

use of com.google.photos.library.v1.PhotosLibraryClient in project data-transfer-project by google.

the class GoogleVideosImporterTest method skipNotFoundVideo.

@Test
public void skipNotFoundVideo() throws Exception {
    PhotosLibraryClient photosLibraryClient = mock(PhotosLibraryClient.class);
    HttpURLConnection httpURLConnection = mock(HttpURLConnection.class);
    when(httpURLConnection.getInputStream()).thenThrow(new FileNotFoundException());
    when(streamProvider.getConnection(any())).thenReturn(httpURLConnection);
    InMemoryIdempotentImportExecutor executor = new InMemoryIdempotentImportExecutor(mock(Monitor.class));
    long length = googleVideosImporter.importVideoBatch(Lists.newArrayList(new VideoModel(VIDEO_TITLE, VIDEO_URI, VIDEO_DESCRIPTION, MP4_MEDIA_TYPE, VIDEO_ID, null, false)), photosLibraryClient, executor);
    assertEquals("Expected the number of bytes to be 0L.", 0L, length);
    assertEquals("Expected executor to have no errors.", 0, executor.getErrors().size());
}
Also used : HttpURLConnection(java.net.HttpURLConnection) Monitor(org.datatransferproject.api.launcher.Monitor) FileNotFoundException(java.io.FileNotFoundException) InMemoryIdempotentImportExecutor(org.datatransferproject.spi.transfer.idempotentexecutor.InMemoryIdempotentImportExecutor) PhotosLibraryClient(com.google.photos.library.v1.PhotosLibraryClient) VideoModel(org.datatransferproject.types.common.models.videos.VideoModel) Test(org.junit.Test)

Example 3 with PhotosLibraryClient

use of com.google.photos.library.v1.PhotosLibraryClient in project data-transfer-project by google.

the class GoogleVideosImporter method importItem.

@Override
public ImportResult importItem(UUID jobId, IdempotentImportExecutor executor, TokensAndUrlAuthData authData, VideosContainerResource data) throws Exception {
    if (data == null) {
        // Nothing to do
        return ImportResult.OK;
    }
    PhotosLibraryClient client;
    if (clientsMap.containsKey(jobId)) {
        client = clientsMap.get(jobId);
    } else {
        PhotosLibrarySettings settings = PhotosLibrarySettings.newBuilder().setCredentialsProvider(FixedCredentialsProvider.create(UserCredentials.newBuilder().setClientId(appCredentials.getKey()).setClientSecret(appCredentials.getSecret()).setAccessToken(new AccessToken(authData.getAccessToken(), new Date())).setRefreshToken(authData.getRefreshToken()).build())).build();
        client = PhotosLibraryClient.initialize(settings);
        clientsMap.put(jobId, client);
    }
    long bytes = 0L;
    // Uploads videos
    final Collection<VideoModel> videos = data.getVideos();
    if (videos != null && videos.size() > 0) {
        Stream<VideoModel> stream = videos.stream().filter(video -> shouldImport(video, executor)).map(this::transformVideoName);
        // We partition into groups of 49 as 50 is the maximum number of items that can be created in
        // one call. (We use 49 to avoid potential off by one errors)
        // https://developers.google.com/photos/library/guides/upload-media#creating-media-item
        final UnmodifiableIterator<List<VideoModel>> batches = Iterators.partition(stream.iterator(), 49);
        while (batches.hasNext()) {
            long batchBytes = importVideoBatch(batches.next(), client, executor);
            bytes += batchBytes;
        }
    }
    final ImportResult result = ImportResult.OK;
    return result.copyWithBytes(bytes);
}
Also used : RandomAccessFile(java.io.RandomAccessFile) DestinationMemoryFullException(org.datatransferproject.spi.transfer.types.DestinationMemoryFullException) Error(com.google.photos.library.v1.upload.UploadMediaItemResponse.Error) ImportResult(org.datatransferproject.spi.transfer.provider.ImportResult) Date(java.util.Date) PhotosLibrarySettings(com.google.photos.library.v1.PhotosLibrarySettings) FixedCredentialsProvider(com.google.api.gax.core.FixedCredentialsProvider) HashMap(java.util.HashMap) AppCredentials(org.datatransferproject.types.transfer.auth.AppCredentials) Iterators(com.google.common.collect.Iterators) ArrayList(java.util.ArrayList) TemporaryPerJobDataStore(org.datatransferproject.spi.cloud.storage.TemporaryPerJobDataStore) Strings(com.google.common.base.Strings) NewMediaItem(com.google.photos.library.v1.proto.NewMediaItem) Pair(org.apache.commons.lang3.tuple.Pair) Map(java.util.Map) Importer(org.datatransferproject.spi.transfer.provider.Importer) UploadMediaItemRequest(com.google.photos.library.v1.upload.UploadMediaItemRequest) NewMediaItemFactory(com.google.photos.library.v1.util.NewMediaItemFactory) Code(com.google.rpc.Code) ImageStreamProvider(org.datatransferproject.transfer.ImageStreamProvider) PhotosLibraryClient(com.google.photos.library.v1.PhotosLibraryClient) IdempotentImportExecutor(org.datatransferproject.spi.transfer.idempotentexecutor.IdempotentImportExecutor) UploadErrorException(org.datatransferproject.spi.transfer.types.UploadErrorException) Collection(java.util.Collection) Status(com.google.rpc.Status) MediaObject(org.datatransferproject.types.common.models.MediaObject) IOException(java.io.IOException) NewMediaItemResult(com.google.photos.library.v1.proto.NewMediaItemResult) UUID(java.util.UUID) UserCredentials(com.google.auth.oauth2.UserCredentials) File(java.io.File) FileNotFoundException(java.io.FileNotFoundException) VideosContainerResource(org.datatransferproject.types.common.models.videos.VideosContainerResource) BatchCreateMediaItemsResponse(com.google.photos.library.v1.proto.BatchCreateMediaItemsResponse) List(java.util.List) Stream(java.util.stream.Stream) Monitor(org.datatransferproject.api.launcher.Monitor) TokensAndUrlAuthData(org.datatransferproject.types.transfer.auth.TokensAndUrlAuthData) InvalidArgumentException(com.google.api.gax.rpc.InvalidArgumentException) VideoModel(org.datatransferproject.types.common.models.videos.VideoModel) Preconditions(com.google.common.base.Preconditions) UploadMediaItemResponse(com.google.photos.library.v1.upload.UploadMediaItemResponse) VisibleForTesting(com.google.common.annotations.VisibleForTesting) UnmodifiableIterator(com.google.common.collect.UnmodifiableIterator) AccessToken(com.google.auth.oauth2.AccessToken) InputStream(java.io.InputStream) ImportResult(org.datatransferproject.spi.transfer.provider.ImportResult) AccessToken(com.google.auth.oauth2.AccessToken) PhotosLibrarySettings(com.google.photos.library.v1.PhotosLibrarySettings) ArrayList(java.util.ArrayList) List(java.util.List) PhotosLibraryClient(com.google.photos.library.v1.PhotosLibraryClient) VideoModel(org.datatransferproject.types.common.models.videos.VideoModel) Date(java.util.Date)

Example 4 with PhotosLibraryClient

use of com.google.photos.library.v1.PhotosLibraryClient in project data-transfer-project by google.

the class GoogleVideosImporter method uploadMediaItem.

private Pair<String, Long> uploadMediaItem(MediaObject inputVideo, PhotosLibraryClient photosLibraryClient) throws IOException, UploadErrorException {
    final File tmp;
    try (InputStream inputStream = this.videoStreamProvider.getConnection(inputVideo.getContentUrl().toString()).getInputStream()) {
        tmp = dataStore.getTempFileFromInputStream(inputStream, inputVideo.getName(), ".mp4");
    }
    try {
        UploadMediaItemRequest uploadRequest = UploadMediaItemRequest.newBuilder().setFileName(inputVideo.getName()).setDataFile(new RandomAccessFile(tmp, "r")).build();
        UploadMediaItemResponse uploadResponse = photosLibraryClient.uploadMediaItem(uploadRequest);
        String uploadToken;
        if (uploadResponse.getError().isPresent() || !uploadResponse.getUploadToken().isPresent()) {
            Error error = uploadResponse.getError().orElse(null);
            if (error != null && error.getCause().getMessage().contains("The upload url is either finalized or rejected by the server")) {
                throw new UploadErrorException("Upload was terminated because of error", error.getCause());
            }
            throw new IOException("An error was encountered while uploading the video.", error != null ? error.getCause() : null);
        } else {
            uploadToken = uploadResponse.getUploadToken().get();
        }
        return Pair.of(uploadToken, tmp.length());
    } finally {
        // noinspection ResultOfMethodCallIgnored
        tmp.delete();
    }
}
Also used : UploadMediaItemResponse(com.google.photos.library.v1.upload.UploadMediaItemResponse) RandomAccessFile(java.io.RandomAccessFile) InputStream(java.io.InputStream) UploadErrorException(org.datatransferproject.spi.transfer.types.UploadErrorException) UploadMediaItemRequest(com.google.photos.library.v1.upload.UploadMediaItemRequest) Error(com.google.photos.library.v1.upload.UploadMediaItemResponse.Error) IOException(java.io.IOException) RandomAccessFile(java.io.RandomAccessFile) File(java.io.File)

Example 5 with PhotosLibraryClient

use of com.google.photos.library.v1.PhotosLibraryClient in project data-transfer-project by google.

the class GoogleVideosImporter method importVideoBatch.

long importVideoBatch(List<VideoModel> batchedVideos, PhotosLibraryClient client, IdempotentImportExecutor executor) throws Exception {
    final ArrayList<NewMediaItem> mediaItems = new ArrayList<>();
    final HashMap<String, VideoModel> uploadTokenToDataId = new HashMap<>();
    final HashMap<String, Long> uploadTokenToLength = new HashMap<>();
    // calls of the client to handle the InvalidArgumentException when the user's storage is full.
    try {
        for (VideoModel video : batchedVideos) {
            try {
                Pair<String, Long> pair = uploadMediaItem(video, client);
                final String uploadToken = pair.getLeft();
                mediaItems.add(buildMediaItem(video, uploadToken));
                uploadTokenToDataId.put(uploadToken, video);
                uploadTokenToLength.put(uploadToken, pair.getRight());
            } catch (IOException e) {
                if (e instanceof FileNotFoundException) {
                    // If the video file is no longer available then skip the video. We see this in a small
                    // number of videos where the video has been deleted.
                    monitor.info(() -> String.format("Video resource was missing for id: %s", video.getDataId()), e);
                    continue;
                }
                executor.executeAndSwallowIOExceptions(video.getDataId(), video.getName(), () -> {
                    throw e;
                });
            }
        }
        if (mediaItems.isEmpty()) {
            // Either we were not passed in any videos or we failed upload on all of them.
            return 0L;
        }
        BatchCreateMediaItemsResponse response = client.batchCreateMediaItems(mediaItems);
        final List<NewMediaItemResult> resultsList = response.getNewMediaItemResultsList();
        long bytes = 0L;
        for (NewMediaItemResult result : resultsList) {
            String uploadToken = result.getUploadToken();
            Status status = result.getStatus();
            final VideoModel video = uploadTokenToDataId.get(uploadToken);
            Preconditions.checkNotNull(video);
            final int code = status.getCode();
            if (code == Code.OK_VALUE) {
                executor.executeAndSwallowIOExceptions(video.getDataId(), video.getName(), () -> result.getMediaItem().getId());
                Long length = uploadTokenToLength.get(uploadToken);
                if (length != null) {
                    bytes += length;
                }
            } else {
                executor.executeAndSwallowIOExceptions(video.getDataId(), video.getName(), () -> {
                    throw new IOException(String.format("Video item could not be created. Code: %d Message: %s", code, result.getStatus().getMessage()));
                });
            }
            uploadTokenToDataId.remove(uploadToken);
        }
        if (!uploadTokenToDataId.isEmpty()) {
            for (VideoModel video : uploadTokenToDataId.values()) {
                executor.executeAndSwallowIOExceptions(video.getDataId(), video.getName(), () -> {
                    throw new IOException("Video item was missing from results list.");
                });
            }
        }
        return bytes;
    } catch (InvalidArgumentException e) {
        if (e.getMessage().contains("The remaining storage in the user's account is not enough")) {
            throw new DestinationMemoryFullException("Google destination storage full", e);
        } else {
            throw e;
        }
    }
}
Also used : Status(com.google.rpc.Status) HashMap(java.util.HashMap) DestinationMemoryFullException(org.datatransferproject.spi.transfer.types.DestinationMemoryFullException) ArrayList(java.util.ArrayList) FileNotFoundException(java.io.FileNotFoundException) NewMediaItemResult(com.google.photos.library.v1.proto.NewMediaItemResult) IOException(java.io.IOException) VideoModel(org.datatransferproject.types.common.models.videos.VideoModel) InvalidArgumentException(com.google.api.gax.rpc.InvalidArgumentException) NewMediaItem(com.google.photos.library.v1.proto.NewMediaItem) BatchCreateMediaItemsResponse(com.google.photos.library.v1.proto.BatchCreateMediaItemsResponse)

Aggregations

VideoModel (org.datatransferproject.types.common.models.videos.VideoModel)5 PhotosLibraryClient (com.google.photos.library.v1.PhotosLibraryClient)4 BatchCreateMediaItemsResponse (com.google.photos.library.v1.proto.BatchCreateMediaItemsResponse)4 NewMediaItemResult (com.google.photos.library.v1.proto.NewMediaItemResult)4 Monitor (org.datatransferproject.api.launcher.Monitor)4 FileNotFoundException (java.io.FileNotFoundException)3 IOException (java.io.IOException)3 InvalidArgumentException (com.google.api.gax.rpc.InvalidArgumentException)2 NewMediaItem (com.google.photos.library.v1.proto.NewMediaItem)2 UploadMediaItemRequest (com.google.photos.library.v1.upload.UploadMediaItemRequest)2 UploadMediaItemResponse (com.google.photos.library.v1.upload.UploadMediaItemResponse)2 Error (com.google.photos.library.v1.upload.UploadMediaItemResponse.Error)2 Status (com.google.rpc.Status)2 File (java.io.File)2 InputStream (java.io.InputStream)2 RandomAccessFile (java.io.RandomAccessFile)2 ArrayList (java.util.ArrayList)2 HashMap (java.util.HashMap)2 InMemoryIdempotentImportExecutor (org.datatransferproject.spi.transfer.idempotentexecutor.InMemoryIdempotentImportExecutor)2 DestinationMemoryFullException (org.datatransferproject.spi.transfer.types.DestinationMemoryFullException)2