Road Trip Timelapse
Compilation of time lapses from a recent toad trip - Grand Teton (with less than perfect focus), Yellowstone (Old Faithful), Yellowstone (Mammoth Hot Springs), Glacier (Swiftcurrent lake at the Many Glacier Area), Humboldt Redwoods State Park and then finally stars over Humboldt.
Related Posts
- Lassen Star Trails
- Stars over Columbia State Historic Park
- San Francisco to Shasta Lake
- Stars over Lake Tahoe
- Sugarloaf Stars and Milky Way
(Published to the Fediverse as: Road Trip Timelapse #timelapse #stars #glacier #yellowstone #grandteton #video Timelapse compilation featuring Grand Teton, Yellowstone, Glacier and Humboldt Redwoods )
Please fix phone spam Google!
95% of my incoming calls are now spam. Most of them are some strange pre-recorded Chinese voice with music playing in the background but I occasionally get a free hotel stay as well.
So far Google has rolled out Call Screen. This means I can waste my time watching Google Assistant talk to the spammer. It's way faster not to bother, hang up all calls and delete the voicemails later.
It seems like instead of Call Screen there could be a better way to deal with this.
Firstly, send any call not from someone in my contacts directly to voice mail. This would actually solve a lot of the problem.
Next, for extra credit, run spam detection on the voice mail before sending it to me. If it's two seconds long and blank then just bin it. If it's Chinese with music bin it. Only if it passes the smell test should it appear in my actual voice mail. Google is very good at this for Gmail.
Please!
Related Posts
- AI Wishlist: Use email to reply to voicemail
- Stamp out B2B spam with an evil calendar
- Chiroopractoor
- I'm not afraid of Google
- Android 11 Gripes
(Published to the Fediverse as: Please fix phone spam Google! #etc #google #spam #phone Why can't Google manage to fix phone spam when Gmail does such a good job? )
How to backup Google Photos to Google Drive automatically after July 2019 with Apps Script
Warning - I no longer recommend using this script to backup Google Photos. The Google Photos API has too many bugs that Google doensn't seem interested in fixing. My personal approach at this point is to use Google Takeout to get a periodic archive of my most recent year of photos and videos. I have a tool that does some deduplication and puts everything in year/month folders. See Photo Sorter for more details.
Google has decided that backing up your photos via Google Drive is 'confusing' and so Drive based backup is going away this month. I love Google Photos but I don't trust it - I pull everything into Drive and then I stick a monthly backup from there onto an external drive in a fire safe. There is a way to get Drive backup working again using Google Apps Script and the Google Photos API. There are a few steps to follow but it's pretty straightforward - you should only need to change two lines in the script to get this working for your account.
First two four caveats to be aware of. Apps Script has a time limit and so it's possible that it could fail if moving a large number of photos. You should get an email if the script ever fails so watch out for that. Secondly and more seriously you could end up with two copies of your photos. If you use Backup and Sync to add photos from Google Drive then these photos will be downloaded from Google Photos by the script and added to Drive again. You need to either upload directly to Google Photos (i.e. from the mobile app or web site) or handle the duplicates in some way. If you run Windows then I have released a command line tool that sorts photos into year+month taken folders and handles de-duplication.
One more limitation. After a comment by 'Logan' below I realized that Apps Script has a 50MB limitation for adding files to Google Drive. The latest version of the script will detect this and send you an email listing any files that could not be copied automatically.
And a fourth limitation after investigating a comment by 'Tim' it turns out there is a bug in the Google Photos API that means it will not download original quality video files. You get some lower resolution version instead. Together with the file size limit this is a bit of a deal breaker for most videos. Photos will be fine, but videos will need a different fix.
On to the script. In Google Drive create a new spreadsheet. This is just a host for the script and makes it easy to authorize it to access Google Photos. Select 'Script editor' from the Tools menu to create a new Apps Script project.
In the script editor select 'Libraries...' from the Resources menu. Enter 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF next to 'Add a library' and click add. This will find the Google OAuth2 library Pick the most recent version and click Save.
Select 'Project properties' from the File menu and copy the Script ID (a long sequence of letters and numbers). You'll need this when configuring the Google Photos API.
In a new window open the Google API Console, part of the Google Cloud Platform. Create a new project, click Enable APIs and Services and find and enable the Google Photos API. Then go to the Keys section and create an OAuth Client ID. You'll need to add a consent screen, the only field you need to fill out is the product name. Choose Web Application as the application type. When prompted for the authorized redirect URL enter https://script.google.com/macros/d/{SCRIPTID}/usercallback and replace {SCRIPTID} with the Script ID you copied above. Copy the Client ID and Client Secret which will be used in the next step.
Go back to the Apps Script project and paste the code below into the Code.gs window:
// script settings | |
var ClientID = ''; | |
var ClientSecret = ''; | |
var BackupFolder = 'Google Photos'; | |
var MaxLength = 50000000; | |
var AlertEmail = '' | |
function onOpen() { | |
var ui = SpreadsheetApp.getUi(); | |
ui.createMenu('Google Photos Backup') | |
.addItem('Authorize if needed (does nothing if already authorized)', 'showSidebar') | |
.addItem('Backup Now', 'runBackup') | |
.addItem('Reset Settings', 'clearProps') | |
.addToUi(); | |
} | |
function runBackup() { | |
var photos = getPhotosService(); | |
var nextPageToken = null; | |
var downloadList = []; | |
// Limit of around 10,000 photos | |
for (var page = 1; page <= 1000; page++) { | |
Logger.log('Requesting page ' + page); | |
var date = new Date(); | |
date.setDate(date.getDate() - 1); | |
var request = { | |
"pageSize":"100", | |
"filters": { | |
"dateFilter": { | |
"ranges": [ | |
{ | |
"startDate": { | |
"year": date.getFullYear(), | |
"month": date.getMonth() + 1, | |
"day": date.getDate() | |
}, | |
"endDate": { | |
"year": date.getFullYear(), | |
"month": date.getMonth() + 1, | |
"day": date.getDate() | |
} | |
} | |
] | |
} | |
} | |
}; | |
if (nextPageToken != null) { | |
request["pageToken"] = nextPageToken; | |
} | |
var response = UrlFetchApp.fetch('https://photoslibrary.googleapis.com/v1/mediaItems:search', { | |
headers: { | |
Authorization: 'Bearer ' + photos.getAccessToken() | |
}, | |
'method' : 'post', | |
'contentType' : 'application/json', | |
'payload' : JSON.stringify(request, null, 2) | |
}); | |
var json = JSON.parse(response.getContentText()); | |
if ('mediaItems' in json) { | |
var photoCount = json.mediaItems.length; | |
for (var i = 0; i < photoCount; i++) { | |
var filename = json.mediaItems[i].filename; | |
var baseUrl = json.mediaItems[i].baseUrl; | |
if ('photo' in json.mediaItems[i].mediaMetadata) { | |
// if the photo property exists it's a photo and use =d to download, otherwise a video and we need to use =dv | |
baseUrl += '=d'; | |
} else { | |
baseUrl += '=dv'; | |
} | |
downloadList.push({ | |
"filename" : filename, | |
"baseUrl" : baseUrl | |
}); | |
} | |
} | |
if ('nextPageToken' in json) { | |
nextPageToken = json.nextPageToken; | |
} else { | |
break; | |
} | |
} | |
var folder; | |
var folders = DriveApp.getFoldersByName(BackupFolder); | |
while (folders.hasNext()) { | |
folder = folders.next(); | |
break; | |
} | |
var alerts = []; | |
var downloadCount = downloadList.length; | |
for (var i = 0; i < downloadCount; i++) { | |
var filename = downloadList[i].filename; | |
Logger.log('Downloading ' + downloadList[i].filename); | |
while (true) { | |
var files = folder.getFilesByName(filename); | |
if (!files.hasNext()) { | |
break; | |
} | |
// duplicate... keep adding (1) to the start until it isn't any more | |
filename = '(1) ' + filename; | |
} | |
var response = UrlFetchApp.fetch(downloadList[i].baseUrl, { | |
headers: { | |
Authorization: 'Bearer ' + photos.getAccessToken() | |
} | |
}); | |
// check size | |
var responseHeaders = response.getHeaders(); | |
var contentLength = responseHeaders['Content-Length']; | |
if (contentLength >= MaxLength) { | |
// need to alert... | |
alerts.push('Cannot download ' + downloadList[i].filename + ' as it is too large.'); | |
} else { | |
// small enough to download | |
var blob = response.getBlob(); | |
blob.setName(filename); | |
folder.createFile(blob); | |
} | |
} | |
var alertCount = alerts.length; | |
if (alertCount > 0) { | |
MailApp.sendEmail(AlertEmail, 'Google Photos Backup large file failures on ' + new Date(), alerts.join('\r\n')); | |
} | |
} | |
// functions below adapted from Google OAuth example at https://github.com/googlesamples/apps-script-oauth2 | |
function getPhotosService() { | |
// Create a new service with the given name. The name will be used when | |
// persisting the authorized token, so ensure it is unique within the | |
// scope of the property store. | |
return OAuth2.createService('photos') | |
// Set the endpoint URLs, which are the same for all Google services. | |
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth') | |
.setTokenUrl('https://accounts.google.com/o/oauth2/token') | |
// Set the client ID and secret, from the Google Developers Console. | |
.setClientId(ClientID) | |
.setClientSecret(ClientSecret) | |
// Set the name of the callback function in the script referenced | |
// above that should be invoked to complete the OAuth flow. | |
.setCallbackFunction('authCallback') | |
// Set the property store where authorized tokens should be persisted. | |
.setPropertyStore(PropertiesService.getUserProperties()) | |
// Set the scopes to request (space-separated for Google services). | |
// see https://developers.google.com/fit/rest/v1/authorization for a list of Google Fit scopes | |
.setScope('https://www.googleapis.com/auth/photoslibrary.readonly') | |
// Below are Google-specific OAuth2 parameters. | |
// Sets the login hint, which will prevent the account chooser screen | |
// from being shown to users logged in with multiple accounts. | |
.setParam('login_hint', Session.getActiveUser().getEmail()) | |
// Requests offline access. | |
.setParam('access_type', 'offline') | |
// Forces the approval prompt every time. This is useful for testing, | |
// but not desirable in a production application. | |
//.setParam('approval_prompt', 'force'); | |
} | |
function showSidebar() { | |
var photos = getPhotosService(); | |
if (!photos.hasAccess()) { | |
var authorizationUrl = photos.getAuthorizationUrl(); | |
var template = HtmlService.createTemplate( | |
'<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' + | |
'Close this after you have finished.'); | |
template.authorizationUrl = authorizationUrl; | |
var page = template.evaluate(); | |
SpreadsheetApp.getUi().showSidebar(page); | |
} else { | |
// ... | |
} | |
} | |
function authCallback(request) { | |
var photos = getPhotosService(); | |
var isAuthorized = photos.handleCallback(request); | |
if (isAuthorized) { | |
return HtmlService.createHtmlOutput('Success! You can close this tab.'); | |
} else { | |
return HtmlService.createHtmlOutput('Denied. You can close this tab'); | |
} | |
} | |
function clearProps() { | |
PropertiesService.getUserProperties().deleteAllProperties(); | |
} |
Enter the Client ID and Client Secret inside the empty quotes at the top of the file. You also need to add an email address to receive alerts for large files. There is a BackupFolder option at the top as well - the default is 'Google Photos' which will mimic the old behavior. You can change this if you like but make sure that the desired folder exists before running the script. Save the script.
Go back to the spreadsheet you created and reload. After a few seconds you will have a Google Photos Backup menu (to the right of the Help menu). Choose 'Authorize' from this menu. You will be prompted to give the script various permissions which you should grant. After this a sidebar should appear on the spreadsheet (if not choose 'Authorize' from the Google Photos Backup menu again). Click the authorize link from the sidebar to grant access to Google Photos. Once this is done you should be in business - choose Backup Now from the Google Photos Backup menu and any new items from yesterday should be copied to the Google Photos folder in Drive (or the folder you configured above if you changed this).
Finally you should set up a trigger to automate running the script every day. Choose 'Script editor' from the Tools menu to re-open the script, and then in the script window choose 'Current project's triggers' from the Edit menu. This will open yet another window. Click 'Add Trigger' which is cunningly hidden at the bottom right of the window. Under 'Choose which function to run' select 'runBackup'. Then under 'Select event source' select 'Time-driven'. Under 'Select type of time based trigger' select 'Day timer'. Under 'Select time of day' select the time window that works best for you. Click Save. The backup should now run every day.
The way the script is written you'll get a backup of anything added the previous day each time it runs. If there are any duplicate filenames in the backup folder the script will save a new copy of the file with (1) appended in front of the filename. Let me know in the comments if you use this script or have any suggestions to improve it.
More Google Apps Script Projects
- Get an email when your security camera sees something new (Apps Script + Cloud Vision)
- Get an email if your site stops being mobile friendly (no longer available)
- Export Google Fit Daily Steps, Weight and Distance to a Google Sheet
- Email Alerts for new Referers in Google Analytics using Apps Script
- Animation of a year of Global Cloud Cover
- Control LIFX WiFi light bulbs from Google Apps Script
- Adding AI to Todoist with Google Apps Script and OpenAI
- Using the Todoist API to set a due date on the Alexa integration to-do list (with Apps Script)
- Automate Google PageSpeed Insights and Core Web Vitals Logging with Apps Script
- Using the Azure Monitor REST API from Google Apps Script
- Monitor page index status with Google Sheets, Apps Script and the Google Search Console API
(Published to the Fediverse as: How to backup Google Photos to Google Drive automatically after July 2019 with Apps Script #code #software #photos #appsscript #google #sheets #drive #api Easy to configure apps script to continue backing up your Google Photos to Google Drive after the July 2019 change. You just need to change two lines of the script to get this running with your Google Photos account. )
Stars over Pinnacles
Timelapse of the night sky over Pinnacles National Park in California. Jupiter and the Milky Way are both prominent. Shot from the campground on July 4, 2019.
(A hike to some of the caves a few years ago).
Related Posts
- Stars over Columbia State Historic Park
- Stars from Pine Mountain Lake
- Stars over Casini Ranch, a Timelapse
- Jupiter from Casini Ranch
- Time Lapse of the Milky Way over Lake Shasta
(Published to the Fediverse as: Stars over Pinnacles #timelapse #stars #pinnacles #video A timelapse showing the Milky Way and Jupiter over the Pinnacles National Park in California. )
ITHCWY Newsletter for June 2019
Timelapse of Pacifica State Beach.
Cory Doctorow argues for adversarial interoperability as a tool to weaken tech monopolies. I've reached the same conclusion: Facebook shouldn't own your social graph.
Dead Ringers S18E04 (available very briefly) has a halfway decent Brexit / Douglas Adams joke. Here's a better one.
A longer hike at China Camp State Park.
Previously:
- 2018: Animation of a year of Global Cloud Cover
- 2017: Reading and Writing Office 365 Excel from a Console app using the Microsoft.Graph C# Client API
- 2016: West Portal Timelapse of Timelapses
Related Posts
Bay View and Shoreline Trails at China Camp State Park
A nice 4 mile hike at China Camp State Park - starting at the day use parking in the campsite follow Powerline Fire Trail up to a left on Bay View Trail then left on Back Ranch Fire Trail and finally left again on Shoreline Trial back to the parking lot. Gentle ascent for three miles then a fairly steep stretch down Back Ranch Fire Trail (I slipped three times, watch where you're going) and then gentle again for the final half mile.
(Previously: shorter 3 mile hike of Back Ranch Meadows and Turtleback Point loop)
Hike starts at: 38.006014, -122.496775. View in Google Earth.
Related Posts
(Hike Map)
(Published to the Fediverse as: Bay View and Shoreline Trails at China Camp State Park #hike #chinacamp #map Bay View Trail to Shoreline Trail 4 mile hike at China Camp State Park )
Summer Solstice 2019
Summer starts now in the Northern Hemisphere, Winter if you happen to be south of the Equator. Rendered in Catfood Earth.
Related Posts
San Francisco from San Bruno Mountain
Photo of San Francisco framed by a mossy tree on San Bruno Mountain.
The photograph captures a captivating scene from San Bruno Mountain, where the lush greenery in the foreground frames a distant view of San Francisco's skyline. Twisted branches, adorned with moss and lichen, create a natural window through which the cityscape emerges, enticing the eye to travel from the wild, textured foreground to the slick, urban expanse beyond. The city appears as a distant sentinel, its skyscrapers punctuating the horizon, against a serene, cloudless sky. The juxtaposition of natural and manmade worlds invites a contemplation of their coexistence and contrast.
In terms of composition, the photograph employs a clever use of framing, using the organic shapes of the tree branches to guide the viewer’s gaze towards the city. This stylistic choice emphasizes depth, drawing attention to the dichotomy between the untouched foreground and the bustling metropolis in the distance. The image benefits from a strong sense of depth and texture, achieved through the detailed focus on the mossy branches. However, the shot could benefit from better balancing of light, as the bright sky might overwhelm the details in the foreground. Overall, this photograph artfully encapsulates the contrasting worlds of nature and urban life, showcasing both beauty and tension in its composition.
Related Posts
- San Francisco from Mount Davidson
- San Francisco
- San Francisco from Twin Peaks
- Grand View of Fog
- San Francisco from Alameda
(Published to the Fediverse as: San Francisco from San Bruno Mountain #photo #sanfrancisco #sanbruno A mossy tree frames San Francisco, shot from San Bruno Mountain in San Mateo, California. )
Pacifica
Timelapse of Pacifica State Beach (on a rare warm and sunny day).
Related Posts
(Published to the Fediverse as: Pacifica #timelapse #pacifica #video Time lapse of Pacifica State Beach in California on a warm and sunny day. )