Skip to content

Commit d2c366f

Browse files
authored
Merge pull request #6 from cybozu/feat/add-tumbnails
ogpのcross-origin-resource-policy: same-origin対策
2 parents 7cee6be + 7429a45 commit d2c366f

3 files changed

Lines changed: 49 additions & 21 deletions

File tree

src/feed/generate-feed-command.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,24 @@ const feedImagePrecacher = new FeedImagePrecacher();
4646
);
4747
}
4848

49-
// まとめフィード作成
5049
const ogObjectMap = new Map([...crawlFeedsResult.feedItemOgObjectMap, ...crawlFeedsResult.feedBlogOgObjectMap]);
50+
51+
// 画像のキャッシュとサムネイルURL取得(フィード生成前に実行してURLをフィードに含める)
52+
const thumbnailUrlMap = await feedImagePrecacher.fetchAndCacheFeedImages(
53+
crawlFeedsResult.feeds,
54+
crawlFeedsResult.feedItems,
55+
ogObjectMap,
56+
FETCH_IMAGE_CONCURRENCY,
57+
);
58+
59+
// まとめフィード作成
5160
const generateFeedsResult = feedGenerator.generateFeeds(
5261
crawlFeedsResult.feedItems,
5362
ogObjectMap,
5463
crawlFeedsResult.feedItemHatenaCountMap,
5564
MAX_FEED_DESCRIPTION_LENGTH,
5665
MAX_FEED_CONTENT_LENGTH,
66+
thumbnailUrlMap,
5767
);
5868

5969
// まとめフィードのバリデーション。エラーならすぐに終了する
@@ -70,12 +80,4 @@ const feedImagePrecacher = new FeedImagePrecacher();
7080
crawlFeedsResult.feedItemHatenaCountMap,
7181
STORE_BLOG_FEEDS_DIR_PATH,
7282
);
73-
74-
// 画像の事前キャッシュ
75-
await feedImagePrecacher.fetchAndCacheFeedImages(
76-
crawlFeedsResult.feeds,
77-
crawlFeedsResult.feedItems,
78-
ogObjectMap,
79-
FETCH_IMAGE_CONCURRENCY,
80-
);
8183
})();

src/feed/utils/feed-generator.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Feed, FeedOptions } from 'feed';
22
import { CustomRssParserItem, FeedItemHatenaCountMap, OgObjectMap } from './feed-crawler';
3+
import { ThumbnailUrlMap } from './feed-image-precacher';
34
import { escapeTextForXml, textToMd5Hash, textTruncate } from './common-util';
45
import { logger } from './logger';
56
import * as constants from '../../common/constants';
@@ -22,13 +23,15 @@ export class FeedGenerator {
2223
allFeedItemHatenaCountMap: FeedItemHatenaCountMap,
2324
maxFeedDescriptionLength: number,
2425
maxFeedContentLength: number,
26+
thumbnailUrlMap: ThumbnailUrlMap = new Map(),
2527
): GenerateFeedResult {
2628
const aggregatedFeed = this.generateAggregatedFeed(
2729
feedItems,
2830
feedItemOgObjectMap,
2931
allFeedItemHatenaCountMap,
3032
maxFeedDescriptionLength,
3133
maxFeedContentLength,
34+
thumbnailUrlMap,
3235
);
3336

3437
return {
@@ -48,6 +51,7 @@ export class FeedGenerator {
4851
allFeedItemHatenaCountMap: FeedItemHatenaCountMap,
4952
maxFeedDescriptionLength: number,
5053
maxFeedContentLength: number,
54+
thumbnailUrlMap: ThumbnailUrlMap,
5155
): Feed {
5256
const outputFeed = new Feed({
5357
title: constants.feedTitle,
@@ -72,6 +76,7 @@ export class FeedGenerator {
7276
const ogObject = feedItemOgObjectMap.get(feedItem.link);
7377
// 配列担っているが2つ目以降を使う理由もないので0を使う
7478
const ogImage = ogObject?.customOgImage;
79+
const feedThumbnailUrl = ogImage?.url ? thumbnailUrlMap.get(ogImage.url) : undefined;
7580

7681
// 日付がないものは入れない
7782
if (!feedItem.isoDate) {
@@ -113,6 +118,7 @@ export class FeedGenerator {
113118
blogLink: feedItem.blogLink,
114119
blogLinkMd5Hash: textToMd5Hash(feedItem.blogLink),
115120
favicon: ogObject?.favicon,
121+
feedThumbnailUrl: feedThumbnailUrl || '',
116122
},
117123
},
118124
],

src/feed/utils/feed-image-precacher.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,52 @@ import { logger } from './logger';
33
import { imageCacheOptions } from '../../common/eleventy-cache-option';
44
import { to } from 'await-to-js';
55
import { CustomRssParserFeed, CustomRssParserItem, OgObjectMap } from './feed-crawler';
6-
import EleventyFetch from '@11ty/eleventy-fetch';
6+
import EleventyImage from '@11ty/eleventy-img';
7+
import constants from '../../common/constants';
8+
9+
const THUMBNAIL_OUTPUT_DIR = 'public/images/feed-thumbnails';
10+
const THUMBNAIL_URL_PATH = `${constants.siteUrlStem}/images/feed-thumbnails/`;
11+
12+
export type ThumbnailUrlMap = Map<string, string>;
713

814
export class ImagePrecacher {
915
public async fetchAndCacheFeedImages(
1016
feeds: CustomRssParserFeed[],
1117
feedItems: CustomRssParserItem[],
1218
ogObjectMap: OgObjectMap,
1319
concurrency: number,
14-
): Promise<void> {
20+
): Promise<ThumbnailUrlMap> {
21+
const thumbnailUrlMap: ThumbnailUrlMap = new Map();
1522
let ogImageUrls: string[] = [];
1623

1724
for (const feed of feeds) {
18-
ogImageUrls.push(ogObjectMap.get(feed.link)?.customOgImage?.url || '');
25+
const url = ogObjectMap.get(feed.link)?.customOgImage?.url;
26+
if (url) ogImageUrls.push(url);
1927
}
2028

2129
for (const feedItem of feedItems) {
22-
ogImageUrls.push(ogObjectMap.get(feedItem.link)?.customOgImage?.url || '');
30+
const url = ogObjectMap.get(feedItem.link)?.customOgImage?.url;
31+
if (url) ogImageUrls.push(url);
2332
}
2433

25-
// フィルタ
26-
ogImageUrls = ogImageUrls.filter(Boolean);
34+
// 重複排除
35+
ogImageUrls = [...new Set(ogImageUrls)];
2736

28-
// 画像取得
2937
const ogImageUrlsLength = ogImageUrls.length;
3038
let fetchProcessCounter = 1;
3139

3240
await PromisePool.for(ogImageUrls)
3341
.withConcurrency(concurrency)
3442
.process(async (ogImageUrl) => {
35-
const [error] = await to(
36-
EleventyFetch(ogImageUrl, {
37-
type: 'buffer',
38-
duration: imageCacheOptions.duration,
39-
concurrency,
43+
const [error, metadata] = await to<EleventyImage.Metadata>(
44+
EleventyImage(ogImageUrl, {
45+
widths: [150, 450],
46+
formats: ['webp', 'jpeg'],
47+
outputDir: THUMBNAIL_OUTPUT_DIR,
48+
urlPath: THUMBNAIL_URL_PATH,
49+
cacheOptions: imageCacheOptions,
50+
sharpWebpOptions: { quality: 50 },
51+
sharpJpegOptions: { quality: 70 },
4052
}),
4153
);
4254
if (error) {
@@ -45,9 +57,17 @@ export class ImagePrecacher {
4557
return;
4658
}
4759

60+
// 最大サイズのjpegをfeed用サムネイルURLとして登録
61+
const jpegImages = metadata.jpeg;
62+
const largestJpeg = jpegImages?.[jpegImages.length - 1];
63+
if (largestJpeg?.url) {
64+
thumbnailUrlMap.set(ogImageUrl, largestJpeg.url);
65+
}
4866
logger.info('[cache-og-image] fetched', `${fetchProcessCounter++}/${ogImageUrlsLength}`, ogImageUrl);
4967
});
5068

5169
logger.info('[cache-og-image] finished');
70+
71+
return thumbnailUrlMap;
5272
}
5373
}

0 commit comments

Comments
 (0)