Skip to content

Instantly share code, notes, and snippets.

@danopia
Last active April 3, 2019 06:25
Show Gist options
  • Save danopia/c3b3ce837200ae610de957ee7892529d to your computer and use it in GitHub Desktop.
Save danopia/c3b3ce837200ae610de957ee7892529d to your computer and use it in GitHub Desktop.
Withboard Google Drive Slideshow
{folderId, userId} = @config
return unless folderId and userId
return unless user = Meteor.users.findOne userId
{assets_s3_bucket} = Meteor.settings.public
parts = assets_s3_bucket.split '/'
if parts[2] isnt 's3.amazonaws.com'
throw new Error 'Weird S3 bucket'
Bucket = parts[3]
AssetPrefix = parts.slice(4).join('/')
Prefix = "#{AssetPrefix}dynamic/gdrive-slides/#{folderId}/"
s3 = new AWS.S3 params: {Bucket}
#objList = s3.listObjectsSync {Prefix}
#storedKeys = objList.Contents.map (x) -> x.Key
images = @getData('images') or new Array
images = images.filter (x) -> x.signedUrl
knownFiles = images.map (x) -> x.fileId
extraFiles = new Set(knownFiles)
{items} = GoogleApi.get "drive/v2/files/#{folderId}/children",
params:
maxResults: 1000
fields: 'items/id'
q: "trashed = false and (mimeType contains 'image/')"
# or mimeType contains 'video/'
user: user
for {id} in items
extraFiles.delete id
# download to S3 if not already
continue if knownFiles.includes id
Meteor._sleepForMs 1000 # rate limiting
meta = GoogleApi.get "drive/v2/files/#{id}",
params:
fields: 'createdDate,description,imageMediaMetadata(height,width),mimeType,ownerNames,downloadUrl,id,title,labels'
supportsTeamDrives: true
user: user
unless meta.imageMediaMetadata
console.log 'lacks media metadata:', meta
urlParts = meta.downloadUrl.split('/')
rawImage = GoogleApi.get urlParts.slice(3).join('/'),
host: urlParts.slice(0, 3).join('/')
user: user
npmRequestOptions:
encoding: null
Key = Prefix+id
s3.putObjectSync
Key: Key
ACL: 'bucket-owner-full-control'
Body: rawImage
CacheControl: 'public, max-age=604800'
ContentType: meta.mimeType
Metadata:
title: encodeURIComponent meta.title
created: meta.createdDate
width: ''+meta.imageMediaMetadata.width
height: ''+meta.imageMediaMetadata.height
console.log 'Uploaded slide', meta.title, 'to S3 as', Key
images.push
fileId: id
title: meta.title
description: meta.description
owners: meta.ownerNames
createdDate: meta.createdDate
size: meta.imageMediaMetadata
mimeType: meta.mimeType
# get the new token
console.log 'Had', knownFiles.length, 'images'
console.log 'Listed', items.length, 'images'
console.log 'Now have', images.length, 'images'
console.log 'Need to delete', extraFiles.size, 'images'
for extraId of extraFiles
console.log 'removing "extra" file', extraId
# remove from bucket
s3.deleteObjectSync
Key: Prefix+extraId
# remove from list
images = images.filter (x) ->
x.fileId isnt extraId
for image in images
image.signedUrl = s3.getSignedUrlSync 'getObject',
Bucket: Bucket
Key: Prefix+image.fileId
@setData 'images', images
->
style: 'background-image: url("' + @blobUrl + '");'
-> if img = Template.instance().image.get()
img if img.mimeType.indexOf('image/') is 0
->
@nextImage = new ReactiveVar
@image = new ReactiveVar
@timer = new Chronos.Timer 30 * 1000
console.log 'created'
->
# Dynamically update timer interval from config
@autorun =>
#speed = @getSetting('speed') ? 30
speed = @data.settings.speed ? 30
console.log 'Setting image interval to', +speed
@timer.setInterval +speed * 1000
# Start the timer ONLY when we have images
@autorun =>
images = @getData('images') ? []
console.log 'Have', images.length, 'images'
if images.length
@timer.start() unless @timer.isActive()
else @timer.stop()
# Select a new image to show next
# TODO: when we start combining portrait imges,
# we'll be building a composite object here
@autorun =>
@timer.dep()
Tracker.nonreactive =>
images = @getData('images') ? []
if prevImg = @nextImage.get()
images = images.filter (x) ->
x.fileId isnt prevImg.fileId
if images.length
@nextImage.set Random.choice(images)
console.log 'Set next image'
else
console.log 'No other image to go to :('
# Preload the next image, then switch to it
@autorun => if info = @nextImage.get()
console.log 'Loading', info.title
window.DRIVE_IMAGE_CACHE ?= new Map
if DRIVE_IMAGE_CACHE.has info.fileId
info.blobUrl = DRIVE_IMAGE_CACHE.get info.fileId
@image.set info
else
fetch info.signedUrl,
mode: 'cors'
.then (x) -> x.blob()
.then (x) =>
info.blobUrl = URL.createObjectURL(x)
DRIVE_IMAGE_CACHE.set info.fileId, info.blobUrl
console.log 'Image loaded as', info.blobUrl
@image.set info
.slideshow {
display: flex;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #000;
}
.slide {
flex: 1;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
transition: background-image 1s;
}
#anglebar {
position: absolute;
bottom: 5px;
margin-left: 25px;
font-size: 0.5em;
}
#anglebar li {
height: 50px;
margin: 0 25px;
}
#anglebar img {
height: 50px;
}
#anglebar h1 {
line-height: 50px;
}
#anglebar li::before, #anglebar li::after {
width: 50px;
height: 50px;
}
#anglebar h2, #anglebar h3 {
line-height: 5px;
height: 22px;
}
<div class="slideshow">
{{#with image}}
<div {{attrs}} class="slide">
</div>
{{/with}}
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment