Created
May 16, 2020 10:27
-
-
Save ivmos/860b5db0ffeeeba8ffd33adebfaaa094 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env kscript | |
//DEPS com.google.photos.library:google-photos-library-client:1.5.0 | |
import com.google.api.gax.core.FixedCredentialsProvider | |
import com.google.api.gax.rpc.ApiException | |
import com.google.auth.oauth2.AccessToken | |
import com.google.auth.oauth2.UserCredentials | |
import com.google.photos.library.v1.PhotosLibraryClient | |
import com.google.photos.library.v1.PhotosLibrarySettings | |
import com.google.photos.library.v1.proto.BatchCreateMediaItemsResponse | |
import com.google.photos.library.v1.upload.UploadMediaItemRequest | |
import com.google.photos.library.v1.upload.UploadMediaItemResponse | |
import com.google.photos.library.v1.util.NewMediaItemFactory | |
import com.google.photos.types.proto.Album | |
import com.google.photos.types.proto.MediaItem | |
import com.google.rpc.Code | |
import java.io.File | |
import java.io.IOException | |
import java.io.RandomAccessFile | |
import java.util.* | |
import java.util.concurrent.TimeUnit | |
class AnsiColors { companion object { const val ANSI_RESET = "\u001B[0m"; const val ANSI_RED = "\u001B[31m"; const val ANSI_GREEN = "\u001B[32m"; const val ANSI_YELLOW = "\u001B[33m"; const val ANSI_BLUE = "\u001B[34m"; const val ANSI_PURPLE = "\u001B[35m"; const val ANSI_CYAN = "\u001B[36m"; const val ANSI_WHITE = "\u001B[37m"; } } | |
fun logInfo(message: String) = println("${AnsiColors.ANSI_BLUE}$message${AnsiColors.ANSI_RESET}") | |
fun logWarn(message: String) = println("${AnsiColors.ANSI_YELLOW}$message${AnsiColors.ANSI_RESET}") | |
fun logError(message: String) = println("${AnsiColors.ANSI_RED}$message${AnsiColors.ANSI_RESET}") | |
val defaultWorkingDir = System.getProperty("user.dir"); | |
fun String.runCommand(waitTimeInSeconds : Long = 300, workingDir : String = defaultWorkingDir): Pair<String?, Int> { | |
try { | |
val workingDirFile = File(workingDir) | |
val parts = this.split("\\s".toRegex()) | |
System.out.println(parts) | |
val proc = ProcessBuilder(*parts.toTypedArray()) | |
.directory(workingDirFile) | |
.redirectOutput(ProcessBuilder.Redirect.PIPE) | |
.redirectError(ProcessBuilder.Redirect.PIPE) | |
.start() | |
proc.waitFor(waitTimeInSeconds, TimeUnit.SECONDS) | |
return Pair<String?, Int>( | |
proc.inputStream.bufferedReader().readText(), | |
proc.exitValue()) | |
} catch(e: IOException) { | |
e.printStackTrace() | |
return Pair<String?, Int>(null, -1) | |
} | |
} | |
val usage = """ | |
Use this tool to... <TODO> | |
<TODO> required params info | |
""" | |
if (args.size < 2) { | |
//logWarn(usage) | |
//exitProcess(-1) | |
} | |
val dirs = "ls /Users/imosquera/tmp/Takeout/Google_Fotos".runCommand().first?.split("\n")?.filter { s -> !s.contains("-") }?.filter { s -> !s.contains("json") } | |
println(dirs?.drop(1)) | |
val cred = UserCredentials.newBuilder() | |
.setClientId("FOO.apps.googleusercontent.com") | |
.setClientSecret("bar") | |
.setRefreshToken("bar2") | |
.build() | |
var settings = PhotosLibrarySettings.newBuilder() | |
.setCredentialsProvider( | |
FixedCredentialsProvider.create(cred) | |
).build() | |
val client = PhotosLibraryClient.initialize(settings) | |
val response = client.listSharedAlbums() | |
var i = 0; | |
response.iterateAll().forEach { | |
t: Album? -> | |
run { | |
if (!t?.title?.isEmpty()!!) { | |
val album = client.createAlbum(t?.title + "exp2") | |
val folder = "/Users/imosquera/tmp/Takeout/Google_Fotos/" + (t?.title.replace(" ", "_")) | |
val result = ("ls " + folder).runCommand().first?.split("\n")?.filter{ s -> !s.contains("json")}?.map { s -> s.replace(" ", "-")} | |
result?.forEach { | |
if (!it.isEmpty()) { | |
val path = folder + "/" + it | |
//println("Will add " + path + " to album " + folder) | |
val mimeType = when { | |
it.contains("jpeg", true) -> "image/jpeg" | |
it.contains("jpg", true) -> "image/jpeg" | |
it.contains("heic", true) -> "image/heic" | |
it.contains("mov", true) -> "video/quicktime" | |
it.contains("mp4", true) -> "video/mp4" | |
it.contains("gif", true) -> "image/gif" | |
else -> "ouch" | |
} | |
if (mimeType == "ouch") { | |
println(it + "not known!") | |
} | |
val fileUpload = upload(client, path, it, mimeType, it) | |
println("Upload achieved" + fileUpload?.id) | |
if (fileUpload != null) { | |
val res = client.batchAddMediaItemsToAlbum(album.id, listOf(fileUpload.id)) | |
println(res) | |
} else { | |
println("Unable to upload") | |
} | |
} | |
} | |
i++; | |
if (i > 10) { | |
System.exit(0) | |
} | |
} | |
} | |
} | |
fun upload(photosLibraryClient: PhotosLibraryClient, pathToFile: String, fileName: String, mimeType: String, itemDescription: String): MediaItem? { | |
// Open the file and automatically close it after upload | |
try { | |
RandomAccessFile(pathToFile, "r").use { file -> | |
// Create a new upload request | |
val uploadRequest = UploadMediaItemRequest.newBuilder() // The media type (e.g. "image/png") | |
.setMimeType(mimeType) // The file to upload | |
.setDataFile(file) | |
.build() | |
// Upload and capture the response | |
val uploadResponse: UploadMediaItemResponse = photosLibraryClient.uploadMediaItem(uploadRequest) | |
if (uploadResponse.error.isPresent) { // If the upload results in an error, handle it | |
//val error: Error = uploadResponse.error.get() | |
println("Error!") | |
} else { // If the upload is successful, get the uploadToken | |
val uploadToken = uploadResponse.uploadToken.get() | |
// Use this upload token to create a media item | |
try { // Create a NewMediaItem with the following components: | |
// - uploadToken obtained from the previous upload request | |
// - filename that will be shown to the user in Google Photos | |
// - description that will be shown to the user in Google Photos | |
val newMediaItem = NewMediaItemFactory | |
.createNewMediaItem(uploadToken, fileName, itemDescription) | |
val newItems = Arrays.asList(newMediaItem) | |
val response: BatchCreateMediaItemsResponse = photosLibraryClient.batchCreateMediaItems(newItems) | |
for (itemsResponse in response.newMediaItemResultsList) { | |
val status = itemsResponse.status | |
if (status.code == Code.OK_VALUE) { // The item is successfully created in the user's library | |
val createdItem = itemsResponse.mediaItem | |
return createdItem | |
} else { // The item could not be created. Check the status and try again | |
println("Item could not be created") | |
} | |
} | |
} catch (e: ApiException) { // Handle error | |
return null | |
} | |
} | |
} | |
} catch (e: ApiException) { // Handle error | |
return null | |
} catch (e: IOException) { // Error accessing the local file | |
return null | |
} | |
return null | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The out-of-band (OOB) flow has been blocked in order to keep users secure. Follow the Out-of-Band (OOB) flow migration guide linked in the developer docs below to migrate your app to an alternative method.
Related developer documentation