diff --git a/projects/Mallard/package.json b/projects/Mallard/package.json index d469de1401..5d6fbc40ba 100644 --- a/projects/Mallard/package.json +++ b/projects/Mallard/package.json @@ -12,7 +12,7 @@ "shake-android": "echo '👋 📱' && adb shell input keyevent 82 && echo '🙌📲🙌'", "wifi-android": "ip=$(adb shell ifconfig wlan0 | awk '/inet addr/{print substr($2,6)}') && adb tcpip 5555 && adb connect $ip:5555", "run-android": "yarn pre-run && adb reverse tcp:8081 tcp:8081 && adb reverse tcp:8097 tcp:8097 && adb reverse tcp:3131 tcp:3131 && react-native run-android", - "run-ios": "yarn pre-run && yarn install-pods && react-native run-ios", + "run-ios": "yarn pre-run && yarn install-pods && react-native run-ios", "run-ipad": "yarn pre-run && react-native run-ios --simulator=\"iPad Air 2\"", "validate": "cd ../.. && make validate-mallard", "fix": "cd ../.. && make fix-mallard", diff --git a/projects/Mallard/src/components/article/html/components/images.ts b/projects/Mallard/src/components/article/html/components/images.ts index 472fad0606..b550ac6f21 100644 --- a/projects/Mallard/src/components/article/html/components/images.ts +++ b/projects/Mallard/src/components/article/html/components/images.ts @@ -204,8 +204,7 @@ const Image = ({ const path = `${backend}${mediaPath( publishedId, imageSize, - imageElement.src.source, - imageElement.src.path, + imageElement.src, )}` return ImageBase({ path, ...imageElement }) diff --git a/projects/Mallard/src/components/front/image-resource.tsx b/projects/Mallard/src/components/front/image-resource.tsx index 8b11836171..4a92a4293b 100644 --- a/projects/Mallard/src/components/front/image-resource.tsx +++ b/projects/Mallard/src/components/front/image-resource.tsx @@ -9,7 +9,7 @@ import { } from 'react-native' import { useAspectRatio } from 'src/hooks/use-aspect-ratio' import { useImagePath, useScaledImage } from 'src/hooks/use-image-paths' -import { Image as IImage } from '../../../../common/src' +import { Image as IImage, ImageUse } from '../../../../common/src' /** * This component abstracts away the endpoint for images @@ -22,6 +22,7 @@ import { Image as IImage } from '../../../../common/src' */ type ImageResourceProps = { image: IImage + use: ImageUse style?: StyleProp setAspectRatio?: boolean } & Omit @@ -51,12 +52,14 @@ const ImageResource = ({ image, style, setAspectRatio = false, + use, //eslint-disable-line ...props }: ImageResourceProps) => { const [width, setWidth] = useState(null) - const imagePath = useImagePath(image) + const imagePath = useImagePath(image, 'full-size') //TODO: This should be changed to use once we have a good amount of content published with trail thumbs const aspectRatio = useAspectRatio(imagePath) const styles = [style, setAspectRatio && aspectRatio ? { aspectRatio } : {}] + return width && imagePath ? ( { const [isOpinionCard, isSportCard] = [useIsOpinionCard(), useIsSportCard()] - if (isOpinionCard && isSmallItem(size)) { return } @@ -98,6 +101,7 @@ const RoundImageItem = ({ article, size, ...tappableProps }: PropTypes) => { ) : null} diff --git a/projects/Mallard/src/components/front/items/items.tsx b/projects/Mallard/src/components/front/items/items.tsx index f8a6a21151..a92f1dd7e8 100644 --- a/projects/Mallard/src/components/front/items/items.tsx +++ b/projects/Mallard/src/components/front/items/items.tsx @@ -42,6 +42,7 @@ const CoverItem = ({ article, size, ...tappableProps }: PropTypes) => { ) : null} { style={[splashImageStyles.image]} image={cardImage} setAspectRatio + use="thumb" /> diff --git a/projects/Mallard/src/components/front/items/trail-image-view.tsx b/projects/Mallard/src/components/front/items/trail-image-view.tsx index 469d5569db..13f3a6ebd2 100644 --- a/projects/Mallard/src/components/front/items/trail-image-view.tsx +++ b/projects/Mallard/src/components/front/items/trail-image-view.tsx @@ -1,7 +1,9 @@ import React from 'react' -import { StyleSheet, View, StyleProp } from 'react-native' +import { StyleProp, StyleSheet, View } from 'react-native' +import { CAPIArticle, ImageUse } from 'src/common' import { Stars } from 'src/components/stars/stars' -import { CAPIArticle } from 'src/common' +import { useMediaQuery } from 'src/hooks/use-screen' +import { Breakpoints } from 'src/theme/breakpoints' import { ImageResource } from '../image-resource' import { SportScore } from 'src/components/sportscore/sportscore' import { ArticleType } from '../../../../../common/src' @@ -35,10 +37,18 @@ export const TrailImageView = ({ article: CAPIArticle style: StyleProp<{ width?: string; height?: string; marginLeft?: number }> }) => { + const isTablet = useMediaQuery(width => width >= Breakpoints.tabletVertical) + const { trailImage: image } = article if (image == null) { return null } + const use: ImageUse = + 'use' in image + ? isTablet + ? image.use.tablet + : image.use.mobile + : 'full-size' const frameStyle = [trailImageViewStyles.frame, style] const starRating = article.type === 'article' && article.starRating const sportScore = @@ -50,6 +60,7 @@ export const TrailImageView = ({ ) } else { - return + return } } diff --git a/projects/Mallard/src/hooks/use-image-paths.ts b/projects/Mallard/src/hooks/use-image-paths.ts index 43dd5e5627..84840d42aa 100644 --- a/projects/Mallard/src/hooks/use-image-paths.ts +++ b/projects/Mallard/src/hooks/use-image-paths.ts @@ -3,32 +3,34 @@ import ImageResizer from 'react-native-image-resizer' import RNFetchBlob from 'rn-fetch-blob' import { imageForScreenSize } from 'src/helpers/screen' import { APIPaths, FSPaths } from 'src/paths' -import { Image, ImageSize, Issue } from '../../../common/src' +import { Image, ImageSize, Issue, ImageUse } from '../../../common/src' import { useSettingsValue } from './use-settings' import { useIssueSummary } from './use-issue-summary' import { Platform } from 'react-native' const getFsPath = ( localIssueId: Issue['localId'], - { source, path }: Image, + image: Image, size: ImageSize, -) => FSPaths.media(localIssueId, source, path, size) + use: ImageUse, +) => FSPaths.image(localIssueId, size, image, use) export const selectImagePath = async ( apiUrl: string, localIssueId: Issue['localId'], publishedIssueId: Issue['publishedId'], - { source, path }: Image, + image: Image, + use: ImageUse, ) => { const imageSize = await imageForScreenSize() - const api = `${apiUrl}${APIPaths.media( + const api = `${apiUrl}${APIPaths.image( publishedIssueId, imageSize, - source, - path, + image, + use, )}` - const fs = getFsPath(localIssueId, { source, path }, imageSize) + const fs = getFsPath(localIssueId, image, imageSize, use) const fsExists = await RNFetchBlob.fs.exists(fs) const fsUpdatedPath = Platform.OS === 'android' ? 'file:///' + fs : fs @@ -57,7 +59,7 @@ const compressImagePath = async (path: string, width: number) => { * * */ -export const useImagePath = (image?: Image) => { +export const useImagePath = (image?: Image, use: ImageUse = 'full-size') => { const { issueId } = useIssueSummary() const [paths, setPaths] = useState() @@ -65,13 +67,18 @@ export const useImagePath = (image?: Image) => { useEffect(() => { if (issueId && image) { const { localIssueId, publishedIssueId } = issueId - selectImagePath(apiUrl, localIssueId, publishedIssueId, image).then( - setPaths, - ) + selectImagePath( + apiUrl, + localIssueId, + publishedIssueId, + image, + use, + ).then(setPaths) } }, [ apiUrl, image, + use, issueId ? issueId.publishedIssueId : undefined, // Why isn't this just issueId? issueId ? issueId.localIssueId : undefined, ]) diff --git a/projects/Mallard/src/paths/__tests__/index.spec.ts b/projects/Mallard/src/paths/__tests__/index.spec.ts index acfe280151..5c381a5e26 100644 --- a/projects/Mallard/src/paths/__tests__/index.spec.ts +++ b/projects/Mallard/src/paths/__tests__/index.spec.ts @@ -26,12 +26,6 @@ describe('paths', () => { ) }) - it('should give correct media root directory', () => { - expect(FSPaths.mediaRoot('daily-edition/2019-10-10')).toEqual( - 'path/to/base/directory/issues/daily-edition/2019-10-10/media', - ) - }) - it('should give correct zip file location based on a local issue id and filename', () => { expect( FSPaths.zip('daily-edition/2019-10-10', '2019-10-10'), @@ -57,17 +51,35 @@ describe('paths', () => { ) }) - it('should give a media path on the local device', () => { + it('should give a media path on the local device for a full sized image', () => { expect( - FSPaths.media( + FSPaths.image( 'daily-edition/2019-10-10', - 'source', - 'path', 'phone', + { + source: 'source', + path: 'path', + }, + 'full-size', ), ).toEqual( 'path/to/base/directory/issues/daily-edition/2019-10-10/media/phone/source/path', ) }) + it('should give a media path on the local device for a thumbnail image', () => { + expect( + FSPaths.image( + 'daily-edition/2019-10-10', + 'phone', + { + source: 'source', + path: 'path', + }, + 'thumb', + ), + ).toEqual( + 'path/to/base/directory/issues/daily-edition/2019-10-10/thumbs/phone/thumb/source/path', + ) + }) }) }) diff --git a/projects/Mallard/src/paths/index.ts b/projects/Mallard/src/paths/index.ts index 8606ed72fc..1acc98bc8c 100644 --- a/projects/Mallard/src/paths/index.ts +++ b/projects/Mallard/src/paths/index.ts @@ -1,15 +1,17 @@ import { issuePath, - mediaPath, frontPath, Issue, Collection, Front, CAPIArticle, ImageSize, + Image, + ImageUse, } from 'src/common' import RNFetchBlob from 'rn-fetch-blob' import { defaultSettings } from 'src/helpers/settings/defaults' +import { imagePath } from '../../../common/src' export interface PathToIssue { localIssueId: Issue['localId'] @@ -27,26 +29,24 @@ export interface PathToArticle { export const APIPaths = { issue: issuePath, front: frontPath, - media: mediaPath, + image: imagePath, } const issuesDir = `${RNFetchBlob.fs.dirs.DocumentDir}/issues` const issueRoot = (localIssueId: string) => `${issuesDir}/${localIssueId}` -const mediaRoot = (localIssueId: string) => `${issueRoot(localIssueId)}/media` export const MEDIA_CACHE_DIRECTORY_NAME = 'cached' export const FSPaths = { issuesDir, - issueRoot, - mediaRoot, contentPrefixDir: `${issuesDir}/${defaultSettings.contentPrefix}`, - media: ( + issueRoot, + image: ( localIssueId: string, - source: string, - path: string, size: ImageSize, - ) => `${mediaRoot(localIssueId)}/${size}/${source}/${path}`, + image: Image, + use: ImageUse, + ) => imagePath(issueRoot(localIssueId), size, image, use), zip: (localIssueId: string, filename: string) => `${issueRoot(localIssueId)}/${filename}.zip`, issueZip: (localIssueId: string) => `${issueRoot(localIssueId)}/data.zip`, diff --git a/projects/archiver/src/tasks/front/helpers/media.spec.ts b/projects/archiver/src/tasks/front/helpers/media.spec.ts index 1f6183d9e9..e7164032af 100644 --- a/projects/archiver/src/tasks/front/helpers/media.spec.ts +++ b/projects/archiver/src/tasks/front/helpers/media.spec.ts @@ -1,4 +1,4 @@ -import { CAPIArticle } from '../../../../common' +import { CAPIArticle, TrailImage } from '../../../../common' import { getImagesFromArticle } from './media' test('getImage', () => { @@ -24,3 +24,28 @@ test('getImage', () => { } expect(getImagesFromArticle(article)).toContain(image) }) + +test('getImageUse', () => { + const image: TrailImage = { + source: 'test', + path: 'image', + use: { mobile: 'full-size', tablet: 'thumb' }, + } + const article: CAPIArticle = { + key: '🔑', + type: 'article', + headline: '🗣', + showByline: false, + byline: '🧬', + standfirst: '🥇', + kicker: '🥾', + trail: '🛣', + trailImage: image, + showQuotedHeadline: false, + mediaType: 'Image', + elements: [], + isFromPrint: false, + bylineHtml: '🧬 Senior person', + } + expect(getImagesFromArticle(article)).toContain(image) +}) diff --git a/projects/archiver/src/tasks/front/helpers/media.ts b/projects/archiver/src/tasks/front/helpers/media.ts index a549976a87..9dbcdd3702 100644 --- a/projects/archiver/src/tasks/front/helpers/media.ts +++ b/projects/archiver/src/tasks/front/helpers/media.ts @@ -1,15 +1,17 @@ -import { unnest } from 'ramda' -import { attempt, hasFailed } from '../../../../../backend/utils/try' +import { unnest, uniq } from 'ramda' +import { hasFailed } from '../../../../../backend/utils/try' import { BlockElement, CAPIArticle, Front, Image, ImageSize, + ImageUse, notNull, + TrailImage, } from '../../../../common' -import { getColours, getImage } from '../../../utils/backend-client' -import { upload, ONE_WEEK } from '../../../utils/s3' +import { getImageUse } from '../../../utils/backend-client' +import { ONE_WEEK, upload } from '../../../utils/s3' const getImageFromElement = (element: BlockElement): Image | undefined => { switch (element.id) { @@ -21,9 +23,12 @@ const getImageFromElement = (element: BlockElement): Image | undefined => { return undefined } -export const getImagesFromArticle = (article: CAPIArticle): Image[] => { +export const getImagesFromArticle = ( + article: CAPIArticle, +): (Image | TrailImage)[] => { const image = article.image const trailImage = article.trailImage + const elements = article.type !== 'crossword' ? article.elements : [] const cardImages = [article.cardImage, article.cardImageTablet] const bylineImages = @@ -40,7 +45,7 @@ export const getImagesFromArticle = (article: CAPIArticle): Image[] => { ].filter(notNull) } -export const getImagesFromFront = (front: Front): Image[] => { +export const getImagesFromFront = (front: Front): (Image | TrailImage)[] => { const allCards = unnest(front.collections.map(_ => _.cards)) const articles = unnest(allCards.map(_ => Object.values(_.articles))) const images = unnest(articles.map(getImagesFromArticle)) @@ -48,25 +53,25 @@ export const getImagesFromFront = (front: Front): Image[] => { return images } -export const getAndUploadColours = async ( - publishedId: string, - image: Image, -) => { - const [colourPath, colours] = await getColours(publishedId, image) - if (hasFailed(colours)) { - console.error(`Could not get colours for ${colourPath}`) - console.error(JSON.stringify(colours)) - return colours - } - return attempt(upload(colourPath, colours, 'application/json', ONE_WEEK)) -} - -export const getAndUploadImage = async ( +export const getAndUploadImageUse = async ( publishedId: string, image: Image, size: ImageSize, + use: ImageUse, ) => { - const [path, data] = await getImage(publishedId, image, size) + const [path, data] = await getImageUse(publishedId, image, size, use) if (hasFailed(data)) return data return upload(path, data, 'image/jpeg', ONE_WEEK) } + +export const getImageUses = (image: Image | TrailImage): ImageUse[] => { + const fallback: ImageUse = 'full-size' + if (!('use' in image)) { + return [fallback] + } + return uniq( + [image.use.mobile, image.use.tablet, fallback].filter( + _ => _ !== 'not-used', + ), + ) +} diff --git a/projects/archiver/src/tasks/zip/helpers/lister.ts b/projects/archiver/src/tasks/zip/helpers/lister.ts new file mode 100644 index 0000000000..fdacdaef94 --- /dev/null +++ b/projects/archiver/src/tasks/zip/helpers/lister.ts @@ -0,0 +1,51 @@ +import { notNull } from '../../../../common' +import { Bucket, s3 } from '../../../utils/s3' + +type lsResponse = { keys: string[]; continuationToken: string | undefined } + +//This function gets all objects matching a prefix, and filters them by those that match any of the prefixes +//It will return a "continuationToken" which can be used as a prefix to get more objects +const listAndFilterPage = async (Prefix: string): Promise => { + const objects = await s3 + .listObjectsV2({ + Bucket, + Prefix, + }) + .promise() + + console.log('returned', JSON.stringify(objects)) + return { + keys: (objects.Contents || []).map(obj => obj.Key).filter(notNull), + continuationToken: objects.ContinuationToken, + } +} + +//This will list all the objects in a bucket in the form of an async iterable +async function* listAndFilterPrefixes( + prefixes: string[], +) /* : AsyncGenerator( Promise<{ keys: string[]; continuationToken?: string }>,true,never)*/ { + for (const prefix of prefixes) { + let nextPrefix: string | undefined = prefix + while (nextPrefix !== undefined) { + const { + keys, + continuationToken, + }: lsResponse = await listAndFilterPage(nextPrefix) + yield keys + nextPrefix = continuationToken + } + } + return +} + +//This will list all the objects that match a selection of prefixes. +//It will find a common prefix and list that, continuing until all have been found. +export const getMatchingObjects = async (prefixes: string[]) => { + console.log('listing', JSON.stringify(prefixes)) + console.log(JSON.stringify(prefixes)) + const keys: string[] = [] + for await (const partialKeys of listAndFilterPrefixes(prefixes)) { + keys.push(...partialKeys) + } + return keys +} diff --git a/projects/archiver/src/tasks/zip/helpers/zipper.ts b/projects/archiver/src/tasks/zip/helpers/zipper.ts index d195b75a47..faa628228e 100644 --- a/projects/archiver/src/tasks/zip/helpers/zipper.ts +++ b/projects/archiver/src/tasks/zip/helpers/zipper.ts @@ -1,16 +1,15 @@ import { PassThrough } from 'stream' import archiver = require('archiver') import { s3, Bucket, ONE_WEEK } from '../../../utils/s3' - -const notNull = (value: T | null | undefined): value is T => - value !== null && value !== undefined +import { getMatchingObjects } from './lister' export const zip = async ( name: string, - prefix: string, - options: { excludePath?: string; excludePrefixSegment?: string }, + prefixes: string[], + options: { removeFromOutputPath?: string }, ) => { const output = new PassThrough() + const upload = s3 .upload({ Bucket, @@ -22,27 +21,10 @@ export const zip = async ( }) .promise() - const objects = await s3 - .listObjectsV2({ - Bucket, - Prefix: `${prefix}/`, - }) - .promise() - // TODO: deal with paginating S3 responses - if (objects.IsTruncated) { - console.error( - "Object list from S3 was truncated which we don't currently deal with", - ) - } - const files = (objects.Contents || []).map(obj => obj.Key).filter(notNull) + const files = await getMatchingObjects(prefixes) console.log('Got file names') - const matches = - options.excludePath !== undefined - ? files.filter( - name => !name.startsWith(`${prefix}/${options.excludePath}`), - ) - : files + console.log('zipping', JSON.stringify(files)) const archive = archiver('zip') archive.on('warning', err => { @@ -51,16 +33,15 @@ export const zip = async ( archive.pipe(output) await Promise.all( - matches.map(async file => { - const zipPath = options.excludePrefixSegment - ? file.replace(`${options.excludePrefixSegment}/`, '') + files.map(async file => { + const zipPath = options.removeFromOutputPath + ? file.replace(`${options.removeFromOutputPath}`, '') : file console.log(`getting ${file}`) const s3response = await s3 .getObject({ Bucket, Key: file }) .promise() if (s3response.Body == null) return false - console.log(`adding ${file} to zip ${name}`) archive.append(s3response.Body as Buffer, { name: zipPath, diff --git a/projects/archiver/src/tasks/zip/index.ts b/projects/archiver/src/tasks/zip/index.ts index 34fbe3fa55..b5ccdd8e15 100644 --- a/projects/archiver/src/tasks/zip/index.ts +++ b/projects/archiver/src/tasks/zip/index.ts @@ -1,9 +1,16 @@ import { Handler } from 'aws-lambda' -import { imageSizes, issueDir, mediaDir } from '../../../common' +import { + imageSizes, + mediaDir, + issuePath, + frontPath, + ImageSize, +} from '../../../common' import { zip } from './helpers/zipper' import { UploadTaskOutput } from '../upload' import { handleAndNotify } from '../../services/task-handler' import { Bucket } from '../../utils/s3' +import { thumbsDir } from '../../../../common/src' type ZipTaskInput = UploadTaskOutput type ZipTaskOutput = UploadTaskOutput @@ -13,23 +20,33 @@ export const handler: Handler = handleAndNotify( const { issueDate, version } = issuePublication const { publishedId } = issue console.log('Compressing') - await zip(`${publishedId}/data`, publishedId, { - excludePath: 'media', - excludePrefixSegment: version, - }) + await zip( + `${publishedId}/data`, + [issuePath(publishedId), frontPath(publishedId, '')], + { + removeFromOutputPath: `${version}/`, + }, + ) console.log(`data zip uploaded to: s3://${Bucket}/${publishedId}`) await Promise.all( - imageSizes.map(async size => { - await zip( - `${publishedId}/${size}`, - mediaDir(publishedId, size), - { - excludePrefixSegment: version, - }, - ) - console.log(` ${size} media zip uploaded`) - }), + imageSizes.map( + async (size): Promise<[ImageSize, string]> => { + const imgUpload = await zip( + `${publishedId}/${size}`, + [ + thumbsDir(publishedId, size), + mediaDir(publishedId, size), + ], + { + removeFromOutputPath: `${version}/`, + }, + ) + + console.log(` ${size} media zip uploaded`) + return [size, imgUpload.Key] + }, + ), ) console.log('Media zips uploaded.') return { diff --git a/projects/archiver/src/utils/backend-client.ts b/projects/archiver/src/utils/backend-client.ts index 10d7017518..fc51dd39e8 100644 --- a/projects/archiver/src/utils/backend-client.ts +++ b/projects/archiver/src/utils/backend-client.ts @@ -1,13 +1,14 @@ import fetch from 'node-fetch' import { mediaPath, - coloursPath, issuePath, frontPath, Issue, Front, Image, ImageSize, + imagePath, + ImageUse, } from '../../common' import { attempt, @@ -47,12 +48,13 @@ export const getFront = async ( return maybeFront } -export const getImage = async ( +export const getImageUse = async ( publishedId: string, image: Image, size: ImageSize, + use: ImageUse, ): Promise<[string, Attempt]> => { - const path = mediaPath(publishedId, size, image.source, image.path) + const path = imagePath(publishedId, size, image, use) const url = `${URL}/${path}` const resp = attempt(fetch(url)) @@ -63,13 +65,3 @@ export const getImage = async ( return [path, await maybeResponse.buffer()] } - -export const getColours = async ( - publishedId: string, - image: Image, -): Promise<[string, Attempt<{}>]> => { - const path = coloursPath(publishedId, image.source, image.path) - const url = `${URL}/${path}` - const response = await attempt(fetch(url).then(_ => _.json())) - return [path, response] -} diff --git a/projects/backend/__tests__/application.spec.ts b/projects/backend/__tests__/application.spec.ts index 4fe5e289f1..30105ce4c7 100644 --- a/projects/backend/__tests__/application.spec.ts +++ b/projects/backend/__tests__/application.spec.ts @@ -16,7 +16,6 @@ const testStubControllers: EditionsBackendControllers = { issueController: stub, frontController: stub, imageController: stub, - imageColourController: stub, } describe('Endpoints contract test for Preview Editions Backend application', () => { diff --git a/projects/backend/application.ts b/projects/backend/application.ts index f28e51b67a..ab2b80caf6 100644 --- a/projects/backend/application.ts +++ b/projects/backend/application.ts @@ -1,8 +1,8 @@ import express = require('express') import { Request, Response } from 'express' -import { ImageSize, coloursPath } from '../common/src/index' -import { issuePath, mediaPath, frontPath, issueSummaryPath } from './common' import listEndpoints from 'express-list-endpoints' +import { ImageSize, ImageThumbnailUse, thumbsPath } from '../common/src/index' +import { frontPath, issuePath, issueSummaryPath, mediaPath } from './common' import { pickIssuePathSegments } from './utils/issue' export interface EditionsBackendControllers { @@ -10,7 +10,6 @@ export interface EditionsBackendControllers { issueController: (req: Request, res: Response) => void frontController: (req: Request, res: Response) => void imageController: (req: Request, res: Response) => void - imageColourController: (req: Request, res: Response) => void } export const createApp = ( @@ -49,13 +48,22 @@ export const createApp = ( app.get( '/' + - mediaPath(issuePathSegments, ':size' as ImageSize, ':source', '*?'), + mediaPath(issuePathSegments, ':size' as ImageSize, { + source: ':source', + path: '*?', + }), controllers.imageController, ) app.get( - '/' + coloursPath(issuePathSegments, ':source', '*?'), - controllers.imageColourController, + '/' + + thumbsPath( + issuePathSegments, + ':size' as ImageSize, + { source: ':source', path: '*?' }, + ':use' as ImageThumbnailUse, + ), + controllers.imageController, ) const endpoints = listEndpoints(app) diff --git a/projects/backend/controllers/image.ts b/projects/backend/controllers/image.ts index 73a6e81043..0f598d4db8 100644 --- a/projects/backend/controllers/image.ts +++ b/projects/backend/controllers/image.ts @@ -1,29 +1,24 @@ import { Request, Response } from 'express' -import { getImageURL, getPalette } from '../image' -import { imageSizes } from '../../common/src/index' +import { imageSizes, ImageUse, imageUses } from '../../common/src/index' import { Image } from '../common' +import { getImageURL } from '../image' + +const getUse = (use: string | undefined): ImageUse | undefined => { + const imageUse = imageUses.find(_ => _ == use) + return imageUse +} export const imageController = (req: Request, res: Response) => { const source = req.params.source const size = req.params.size const lastPathParam: string = req.params[0] const img: Image = { source, path: lastPathParam } + const use = getUse(req.params.use) || 'full-size' console.log(`Getting image redirect for ${source} ${size} ${lastPathParam}`) if (!imageSizes.includes(size)) { res.status(500) res.send('Invalid size') } - const redirect = getImageURL(img, size) + const redirect = getImageURL(img, size, use) res.redirect(redirect) } -export const imageColourController = (req: Request, res: Response) => { - const source = req.params.source - const lastPathParam: string = req.params[0] - const img: Image = { source, path: lastPathParam } - getPalette(img) - .then(data => { - res.setHeader('Content-Type', 'application/json') - res.send(JSON.stringify(data)) - }) - .catch(e => console.error(e)) -} diff --git a/projects/backend/image.ts b/projects/backend/image.ts index 53acd4d7b2..2b9899fe74 100644 --- a/projects/backend/image.ts +++ b/projects/backend/image.ts @@ -1,7 +1,6 @@ import { createHash } from 'crypto' -import { sizeDescriptions } from '../common/src' -import { ImageSize, Image } from './common' -import Vibrant from 'node-vibrant' +import { ImageUse, imageUseSizes } from '../common/src' +import { Image, ImageSize } from './common' const salt = process.env.IMAGE_SALT @@ -27,32 +26,13 @@ const getSignature = (path: string) => { .digest('hex') } -const sizes: { [k in ImageSize | 'sample']: number } = { - ...sizeDescriptions, - sample: 200, -} - -export const getImageURL = (image: Image, size: ImageSize | 'sample') => { - const newPath = `${image.path}?q=50&dpr=2&w=${sizes[size]}` +export const getImageURL = ( + image: Image, + size: ImageSize, + imageUse: ImageUse, +) => { + const newPath = `${image.path}?q=50&dpr=2&w=${imageUseSizes[imageUse][size]}` return `https://i.guim.co.uk/img/${ image.source }/${newPath}&s=${getSignature(newPath)}` } - -export const getPalette = async (image: Image) => { - const url = getImageURL(image, 'sample') - console.log(url) - const v = Vibrant.from(url).build() - const palette = await v.getPalette() - return { - ...image, - palette: { - Vibrant: palette.Vibrant && palette.Vibrant.getHex(), - Muted: palette.Muted && palette.Muted.getHex(), - DarkVibrant: palette.DarkVibrant && palette.DarkVibrant.getHex(), - DarkMuted: palette.DarkMuted && palette.DarkMuted.getHex(), - LightVibrant: palette.LightVibrant && palette.LightVibrant.getHex(), - LightMuted: palette.LightMuted && palette.LightMuted.getHex(), - }, - } -} diff --git a/projects/backend/index.ts b/projects/backend/index.ts index f45d689ad6..b6ff2766c4 100644 --- a/projects/backend/index.ts +++ b/projects/backend/index.ts @@ -4,7 +4,7 @@ import awsServerlessExpress from 'aws-serverless-express' import { Handler } from 'aws-lambda' import { issueController, issuesSummaryController } from './controllers/issue' import { frontController } from './controllers/fronts' -import { imageController, imageColourController } from './controllers/image' +import { imageController } from './controllers/image' import { createApp, EditionsBackendControllers } from './application' import { isPreview } from './preview' @@ -13,7 +13,6 @@ const runtimeControllers: EditionsBackendControllers = { issueController, frontController, imageController, - imageColourController, } const asPreview = isPreview diff --git a/projects/backend/package.json b/projects/backend/package.json index 6a960cf93e..22ff269805 100644 --- a/projects/backend/package.json +++ b/projects/backend/package.json @@ -10,13 +10,13 @@ "@types/aws-serverless-express": "^3.3.1", "@types/express": "^4.17.0", "@zeit/ncc": "^0.20.4", + "chai": "^4.1.2", + "chai-http": "^4.0.0", "dotenv": "^8.0.0", "ts-jest": "^24.0.2", "ts-node": "^8.3.0", "ts-node-dev": "^1.0.0-pre.40", - "typescript": "^3.5.3", - "chai": "^4.1.2", - "chai-http": "^4.0.0" + "typescript": "^3.5.3" }, "scripts": { "build": "ncc build index.ts -o dist -m", @@ -43,7 +43,6 @@ "jest": "^24.8.0", "moment": "^2.24.0", "node-fetch": "^2.6.0", - "node-vibrant": "^3.1.3", "ramda": "^0.26.1", "react-native-inappbrowser-reborn": "^3.0.1", "striptags": "^3.1.1", diff --git a/projects/backend/yarn.lock b/projects/backend/yarn.lock index 7f7048cb9c..d1fbad37eb 100644 --- a/projects/backend/yarn.lock +++ b/projects/backend/yarn.lock @@ -98,14 +98,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/polyfill@^7.0.0": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.4.4.tgz#78801cf3dbe657844eeabf31c1cae3828051e893" - integrity sha512-WlthFLfhQQhh+A2Gn5NSFl0Huxz36x86Jn+E9OW7ibK8edKPq+KLy4apM1yDpQ8kJOVi1OVjpP4vSDLdrI04dg== - dependencies: - core-js "^2.6.5" - regenerator-runtime "^0.13.2" - "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" @@ -306,258 +298,6 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^12.0.9" -"@jimp/bmp@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.5.4.tgz#b7b375aa774f26154912569864d5466e71333ef1" - integrity sha512-P/ezH1FuoM3FwS0Dm2ZGkph4x5/rPBzFLEZor7KQkmGUnYEIEG4o0BUcAWFmJOp2HgzbT6O2SfrpJNBOcVACzQ== - dependencies: - "@jimp/utils" "^0.5.0" - bmp-js "^0.1.0" - core-js "^2.5.7" - -"@jimp/core@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/core/-/core-0.5.4.tgz#69d2d9eef1a6a9d62127171e2688cf21bc0ee77c" - integrity sha512-n3uvHy2ndUKItmbhnRO8xmU8J6KR+v6CQxO9sbeUDpSc3VXc1PkqrA8ZsCVFCjnDFcGBXL+MJeCTyQzq5W9Crw== - dependencies: - "@jimp/utils" "^0.5.0" - any-base "^1.1.0" - buffer "^5.2.0" - core-js "^2.5.7" - exif-parser "^0.1.12" - file-type "^9.0.0" - load-bmfont "^1.3.1" - mkdirp "0.5.1" - phin "^2.9.1" - pixelmatch "^4.0.2" - tinycolor2 "^1.4.1" - -"@jimp/custom@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/custom/-/custom-0.5.4.tgz#393338efbf15d158ecf6639cb1b196c70411fddd" - integrity sha512-tLfyJoyouDl2J3RPFGfDzTtE+4S8ljqJUmLzy/cmx1n7+xS5TpLPdPskp7UaeAfNTqdF4CNAm94KYoxTZdj2mg== - dependencies: - "@jimp/core" "^0.5.4" - core-js "^2.5.7" - -"@jimp/gif@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/gif/-/gif-0.5.0.tgz#7543870b3d744c9758da76ca43fac4ee48fd6a00" - integrity sha512-HVB4c7b8r/yCpjhCjVNPRFLuujTav5UPmcQcFJjU6aIxmne6e29rAjRJEv3UMamHDGSu/96PzOsPZBO5U+ZGww== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - omggif "^1.0.9" - -"@jimp/jpeg@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/jpeg/-/jpeg-0.5.4.tgz#ff52669f801e9d82041ba6322ee781c344e75241" - integrity sha512-YaPWm+YSGCThNE/jLMckM3Qs6uaMxd/VsHOnEaqu5tGA4GFbfVaWHjKqkNGAFuiNV+HdgKlNcCOF3of+elvzqQ== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - jpeg-js "^0.3.4" - -"@jimp/plugin-blit@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-blit/-/plugin-blit-0.5.4.tgz#8c4f46e00c0a4ca9d5c592713de7575528485e59" - integrity sha512-WqDYOugv76hF1wnKy7+xPGf9PUbcm9vPW28/jHWn1hjbb2GnusJ2fVEFad76J/1SPfhrQ2Uebf2QCWJuLmOqZg== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-blur@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/plugin-blur/-/plugin-blur-0.5.0.tgz#c8222bdae8eb4cc86613c0adbcb26a92829739a2" - integrity sha512-5k0PXCA1RTJdITL7yMAyZ5tGQjKLHqFvwdXj/PCoBo5PuMyr0x6qfxmQEySixGk/ZHdDxMi80vYxHdKHjNNgjg== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-color@^0.5.5": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@jimp/plugin-color/-/plugin-color-0.5.5.tgz#68f9652d5065d3380a9967911a7e529325d230d6" - integrity sha512-hWeOqNCmLguGYLhSvBrpfCvlijsMEVaLZAOod62s1rzWnujozyKOzm2eZe+W3To6mHbp5RGJNVrIwHBWMab4ug== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - tinycolor2 "^1.4.1" - -"@jimp/plugin-contain@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-contain/-/plugin-contain-0.5.4.tgz#1dc258db36d50e23400e0644b7f2694fd74fbf60" - integrity sha512-8YJh4FI3S69unri0nJsWeqVLeVGA77N2R0Ws16iSuCCD/5UnWd9FeWRrSbKuidBG6TdMBaG2KUqSYZeHeH9GOQ== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-cover@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-cover/-/plugin-cover-0.5.4.tgz#a086243b151db9eef09e657fbe8bc3ef8683662e" - integrity sha512-2Rur7b44WiDDgizUI2M2uYWc1RmfhU5KjKS1xXruobjQ0tXkf5xlrPXSushq0hB6Ne0Ss6wv0+/6eQ8WeGHU2w== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-crop@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-crop/-/plugin-crop-0.5.4.tgz#124cf52aa07e36c7a33f39e2e86e78166c300ca7" - integrity sha512-6t0rqn4VazquGk48tO6hFBrQ+nkvC+A1RnR6UM/m8ZtG2/yjpwF0MXcpgJI1Fb+a4Ug7BY1fu2GPcZOhnAVK/g== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-displace@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/plugin-displace/-/plugin-displace-0.5.0.tgz#cb75d8588bdee45c1bdb1bec2323705d0e53d060" - integrity sha512-Bec7SQvnmKia4hOXEDjeNVx7vo/1bWqjuV6NO8xbNQcAO3gaCl91c9FjMDhsfAVb0Ou6imhbIuFPrLxorXsecQ== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-dither@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/plugin-dither/-/plugin-dither-0.5.0.tgz#0f1f6b7dcd5aba8f908bbd4b60685fc29cc6a3ed" - integrity sha512-We2WJQsD/Lm8oqBFp/vUv9/5r2avyenL+wNNu/s2b1HqA5O4sPGrjHy9K6vIov0NroQGCQ3bNznLkTmjiHKBcg== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-flip@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/plugin-flip/-/plugin-flip-0.5.0.tgz#4a973c9c4bdc6dbcc7da66204a2bb2b12feb9381" - integrity sha512-D/ehBQxLMNR7oNd80KXo4tnSET5zEm5mR70khYOTtTlfti/DlLp3qOdjPOzfLyAdqO7Ly4qCaXrIsnia+pfPrA== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-gaussian@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/plugin-gaussian/-/plugin-gaussian-0.5.0.tgz#02c9f07516108e01ba0f2938289b08e6e865c2c9" - integrity sha512-Ln4kgxblv0/YzLBDb/J8DYPLhDzKH87Y8yHh5UKv3H+LPKnLaEG3L4iKTE9ivvdocnjmrtTFMYcWv2ERSPeHcg== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-invert@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/plugin-invert/-/plugin-invert-0.5.0.tgz#4496d2d67ab498c8fa3e89c4b6dd5892e7f14b9b" - integrity sha512-/vyKeIi3T7puf+8ruWovTjzDC585EnTwJ+lGOOUYiNPsdn4JDFe1B3xd+Ayv9aCQbXDIlPElZaM9vd/+wqDiIQ== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-mask@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-mask/-/plugin-mask-0.5.4.tgz#ac4c2625e328818da1443c92bcb9cabb537c74ba" - integrity sha512-mUJ04pCrUWaJGXPjgoVbzhIQB8cVobj2ZEFlGO3BEAjyylYMrdJlNlsER8dd7UuJ2L/a4ocWtFDdsnuicnBghQ== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-normalize@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-normalize/-/plugin-normalize-0.5.4.tgz#d60aeb637bcaecadf654c9621e291d6eed12fa19" - integrity sha512-Q5W0oEz9wxsjuhvHAJynI/OqXZcmqEAuRONQId7Aw5ulCXSOg9C4y2a67EO7aZAt55T+zMVxI9UpVUpzVvO6hw== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-print@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-print/-/plugin-print-0.5.4.tgz#00524a7424a4e12a17764d349485dd1120a43728" - integrity sha512-DOZr5TY9WyMWFBD37oz7KpTEBVioFIHQF/gH5b3O5jjFyj4JPMkw7k3kVBve9lIrzIYrvLqe0wH59vyAwpeEFg== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - load-bmfont "^1.4.0" - -"@jimp/plugin-resize@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-resize/-/plugin-resize-0.5.4.tgz#c9b2c4949ee080df3fa2ca587539e2ce8588b8af" - integrity sha512-lXNprNAT0QY1D1vG/1x6urUTlWuZe2dfL29P81ApW2Yfcio471+oqo45moX5FLS0q24xU600g7cHGf2/TzqSfA== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-rotate@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/plugin-rotate/-/plugin-rotate-0.5.4.tgz#6c4c560779bc3ebf291db9a5095158d32a2a4af3" - integrity sha512-SIdUpMc8clObMchy8TnjgHgcXEQM992z5KavgiuOnCuBlsmSHtE3MrXTOyMW0Dn3gqapV9Y5vygrLm/BVtCCsg== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugin-scale@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/plugin-scale/-/plugin-scale-0.5.0.tgz#095f937e5a4887481b3074f5cd6a144d8f4f815e" - integrity sha512-5InIOr3cNtrS5aQ/uaosNf28qLLc0InpNGKFmGFTv8oqZqLch6PtDTjDBZ1GGWsPdA/ljy4Qyy7mJO1QBmgQeQ== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - -"@jimp/plugins@^0.5.5": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@jimp/plugins/-/plugins-0.5.5.tgz#e97fa368d69ad7718d5a2a9b6ffa8e6cc1e4264d" - integrity sha512-9oF6LbSM/K7YkFCcxaPaD8NUkL/ZY8vT8NIGfQ/NpX+tKQtcsLHcRavHpUC+M1xXShv/QGx9OdBV/jgiu82QYg== - dependencies: - "@jimp/plugin-blit" "^0.5.4" - "@jimp/plugin-blur" "^0.5.0" - "@jimp/plugin-color" "^0.5.5" - "@jimp/plugin-contain" "^0.5.4" - "@jimp/plugin-cover" "^0.5.4" - "@jimp/plugin-crop" "^0.5.4" - "@jimp/plugin-displace" "^0.5.0" - "@jimp/plugin-dither" "^0.5.0" - "@jimp/plugin-flip" "^0.5.0" - "@jimp/plugin-gaussian" "^0.5.0" - "@jimp/plugin-invert" "^0.5.0" - "@jimp/plugin-mask" "^0.5.4" - "@jimp/plugin-normalize" "^0.5.4" - "@jimp/plugin-print" "^0.5.4" - "@jimp/plugin-resize" "^0.5.4" - "@jimp/plugin-rotate" "^0.5.4" - "@jimp/plugin-scale" "^0.5.0" - core-js "^2.5.7" - timm "^1.6.1" - -"@jimp/png@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/png/-/png-0.5.4.tgz#4ed02435ab8ac219b618e9578dfd60626b3b5dd4" - integrity sha512-J2NU7368zihF1HUZdmpXsL/Hhyf+I3ubmK+6Uz3Uoyvtk1VS7dO3L0io6fJQutfWmPZ4bvu6Ry022oHjbi6QCA== - dependencies: - "@jimp/utils" "^0.5.0" - core-js "^2.5.7" - pngjs "^3.3.3" - -"@jimp/tiff@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/tiff/-/tiff-0.5.4.tgz#ce5370283eba390ff32b6fd86b9259d7cf3e2315" - integrity sha512-hr7Zq3eWjAZ+itSwuAObIWMRNv7oHVM3xuEDC2ouP7HfE7woBtyhCyfA7u12KlgtM57gKWeogXqTlewRGVzx6g== - dependencies: - core-js "^2.5.7" - utif "^2.0.1" - -"@jimp/types@^0.5.4": - version "0.5.4" - resolved "https://registry.yarnpkg.com/@jimp/types/-/types-0.5.4.tgz#c312e415ec9c4a35770e89b9eee424a96be60ab8" - integrity sha512-nbZXM6TsdpnYHIBd8ZuoxGpvmxc2SqiggY30/bhOP/VJQoDBzm2v/20Ywz5M0snpIK2SdYG52eZPNjfjqUP39w== - dependencies: - "@jimp/bmp" "^0.5.4" - "@jimp/gif" "^0.5.0" - "@jimp/jpeg" "^0.5.4" - "@jimp/png" "^0.5.4" - "@jimp/tiff" "^0.5.4" - core-js "^2.5.7" - timm "^1.6.1" - -"@jimp/utils@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@jimp/utils/-/utils-0.5.0.tgz#ecb33259c75238053d6c7706a3e91f657dbabf91" - integrity sha512-7H9RFVU+Li2XmEko0GGyzy7m7JjSc7qa+m8l3fUzYg2GtwASApjKF/LSG2AUQCUmDKFLdfIEVjxvKvZUJFEmpw== - dependencies: - core-js "^2.5.7" - "@types/aws-lambda@*": version "8.10.30" resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.30.tgz#ac334e908612d993e0c2fc0c04625f4ad05a50cc" @@ -684,7 +424,7 @@ dependencies: "@types/jest-diff" "*" -"@types/lodash@^4.14.136", "@types/lodash@^4.14.53": +"@types/lodash@^4.14.136": version "4.14.136" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.136.tgz#413e85089046b865d960c9ff1d400e04c31ab60f" integrity sha512-0GJhzBdvsW2RUccNHOBkabI8HZVdOXmXbXhuKlDEd5Vv12P7oAVGfomGp3Ne21o5D/qu1WmthlNKFaoZJJeErA== @@ -706,11 +446,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.3.tgz#44d507c5634f85e7164707ca36bba21b5213d487" integrity sha512-7TEYTQT1/6PP53NftXXabIZDaZfaoBdeBm8Md/i7zsWRoBe0YwOXguyK8vhHs8ehgB/w9U4K/6EWuTyp0W6nIA== -"@types/node@^10.11.7": - version "10.14.12" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.12.tgz#0eec3155a46e6c4db1f27c3e588a205f767d622f" - integrity sha512-QcAKpaO6nhHLlxWBvpc4WeLrTvPqlHOvaj0s5GriKkA1zq+bsFBPpfYCvQhLqLgYlIko8A9YrPdaMHCo5mBcpg== - "@types/ramda@^0.26.19": version "0.26.19" resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.26.19.tgz#400580597566c016e9743f17c1c48bf4671cda81" @@ -842,11 +577,6 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -any-base@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" - integrity sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg== - anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1055,11 +785,6 @@ binary-case@^1.0.0: resolved "https://registry.yarnpkg.com/binary-case/-/binary-case-1.1.4.tgz#d687104d59e38f2b9e658d3a58936963c59ab931" integrity sha512-9Kq8m6NZTAgy05Ryuh7U3Qc4/ujLQU1AZ5vMw4cr3igTdi5itZC6kCNrRr2X8NzPiDn2oUIFTfa71DKMnue/Zg== -bmp-js@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" - integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM= - body-parser@1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -1126,11 +851,6 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" -buffer-equal@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" - integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs= - buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -1145,14 +865,6 @@ buffer@4.9.1: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" - integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -1377,11 +1089,6 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js@^2.5.7, core-js@^2.6.5: - version "2.6.9" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" - integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== - core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -1563,11 +1270,6 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.1.tgz#0c667cb467ebbb5cea7f14f135cc2dba7780a8ff" integrity sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q== -dom-walk@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" - integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg= - domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -1626,7 +1328,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.0, es-abstract@^1.5.1: +es-abstract@^1.5.1: version "1.13.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== @@ -1712,11 +1414,6 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -exif-parser@^0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" - integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI= - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -1854,11 +1551,6 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -file-type@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-9.0.0.tgz#a68d5ad07f486414dfb2c8866f73161946714a18" - integrity sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw== - filewatcher@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/filewatcher/-/filewatcher-3.0.1.tgz#f4a1957355ddaf443ccd78a895f3d55e23c8a034" @@ -1904,13 +1596,6 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1981,7 +1666,7 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" -function-bind@^1.0.2, function-bind@^1.1.1: +function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== @@ -2046,14 +1731,6 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -global@~4.3.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" - integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= - dependencies: - min-document "^2.19.0" - process "~0.5.1" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -2302,7 +1979,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.3, is-callable@^1.1.4: +is-callable@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== @@ -2382,11 +2059,6 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= -is-function@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" - integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= - is-generator-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" @@ -2876,27 +2548,11 @@ jest@^24.8.0: import-local "^2.0.0" jest-cli "^24.8.0" -jimp@^0.5.4: - version "0.5.6" - resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.5.6.tgz#dd114decd060927ae439f2e0980df619c179f912" - integrity sha512-H0nHTu6KgAgQzDxa38ew2dXbnRzKm1w5uEyhMIxqwCQVjwgarOjjkV/avbNLxfxRHAFaNp4rGIc/qm8P+uhX9A== - dependencies: - "@babel/polyfill" "^7.0.0" - "@jimp/custom" "^0.5.4" - "@jimp/plugins" "^0.5.5" - "@jimp/types" "^0.5.4" - core-js "^2.5.7" - jmespath@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= -jpeg-js@^0.3.4: - version "0.3.5" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.5.tgz#6fbd6cd0e49627c5a0341796c9e50c70a2aa3673" - integrity sha512-hvaExqwmQDS8O9qnZAVDXGWU43Tbu1V0wMZmjROjT11jloSgGICZpscG+P6Nyi1BVAvyu2ARRx8qmEW30sxgdQ== - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3035,20 +2691,6 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -load-bmfont@^1.3.1, load-bmfont@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b" - integrity sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g== - dependencies: - buffer-equal "0.0.1" - mime "^1.3.4" - parse-bmfont-ascii "^1.0.3" - parse-bmfont-binary "^1.0.5" - parse-bmfont-xml "^1.1.4" - phin "^2.9.1" - xhr "^2.0.1" - xtend "^4.0.0" - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -3083,7 +2725,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.4: +lodash@^4.17.11, lodash@^4.17.13: version "4.17.14" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== @@ -3230,7 +2872,7 @@ mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.40.0" -mime@1.6.0, mime@^1.3.4, mime@^1.4.1: +mime@1.6.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -3240,13 +2882,6 @@ mimic-fn@^2.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= - dependencies: - dom-walk "^0.1.0" - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -3292,7 +2927,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.1, mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -3412,17 +3047,6 @@ node-pre-gyp@^0.12.0: semver "^5.3.0" tar "^4" -node-vibrant@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/node-vibrant/-/node-vibrant-3.1.3.tgz#0172ce83e35a3f6cd2223a00545a2012eab369da" - integrity sha512-zyo9FZVQyBn3gVSqa9gm4/jMYxcTg61N3jrRsAacpT2YoWnRds5TGrWbAqRArlLRSmhC/G8G2Wy01oLZeDqMfA== - dependencies: - "@types/lodash" "^4.14.53" - "@types/node" "^10.11.7" - jimp "^0.5.4" - lodash "^4.17.4" - url "^0.11.0" - nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -3534,11 +3158,6 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -omggif@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" - integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -3651,37 +3270,6 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pako@^1.0.5: - version "1.0.10" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" - integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== - -parse-bmfont-ascii@^1.0.3: - version "1.0.6" - resolved "https://registry.yarnpkg.com/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz#11ac3c3ff58f7c2020ab22769079108d4dfa0285" - integrity sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU= - -parse-bmfont-binary@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz#d038b476d3e9dd9db1e11a0b0e53a22792b69006" - integrity sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY= - -parse-bmfont-xml@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz#015319797e3e12f9e739c4d513872cd2fa35f389" - integrity sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ== - dependencies: - xml-parse-from-string "^1.0.0" - xml2js "^0.4.5" - -parse-headers@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.2.tgz#9545e8a4c1ae5eaea7d24992bca890281ed26e34" - integrity sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg== - dependencies: - for-each "^0.3.3" - string.prototype.trim "^1.1.2" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -3770,11 +3358,6 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -phin@^2.9.1: - version "2.9.3" - resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" - integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== - pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -3809,13 +3392,6 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" -pixelmatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854" - integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ= - dependencies: - pngjs "^3.0.0" - pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -3828,11 +3404,6 @@ pn@^1.1.0: resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== -pngjs@^3.0.0, pngjs@^3.3.3: - version "3.4.0" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" - integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== - posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -3858,11 +3429,6 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@~0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" - integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= - prompts@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.1.0.tgz#bf90bc71f6065d255ea2bdc0fe6520485c1b45db" @@ -4032,11 +3598,6 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -regenerator-runtime@^0.13.2: - version "0.13.2" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" - integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -4468,15 +4029,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string.prototype.trim@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" - integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.0" - function-bind "^1.0.2" - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -4602,16 +4154,6 @@ throat@^4.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= -timm@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/timm/-/timm-1.6.1.tgz#5f8aafc932248c76caf2c6af60542a32d3c30701" - integrity sha512-hqDTYi/bWuDxL2i6T3v6nrvkAQ/1Bc060GSkVEQZp02zTSTB4CHSKsOkliequCftQaNRcjRqUZmpGWs5FfhrNg== - -tinycolor2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" - integrity sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g= - tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -4842,26 +4384,11 @@ url@0.10.3: punycode "1.3.2" querystring "0.2.0" -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -utif@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/utif/-/utif-2.0.1.tgz#9e1582d9bbd20011a6588548ed3266298e711759" - integrity sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg== - dependencies: - pako "^1.0.5" - util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5019,27 +4546,12 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" -xhr@^2.0.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" - integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== - dependencies: - global "~4.3.0" - is-function "^1.0.1" - parse-headers "^2.0.0" - xtend "^4.0.0" - xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xml-parse-from-string@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" - integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= - -xml2js@0.4.19, xml2js@^0.4.5: +xml2js@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== diff --git a/projects/common/src/index.ts b/projects/common/src/index.ts index 55b831bec6..f430ea968d 100644 --- a/projects/common/src/index.ts +++ b/projects/common/src/index.ts @@ -1,5 +1,4 @@ import { FrontCardAppearance } from './collection/card-layouts' -import { getImageUse } from './collection/thumbnails' export * from './collection/card-layouts' export * from './collection/layout-model' export * from './collection/layouts' @@ -219,6 +218,31 @@ export const Editions = [ export type Edition = typeof Editions[number] +export const imageUseSizes: { + [u in ImageUse]: { [k in ImageSize]: number } +} = /* +Don't really want to run this all the time, so it's calculated below. +{ + 'full-size': sizeDescriptions, + 'not-used': { phone: 0, tablet: 0, tabletL: 0, tabletXL: 0 }, + thumb: { + phone: Math.ceil(sizeDescriptions.phone * 0.42), + tablet: Math.ceil(sizeDescriptions.tablet * 0.25), + tabletL: Math.ceil(sizeDescriptions.tabletL * 0.25), + tabletXL: Math.ceil(sizeDescriptions.tabletXL * 0.25), + }, + 'thumb-large': { + phone: Math.ceil(sizeDescriptions.phone * 0.6), + tablet: Math.ceil(sizeDescriptions.tablet * 0.52), + tabletL: Math.ceil(sizeDescriptions.tabletL * 0.52), + tabletXL: Math.ceil(sizeDescriptions.tabletXL * 0.53), + }, +}*/ { + 'full-size': { phone: 375, tablet: 740, tabletL: 980, tabletXL: 1140 }, + 'not-used': { phone: 0, tablet: 0, tabletL: 0, tabletXL: 0 }, + thumb: { phone: 158, tablet: 185, tabletL: 245, tabletXL: 285 }, + 'thumb-large': { phone: 225, tablet: 385, tabletL: 510, tabletXL: 605 }, +} export interface IssueIdentifier { edition: Edition issueDate: string @@ -401,17 +425,13 @@ export const frontPath = (issue: string, frontId: string) => // These have issueids in the path, but you'll need to change the archiver if you want to use them. export const mediaDir = (issue: string, size: ImageSize) => - `${issueDir(issue)}/media/${size}` + `${issueDir(issue)}/media/${size}/` export const mediaPath = ( issue: string, size: ImageSize, - source: string, - path: string, -) => `${mediaDir(issue, size)}/${source}/${path}` - -export const coloursPath = (issue: string, source: string, path: string) => - `${issueDir(issue)}/colours/${source}/${path}` + { source, path }: Image, +) => `${mediaDir(issue, size)}${source}/${path}` export const issueSummaryPath = (edition: string) => `${edition}/issues` export interface Image { @@ -419,7 +439,11 @@ export interface Image { path: string } -export type ImageUse = 'full-size' | 'thumb' | 'thumb-large' | 'not-used' +export const imageThumbnailUses = ['thumb', 'thumb-large', 'not-used'] as const +export const imageUses = [...imageThumbnailUses, 'full-size'] as const + +export type ImageThumbnailUse = typeof imageThumbnailUses[number] +export type ImageUse = typeof imageUses[number] export interface ImageDeviceUses { mobile: ImageUse @@ -430,6 +454,26 @@ export interface TrailImage extends Image { use: ImageDeviceUses } +export const thumbsDir = (issue: string, size: ImageSize) => + `${issueDir(issue)}/thumbs/${size}/` + +export const thumbsPath = ( + issue: string, + size: ImageSize, + image: Image, + use: ImageThumbnailUse, +) => `${thumbsDir(issue, size)}${use}/${image.source}/${image.path}` + +export const imagePath = ( + issue: string, + size: ImageSize, + image: Image, + use: ImageUse = 'full-size', +) => + use == 'full-size' + ? mediaPath(issue, size, image) + : thumbsPath(issue, size, image, use) + export interface CreditedImage extends Image { credit?: string caption?: string