(Java)Scripting Google Drive
Published
“a beautiful, intensely complicated way not to write a bash script”
— David
I do a podcast with a friend. For simplicity, everything is stored in a Team Drive within Google Drive. For episodes, the folder structure looks like:
Episodes
├── Ep. <x> - <title>
│ ├── EP<x> Notes.gdoc
│ └── Raw
│ ├── ep<x>-person1-raw.mp3
│ └── ep<x>-person2-raw.mp3
Where <x>
is the episode #, which starts at 0 and increments by 1 for each episode. Instead of manually creating folders & copying files around, I wanted a way to scaffold out each new episode—the episode folder, episode notes template, and Raw
folder.
Given the succinct naming conventions this is pretty easily automate-able. Since Google Drive can mount to your local filesystem, I could’ve made this a shell script using mkdir
and cp
in a few lines. However, I wanted a way to abstract it out via Google Apps Script which would allow me to run it from any computer & not have any dependencies with it mounted to my filesystem.
After a few hours of hacking away, here’s what I came up with:
var FOLDER_ID = "xxx"
var TEMPLATE_ID = "yyy"
function doGet () {
return main()
}
function main () {
Logger.log("Getting next episode number...")
var root = DriveApp.getFolderById(FOLDER_ID),
episode = getNextEpisodeNumber(root.getFolders())
Logger.log(episode)
Logger.log("Creating folder...")
var folder = root.createFolder("Ep. " + episode + " - TBD")
Logger.log("Copying template and creating 'Raw' folder...")
DriveApp.getFileById(TEMPLATE_ID).makeCopy("EP" + episode + " Notes", folder)
folder.createFolder("Raw")
return HtmlService.createHtmlOutput("Created new episode: #" + episode)
}
function getNextEpisodeNumber (folders) {
var max = 0
while (folders.hasNext()) {
var folder = folders.next(),
match = /^Ep\. ?(\d+)/i.exec(folder.getName())
if (match !== null)
max = Math.max(max, parseInt(match[1], 10))
}
// Return a string w/ decimal truncated
return (max + 1).toFixed(0)
}
The logic is pretty simple:
- Iterate over all folders within
Episodes/
- Parse the episode # from the folder name
- Find the largest one to get the next episode #
- Create the new folders & copy over the file
The FOLDER_ID
and TEMPLATE_ID
are grabbed from the URLs for each: https://drive.google.com/drive/u/<index>/folders/<id>
and https://docs.google.com/document/d/<id>/edit
, respectively.
There were a few weird gotchas:
ES6 support isn’t really there. You can technically use it in conjunction with Typescript, but that required some local CLI tooling I wasn’t interested in getting setup for a small hack like this.
Despite what I said above, ES6
const
variable declaration was kind of working (script was running, which was not the case withlet
) however the behavior wasn’t functioning as expected within a loop:while (folders.hasNext()) { const folder = folders.next() // note the `const` Logger.log(folder.getName()) // always the same value }
Somehow the
const
is making it immutable & retaining the same value between iterations within the loop body with no errors. Bizarre.
After publishing the script as a “web app”, I got a unique URL that I can hit with a GET request—that’s what the doGet
function is for. After bookmarking the page, I can scaffold out a new episode with the click of my mouse.
Sure, it’s an entirely convoluted solution to a problem that could be solved in a much simpler fashion. However, it’s nice to have a chance to take a peek at some other APIs & have a very portable tool.
I love hearing from readers so please feel free to reach out.