#!/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