summaryrefslogtreecommitdiff
path: root/animism-align
diff options
context:
space:
mode:
Diffstat (limited to 'animism-align')
-rw-r--r--animism-align/frontend/app/common/imageCrop.component.js2
-rw-r--r--animism-align/frontend/app/utils/annotation.utils.js1
-rw-r--r--animism-align/frontend/app/utils/image.utils.js118
-rw-r--r--animism-align/frontend/app/utils/index.js79
-rw-r--r--animism-align/frontend/app/views/media/components/media.formGallery.js3
-rw-r--r--animism-align/frontend/app/views/media/components/media.formImage.js3
-rw-r--r--animism-align/frontend/app/views/viewer/viewer.actions.js14
7 files changed, 136 insertions, 84 deletions
diff --git a/animism-align/frontend/app/common/imageCrop.component.js b/animism-align/frontend/app/common/imageCrop.component.js
index f139dce..6ba9606 100644
--- a/animism-align/frontend/app/common/imageCrop.component.js
+++ b/animism-align/frontend/app/common/imageCrop.component.js
@@ -1,5 +1,5 @@
import React, { Component } from 'react'
-import { cropImage } from 'app/utils'
+import { cropImage } from 'app/utils/image.utils'
export default class ImageCrop extends Component {
state = {
diff --git a/animism-align/frontend/app/utils/annotation.utils.js b/animism-align/frontend/app/utils/annotation.utils.js
index 76175c9..a055d91 100644
--- a/animism-align/frontend/app/utils/annotation.utils.js
+++ b/animism-align/frontend/app/utils/annotation.utils.js
@@ -45,6 +45,7 @@ export const thumbnailURL = media => {
}
export const displayThumbnailURL = media => {
+ if (!media) return null
let image_id
switch (media.type) {
case 'video':
diff --git a/animism-align/frontend/app/utils/image.utils.js b/animism-align/frontend/app/utils/image.utils.js
new file mode 100644
index 0000000..dced2d8
--- /dev/null
+++ b/animism-align/frontend/app/utils/image.utils.js
@@ -0,0 +1,118 @@
+const preloadedImages = {}
+
+export const preloadImages = urls => batchPromise(urls, 4, preloadImage)
+
+export const batchPromise = (list, count, fn) => {
+ // console.log(list, count, fn)
+ return new Promise((resolve, reject) => {
+ let index = 0
+ let len = list.length
+ const worker = j => (
+ new Promise(resolveWorker => {
+ const next = () => {
+ const i = index++
+ if (i >= len) {
+ return resolveWorker()
+ }
+ const item = list[i]
+ // console.log(['worker', j, '=>', i, item].join(' '))
+ fn(item)
+ .then(next)
+ .catch(err => {
+ console.error(err, item)
+ next()
+ })
+ }
+ next()
+ })
+ )
+ const workers = []
+ for (let j = 0; j < count; j++) {
+ workers.push(worker(j))
+ }
+ Promise.all(workers)
+ .then(resolve)
+ .catch(reject)
+ })
+}
+
+export const preloadImage = (url, anonymous=false) => (
+ new Promise((resolve, reject) => {
+ if (preloadedImages[url] || typeof url === 'object' && url instanceof Image) {
+ return resolve(url)
+ }
+ preloadedImages[url] = true
+ const image = new Image()
+ let loaded = false
+ image.onload = () => {
+ if (loaded) return
+ loaded = true
+ image.onload = null
+ image.onerror = null
+ resolve(image)
+ }
+ image.onerror = () => {
+ if (loaded) return
+ image.onload = null
+ image.onerror = null
+ reject(image)
+ }
+ // console.log(img.src)
+ if (anonymous) {
+ image.crossOrigin = 'anonymous'
+ }
+ image.src = url
+ if (image.complete) {
+ image.onload()
+ }
+ })
+)
+
+export const cropImage = (url, crop, maxSide) => {
+ return new Promise((resolve, reject) => {
+ preloadImage(url, true)
+ .then(image => {
+ let { x, y, w, h } = crop
+ x = parseFloat(x)
+ y = parseFloat(y)
+ w = parseFloat(w)
+ h = parseFloat(h)
+ const canvas = document.createElement('canvas')
+ const ctx = canvas.getContext('2d')
+ const { naturalWidth, naturalHeight } = image
+
+ let width, height
+ let cropWidth = naturalWidth * w
+ let cropHeight = naturalHeight * h
+
+ if (maxSide > 0) {
+ if (cropWidth > cropHeight) {
+ width = Math.min(maxSide, cropWidth)
+ height = cropHeight * width / cropWidth
+ } else {
+ height = Math.min(maxSide, cropHeight)
+ width = cropWidth * height / cropHeight
+ }
+ } else {
+ width = cropWidth
+ height = cropHeight
+ }
+
+ canvas.width = width
+ canvas.height = height
+
+ ctx.drawImage(
+ image,
+ Math.round(x * naturalWidth),
+ Math.round(y * naturalHeight),
+ Math.round(w * naturalWidth),
+ Math.round(h * naturalHeight),
+ 0, 0, canvas.width, canvas.height
+ )
+ // console.log(x, y, w, h)
+ // console.log(naturalWidth, naturalHeight)
+ // console.log(width, height)
+ resolve(canvas)
+ })
+ })
+}
diff --git a/animism-align/frontend/app/utils/index.js b/animism-align/frontend/app/utils/index.js
index d6c1a16..ddbfb7e 100644
--- a/animism-align/frontend/app/utils/index.js
+++ b/animism-align/frontend/app/utils/index.js
@@ -114,85 +114,6 @@ export const sha256_tree = (sha256, branch_size=2, tree_depth=2) => {
return tree
}
-export const preloadImage = (url, anonymous=false) => (
- new Promise((resolve, reject) => {
- if (typeof url === 'object' && url instanceof Image) {
- return resolve(url)
- }
- const image = new Image()
- let loaded = false
- image.onload = () => {
- if (loaded) return
- loaded = true
- image.onload = null
- image.onerror = null
- resolve(image)
- }
- image.onerror = () => {
- if (loaded) return
- image.onload = null
- image.onerror = null
- resolve(image)
- }
- // console.log(img.src)
- if (anonymous) {
- image.crossOrigin = 'anonymous'
- }
- image.src = url
- if (image.complete) {
- image.onload()
- }
- })
-)
-
-export const cropImage = (url, crop, maxSide) => {
- return new Promise((resolve, reject) => {
- preloadImage(url, true)
- .then(image => {
- let { x, y, w, h } = crop
- x = parseFloat(x)
- y = parseFloat(y)
- w = parseFloat(w)
- h = parseFloat(h)
- const canvas = document.createElement('canvas')
- const ctx = canvas.getContext('2d')
- const { naturalWidth, naturalHeight } = image
-
- let width, height
- let cropWidth = naturalWidth * w
- let cropHeight = naturalHeight * h
-
- if (maxSide > 0) {
- if (cropWidth > cropHeight) {
- width = Math.min(maxSide, cropWidth)
- height = cropHeight * width / cropWidth
- } else {
- height = Math.min(maxSide, cropHeight)
- width = cropWidth * height / cropHeight
- }
- } else {
- width = cropWidth
- height = cropHeight
- }
-
- canvas.width = width
- canvas.height = height
-
- ctx.drawImage(
- image,
- Math.round(x * naturalWidth),
- Math.round(y * naturalHeight),
- Math.round(w * naturalWidth),
- Math.round(h * naturalHeight),
- 0, 0, canvas.width, canvas.height
- )
- // console.log(x, y, w, h)
- // console.log(naturalWidth, naturalHeight)
- // console.log(width, height)
- resolve(canvas)
- })
- })
-}
export const urlSearchParamsToDict = search => {
const params = new URLSearchParams(search)
const dict = {}
diff --git a/animism-align/frontend/app/views/media/components/media.formGallery.js b/animism-align/frontend/app/views/media/components/media.formGallery.js
index a62a4dc..ab7c8ed 100644
--- a/animism-align/frontend/app/views/media/components/media.formGallery.js
+++ b/animism-align/frontend/app/views/media/components/media.formGallery.js
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom'
import { ReactSortable } from "react-sortablejs"
import actions from 'app/actions'
-import { capitalize, preloadImage, simpleArraysEqual } from 'app/utils'
+import { capitalize, simpleArraysEqual } from 'app/utils'
+import { preloadImage } from 'app/utils/image.utils'
import { DISPLAY_SIZE, DISPLAY_QUALITY, THUMBNAIL_SIZE, THUMBNAIL_QUALITY } from 'app/constants'
import { TextInput, LabelDescription, FileInputField, Select, TextArea, Checkbox, SubmitButton, Loader } from 'app/common'
diff --git a/animism-align/frontend/app/views/media/components/media.formImage.js b/animism-align/frontend/app/views/media/components/media.formImage.js
index 7f22af0..dbbf69f 100644
--- a/animism-align/frontend/app/views/media/components/media.formImage.js
+++ b/animism-align/frontend/app/views/media/components/media.formImage.js
@@ -3,7 +3,8 @@ import { Link } from 'react-router-dom'
import { session } from 'app/session'
import actions from 'app/actions'
-import { capitalize, preloadImage, cropImage } from 'app/utils'
+import { capitalize } from 'app/utils'
+import { preloadImage, cropImage } from 'app/utils/image.utils'
import { DISPLAY_SIZE, DISPLAY_QUALITY, THUMBNAIL_SIZE, THUMBNAIL_QUALITY } from 'app/constants'
import { TextInput, LabelDescription, UploadImage, Select, TextArea, Checkbox, SubmitButton, Loader } from 'app/common'
diff --git a/animism-align/frontend/app/views/viewer/viewer.actions.js b/animism-align/frontend/app/views/viewer/viewer.actions.js
index 71dfa46..1113680 100644
--- a/animism-align/frontend/app/views/viewer/viewer.actions.js
+++ b/animism-align/frontend/app/views/viewer/viewer.actions.js
@@ -12,8 +12,9 @@ import {
} from 'app/constants'
import { floatInRange, timestampToSeconds } from 'app/utils'
import { buildParagraphs } from 'app/utils/transcript.utils'
-import { annotationFadeTimings } from 'app/utils/annotation.utils'
+import { annotationFadeTimings, displayThumbnailURL } from 'app/utils/annotation.utils'
import { getNextSection } from 'app/utils/viewer.utils'
+import { preloadImages } from 'app/utils/image.utils'
/* building the list of sections from the raw annotation list */
@@ -112,6 +113,7 @@ export const loadSections = () => dispatch => {
}
// build timeline of gallery / carousel advance instructions. this is in reverse so we can step thru it
+ // TODO: modify this to append these instructions to a list based on media_id, so we can grab it for the gallery
if (GALLERY_UTILITY_ANNOTATION_TYPES.has(annotation.type) && currentSection.fullscreenTimeline.length) {
const lastTimelineEvent = currentSection.fullscreenTimeline[currentSection.fullscreenTimeline.length - 1]
annotation.settings.frame_index = parseInt(annotation.settings.frame_index)
@@ -178,7 +180,7 @@ export const loadSections = () => dispatch => {
}
}
if ((!currentSection.fullscreenTimeline.length || time_to_first_fullscreen_element > 0.0) && currentSection.index !== 0) {
- // here we should create a dummy curtain event
+ // this section has no initial fullscreen event, so we should create a blank dummy curtain event
sectionColor = currentSection.paragraphs[0].annotations[0].settings.color || 'white'
initial_curtain_event = makeFullscreenEvent(0, {
start_ts: currentSection.start_ts,
@@ -206,6 +208,14 @@ export const loadSections = () => dispatch => {
currentSection.inlineParagraphCount = currentSection.paragraphs.filter(p => !p.hidden).length
// console.log(i, currentSection.inlineParagraphCount)
})
+
+ // Preload chapter cover URLs
+ const chapterCurtainImages = sections
+ .map(section => section.fullscreenTimeline.length && section.fullscreenTimeline[0].mediaItem)
+ .map(section => displayThumbnailURL(section))
+ .filter(s => !!s)
+ preloadImages(chapterCurtainImages)
+
// console.log(sections)
// console.log(footnoteList)
dispatch({ type: types.viewer.load_sections, sections, footnoteList })