Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import javax.annotation.Nullable;
import org.datatransferproject.api.launcher.Monitor;
import org.datatransferproject.datatransfer.google.common.GoogleCredentialFactory;
import org.datatransferproject.datatransfer.google.musicModels.ExportReleaseResponse;
import org.datatransferproject.datatransfer.google.musicModels.GoogleArtist;
import org.datatransferproject.datatransfer.google.musicModels.GooglePlaylist;
import org.datatransferproject.datatransfer.google.musicModels.GooglePlaylistItem;
Expand Down Expand Up @@ -98,8 +99,7 @@ public GoogleMusicExporter(
public ExportResult<MusicContainerResource> export(
UUID jobId, TokensAndUrlAuthData authData, Optional<ExportInformation> exportInformation)
throws IOException, InvalidTokenException, PermissionDeniedException, ParseException {
// TODO: Remove the logic when testing is finished.
// Local demo-server test usage. Start transfer job without ExportInformation.
// Used in production
if (!exportInformation.isPresent()) {
StringPaginationToken paginationToken = new StringPaginationToken(PLAYLIST_TOKEN_PREFIX);
return exportPlaylists(authData, Optional.of(paginationToken), jobId);
Expand All @@ -125,8 +125,7 @@ public ExportResult<MusicContainerResource> export(
return new ExportResult<>(ResultType.END, null, null);
} else if (paginationDataPresent
&& paginationToken.getToken().startsWith(RELEASE_TOKEN_PREFIX)) {
// TODO: export releases
return new ExportResult<>(ResultType.END, null, null);
return exportReleases(authData, Optional.of(paginationToken), jobId);
} else {
// There is nothing to export.
return new ExportResult<>(ResultType.END, null, null);
Expand Down Expand Up @@ -237,6 +236,57 @@ ExportResult<MusicContainerResource> exportPlaylistItems(
return new ExportResult<>(ResultType.CONTINUE, containerResource, continuationData);
}

@VisibleForTesting
ExportResult<MusicContainerResource> exportReleases(TokensAndUrlAuthData authData,
Optional<PaginationData> paginationData,
UUID jobId) throws InvalidTokenException, PermissionDeniedException, IOException {
Optional<String> paginationToken = getToken(RELEASE_TOKEN_PREFIX, paginationData);
ExportReleaseResponse exportReleaseResponse = getOrCreateMusicHttpApi(authData).exportReleases(paginationToken);

PaginationData nextPageData = null;
String token = exportReleaseResponse.getNextPageToken();
ResultType resultType = ResultType.END;
if (!Strings.isNullOrEmpty(token)) {
nextPageData = new StringPaginationToken(RELEASE_TOKEN_PREFIX + token);
resultType = ResultType.CONTINUE;
}

ContinuationData continuationData = new ContinuationData(nextPageData);
MusicContainerResource containerResource = null;
GoogleRelease[] googleReleases = exportReleaseResponse.getReleases();
List<MusicRelease> exportableReleases = new ArrayList<>();

if (googleReleases != null && googleReleases.length > 0) {
for (GoogleRelease googleRelease : googleReleases) {
exportableReleases.add(convertRelease(googleRelease));
monitor.debug(
() ->
String.format(
"%s: Google Music exporting release item: [release title: %s, release icpn: %s]",
jobId,
googleRelease.getTitle(),
googleRelease.getIcpn()));
}
containerResource = new MusicContainerResource(null, null, null, exportableReleases);
}
return new ExportResult<>(resultType, containerResource, continuationData);
}

private Optional<String> getToken(String prefix, Optional<PaginationData> paginationData){
Optional<String> paginationToken = Optional.empty();
if (paginationData.isPresent()) {
String token = ((StringPaginationToken) paginationData.get()).getToken();
Preconditions.checkArgument(
token.startsWith(prefix), "Invalid pagination token %s",
token);
if (prefix.length() < token.length()) {
paginationToken = Optional.of(token.substring(prefix.length()));
}
}
return paginationToken;
}


private int getTokenPrefixLength(String token) {
final ImmutableList<String> knownPrefixes =
ImmutableList.of(
Expand Down Expand Up @@ -267,6 +317,11 @@ private int getTokenPrefixLength(String token) {
return musicGroups;
}

private MusicRelease convertRelease(GoogleRelease googleRelease){
return new MusicRelease(googleRelease.getIcpn(), googleRelease.getTitle(), null);

}

private MusicPlaylistItem convertPlaylistItem(
String playlistId, GooglePlaylistItem googlePlaylistItem) throws ParseException {
GoogleTrack track = googlePlaylistItem.getTrack();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.datatransferproject.datatransfer.google.common.GoogleCredentialFactory;
import org.datatransferproject.datatransfer.google.musicModels.BatchPlaylistItemRequest;
import org.datatransferproject.datatransfer.google.musicModels.BatchPlaylistItemResponse;
import org.datatransferproject.datatransfer.google.musicModels.ExportReleaseResponse;
import org.datatransferproject.datatransfer.google.musicModels.GooglePlaylist;
import org.datatransferproject.datatransfer.google.musicModels.ImportPlaylistRequest;
import org.datatransferproject.datatransfer.google.musicModels.PlaylistItemExportResponse;
Expand All @@ -72,13 +73,15 @@ public class GoogleMusicHttpApi {

private static final String BASE_URL =
"https://youtubemediaconnect.googleapis.com/v1/users/me/musicLibrary/";
private static final String RELEASE_BASE_URL = "https://youtubemediaconnect.googleapis.com/v1/releases";
private static final int PLAYLIST_PAGE_SIZE = 20;
private static final int PLAYLIST_ITEM_PAGE_SIZE = 50;
private static final String PLAYLIST_ID_KEY = "playlistId";
private static final String PAGE_SIZE_KEY = "pageSize";
private static final String TOKEN_KEY = "pageToken";
private static final String ORIGINAL_PLAYLIST_ID_KEY = "originalPlaylistId";
private static final String ACCESS_TOKEN_KEY = "access_token";
private static final int RELEASE_ITEM_PAGE_SIZE = 50;

private final ObjectMapper objectMapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Expand Down Expand Up @@ -128,6 +131,20 @@ PlaylistItemExportResponse exportPlaylistItems(String playlistId, Optional<Strin
PlaylistItemExportResponse.class);
}

ExportReleaseResponse exportReleases(Optional<String> pageToken)
throws InvalidTokenException, PermissionDeniedException, IOException {

Map<String, String> params = new LinkedHashMap<>();
params.put(PAGE_SIZE_KEY, String.valueOf(RELEASE_ITEM_PAGE_SIZE));
if (pageToken.isPresent()) {
params.put(TOKEN_KEY, pageToken.get());
}
return makeGetRequest(
RELEASE_BASE_URL, Optional.of(params),
ExportReleaseResponse.class);

}

GooglePlaylist importPlaylist(GooglePlaylist playlist, String playlistId)
throws IOException, InvalidTokenException, PermissionDeniedException {
ImportPlaylistRequest importPlaylistRequest = new ImportPlaylistRequest(playlist, playlistId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.datatransferproject.datatransfer.google.musicModels;

import com.fasterxml.jackson.annotation.JsonProperty;

public class ExportReleaseResponse {

@JsonProperty("releases")
private GoogleRelease[] releases;


@JsonProperty("nextPageToken")
private String nextPageToken;

public GoogleRelease[] getReleases() {
return releases;
}

public String getNextPageToken() {
return nextPageToken;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.datatransferproject.datatransfer.google.music.GoogleMusicExporter.GOOGLE_PLAYLIST_NAME_PREFIX;
import static org.datatransferproject.datatransfer.google.music.GoogleMusicExporter.PLAYLIST_TOKEN_PREFIX;
import static org.datatransferproject.datatransfer.google.music.GoogleMusicExporter.RELEASE_TOKEN_PREFIX;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
Expand All @@ -36,6 +37,7 @@
import java.util.stream.Collectors;
import org.datatransferproject.api.launcher.Monitor;
import org.datatransferproject.datatransfer.google.common.GoogleCredentialFactory;
import org.datatransferproject.datatransfer.google.musicModels.ExportReleaseResponse;
import org.datatransferproject.datatransfer.google.musicModels.GooglePlaylist;
import org.datatransferproject.datatransfer.google.musicModels.GooglePlaylistItem;
import org.datatransferproject.datatransfer.google.musicModels.GoogleRelease;
Expand All @@ -46,6 +48,7 @@
import org.datatransferproject.spi.transfer.types.ContinuationData;
import org.datatransferproject.spi.transfer.types.InvalidTokenException;
import org.datatransferproject.spi.transfer.types.PermissionDeniedException;
import org.datatransferproject.types.common.ExportInformation;
import org.datatransferproject.types.common.PaginationData;
import org.datatransferproject.types.common.StringPaginationToken;
import org.datatransferproject.types.common.models.ContainerResource;
Expand All @@ -62,6 +65,7 @@ public class GoogleMusicExporterTest {

static final String PLAYLIST_PAGE_TOKEN = "playlist_page_token";
static final String PLAYLIST_ITEM_TOKEN = "playlist_item_token";
static final String RELEASE_ITEM_TOKEN = "release_item_token";

private final UUID uuid = UUID.randomUUID();

Expand All @@ -70,6 +74,7 @@ public class GoogleMusicExporterTest {

private PlaylistExportResponse playlistExportResponse;
private PlaylistItemExportResponse playlistItemExportResponse;
private ExportReleaseResponse exportReleaseResponse;

@BeforeEach
public void setUp() throws IOException, InvalidTokenException, PermissionDeniedException {
Expand All @@ -84,10 +89,12 @@ public void setUp() throws IOException, InvalidTokenException, PermissionDeniedE

playlistExportResponse = mock(PlaylistExportResponse.class);
playlistItemExportResponse = mock(PlaylistItemExportResponse.class);
exportReleaseResponse = mock(ExportReleaseResponse.class);

when(musicHttpApi.exportPlaylists(any(Optional.class))).thenReturn(playlistExportResponse);
when(musicHttpApi.exportPlaylistItems(any(String.class), any(Optional.class)))
.thenReturn(playlistItemExportResponse);
when(musicHttpApi.exportReleases(any(Optional.class))).thenReturn(exportReleaseResponse);

verifyNoInteractions(credentialFactory);
}
Expand Down Expand Up @@ -229,6 +236,93 @@ public void exportPlaylistItemSubsequentSet()
assertThat(paginationToken).isNull();
}

@Test
public void exportReleaseFirstSet()
throws InvalidTokenException, PermissionDeniedException, IOException, ParseException {
GoogleRelease release = setUpSingleRelease("Test", "R_icpn");
when(exportReleaseResponse.getReleases())
.thenReturn(new GoogleRelease[]{release});
when(exportReleaseResponse.getNextPageToken()).thenReturn(null);
StringPaginationToken inputPaginationToken = new StringPaginationToken(RELEASE_TOKEN_PREFIX);
ExportInformation exportInformation = new ExportInformation(inputPaginationToken, null);
ExportResult<MusicContainerResource> result = googleMusicExporter.export(uuid, null, Optional.of(exportInformation));

// Check results
// Verify correct methods were called
verify(musicHttpApi).exportReleases(Optional.empty());
verify(exportReleaseResponse).getReleases();

// Check pagination token
ContinuationData continuationData = result.getContinuationData();
StringPaginationToken paginationToken = (StringPaginationToken) continuationData.getPaginationData();
assertThat(paginationToken).isNull();
}

@Test
public void exportReleaseSubsequentSet()
throws InvalidTokenException, PermissionDeniedException, IOException, ParseException {
GoogleRelease release = setUpSingleRelease("Test", "R_icpn");
when(exportReleaseResponse.getReleases())
.thenReturn(new GoogleRelease[]{release});
when(exportReleaseResponse.getNextPageToken()).thenReturn(null);
StringPaginationToken inputPaginationToken = new StringPaginationToken(RELEASE_TOKEN_PREFIX + RELEASE_ITEM_TOKEN);
ExportInformation exportInformation = new ExportInformation(inputPaginationToken, null);
ExportResult<MusicContainerResource> result = googleMusicExporter.export(uuid, null, Optional.of(exportInformation));

// Check results
// Verify correct methods were called
verify(musicHttpApi).exportReleases(Optional.of(RELEASE_ITEM_TOKEN));
verify(exportReleaseResponse).getReleases();

// Check pagination token
ContinuationData continuationData = result.getContinuationData();
StringPaginationToken paginationToken = (StringPaginationToken) continuationData.getPaginationData();
assertThat(paginationToken).isNull();
}

@Test
public void exportReleaseWithNextPage()
throws InvalidTokenException, PermissionDeniedException, IOException, ParseException {
GoogleRelease release = setUpSingleRelease("Test", "R_icpn");
when(exportReleaseResponse.getReleases())
.thenReturn(new GoogleRelease[]{release});
when(exportReleaseResponse.getNextPageToken()).thenReturn(RELEASE_ITEM_TOKEN);
StringPaginationToken inputPaginationToken = new StringPaginationToken(RELEASE_TOKEN_PREFIX + RELEASE_ITEM_TOKEN);
ExportInformation exportInformation = new ExportInformation(inputPaginationToken, null);
ExportResult<MusicContainerResource> result = googleMusicExporter.export(uuid, null, Optional.of(exportInformation));

// Check results
// Verify correct methods were called
verify(musicHttpApi).exportReleases(Optional.of(RELEASE_ITEM_TOKEN));
verify(exportReleaseResponse).getReleases();

// Check pagination token
ContinuationData continuationData = result.getContinuationData();
StringPaginationToken paginationToken = (StringPaginationToken) continuationData.getPaginationData();
assertEquals(RELEASE_TOKEN_PREFIX+RELEASE_ITEM_TOKEN, paginationToken.getToken());
}

@Test
public void exportReleaseWithNoData()
throws InvalidTokenException, PermissionDeniedException, IOException, ParseException {
when(exportReleaseResponse.getReleases())
.thenReturn(null);
when(exportReleaseResponse.getNextPageToken()).thenReturn(null);
StringPaginationToken inputPaginationToken = new StringPaginationToken(RELEASE_TOKEN_PREFIX);
ExportInformation exportInformation = new ExportInformation(inputPaginationToken, null);
ExportResult<MusicContainerResource> result = googleMusicExporter.export(uuid, null, Optional.of(exportInformation));

// Check results
// Verify correct methods were called
verify(musicHttpApi).exportReleases(Optional.empty());
verify(exportReleaseResponse).getReleases();

// Check pagination token
ContinuationData continuationData = result.getContinuationData();
StringPaginationToken paginationToken = (StringPaginationToken) continuationData.getPaginationData();
assertThat(paginationToken).isNull();
}

/**
* Sets up a response with a single playlist, containing a single playlist item
*/
Expand All @@ -255,4 +349,11 @@ private GooglePlaylistItem setUpSinglePlaylistItem(String isrc, String icpn) {
playlistItemEntry.setTrack(track);
return playlistItemEntry;
}

private GoogleRelease setUpSingleRelease(String title, String icpn){
GoogleRelease release = new GoogleRelease();
release.setIcpn(icpn);
release.setReleaseTitle(title);
return release;
}
}