Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/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
You can’t perform that action at this time.