Hexoで複数RSSに対応する

  1. 🏷️
  2. プログラミング
  3. Hexo
  4. RSS
  5. Hexo時代の記事
No hero image

この記事はHexo時代の記事です。 現在のブログとは見た目や機能が異なる可能性があります。

2021/09/12追記

今は違う方法を使ってる。↓を参照。


フィギュア記事に興味がある人向けに専用のRSSを用意したい❗

ということで、HexoでRSSフィードを複数用意できるようにした。

  • tagに フィギュア がついてる記事のRSS: https://tenpamk2-blog.netlify.app/atom_figure.xml
  • 2023-04-30追記: Gatsbyでは https://tenpamk2-blog.netlify.app/tags/フィギュア/rss.xml
  • tagに フィギュアレビュー がついてる記事のRSS: https://tenpamk2-blog.netlify.app/atom_figure_review.xml
  • 2023-04-30追記: Gatsbyでは廃止

画面右上のアイコンからもどうぞ

フィギュア全般用とフィギュアレビュー専用の2つを追加した。 お好みのRSSリーダーに突っ込めば、フィギュア記事だけを追えるぞ❗

例によって、hexoの修正手順などは長くなったので、記事は折りたたみ。

方針としては、既存の hexo-generator-feed のスクリプトを丸パクリして、ちょびっとだけ変更を加える。

解説は記事末尾。

手順

前の記事↓とだいたい一緒。scriptを置いて、設定なりをいじる。

↓のscriptをthemes/***/scripts/add_custom_rss.jsとして設置。 scriptsフォルダに置いたjsファイルはhexoのgenerate時に自動で実行される。

// Original code from ↓
//
// - <https://github.com/hexojs/hexo-generator-feed/blob/master/index.js>
// - <https://github.com/hexojs/hexo-generator-feed/blob/master/lib/generator.js>
const feedFn = require("./add_custom_rss_lib/generator");

hexo.config.feed = Object.assign(
  {
    type: "atom",
    limit: 20,
    hub: "",
    content: true,
    content_limit: 140,
    content_limit_delim: "",
    order_by: "-date",
    autodiscovery: true,
    template: "",
  },
  hexo.config.feed
);

hexo.config.feed = Object.assign(
  {
    figure_atom_path: "",
    figure_review_atom_path: "",
  },
  hexo.config.add_custom_rss
);

if (!hexo.config.feed.figure_atom_path) {
  // error
  return;
}

if (!hexo.config.feed.figure_review_atom_path) {
  // error
  return;
}

const filterNonFigure = (post) => {
  const figRevTag = post.tags.toArray().filter((tag) => {
    return tag.name === "フィギュア";
  });

  return 1 <= figRevTag.length;
};

const filterNonFigureReview = (post) => {
  const figRevTag = post.tags.toArray().filter((tag) => {
    return tag.name === "フィギュア" || tag.name === "レビュー";
  });

  return 2 <= figRevTag.length;
};

hexo.extend.generator.register("atom_figure", (locals) => {
  return feedFn.call(
    hexo,
    locals,
    "atom",
    hexo.config.feed.figure_atom_path,
    filterNonFigure
  );
});

hexo.extend.generator.register("atom_figure_review", (locals) => {
  return feedFn.call(
    hexo,
    locals,
    "atom",
    hexo.config.feed.figure_review_atom_path,
    filterNonFigureReview
  );
});

追加で、↓のscriptをthemes/***/scripts/add_custom_rss_lib/generator.jsとして設置。

// Original code from ↓
//
// - <https://github.com/hexojs/hexo-generator-feed/blob/master/index.js>
// - <https://github.com/hexojs/hexo-generator-feed/blob/master/lib/generator.js>

"use strict";

const nunjucks = require("nunjucks");
const { join } = require("path");
const { readFileSync } = require("fs");
const { encodeURL, gravatar, full_url_for } = require("hexo-util");

const env = new nunjucks.Environment();

env.addFilter("uriencode", (str) => {
  return encodeURL(str);
});

env.addFilter("noControlChars", (str) => {
  return str.replace(/[\x00-\x1F\x7F]/g, ""); // eslint-disable-line no-control-regex
});

module.exports = function (locals, type, path, filterFunc) {
  const { config } = this;
  const { email, feed, url: urlCfg } = config;
  const {
    icon: iconCfg,
    limit,
    order_by,
    template: templateCfg,
    type: typeCfg,
  } = feed;

  env.addFilter("formatUrl", (str) => {
    return full_url_for.call(this, str);
  });

  let tmplSrc = join(
    __dirname,
    `../../../../node_modules/hexo-generator-feed/atom.xml`
  );

  if (templateCfg) {
    if (typeof templateCfg === "string") tmplSrc = templateCfg;
    else tmplSrc = templateCfg[typeCfg.indexOf(type)];
  }

  const template = nunjucks.compile(readFileSync(tmplSrc, "utf8"), env);

  let posts = locals.posts.sort(order_by || "-date");

  posts = posts.filter((post) => {
    return post.draft !== true;
  });

  // filter by custom-filter
  posts = posts.filter(filterFunc);

  if (posts.length <= 0) {
    feed.autodiscovery = false;
    return;
  }

  if (limit) posts = posts.limit(limit);

  let url = urlCfg;
  if (url[url.length - 1] !== "/") url += "/";

  let icon = "";
  if (iconCfg) icon = full_url_for.call(this, iconCfg);
  else if (email) icon = gravatar(email);

  const feed_url = full_url_for.call(this, path);

  const data = template.render({
    config,
    url,
    icon,
    posts,
    feed_url,
  });

  return {
    path,
    data,
  };
};

/_config.ymlもいじる。↓の設定を追加。

add_custom_rss:
  figure_atom_path: "atom_figure.xml"
  figure_review_atom_path: "atom_figure_review.xml"

/themes/landscape/_config.ymlもいじる。 ↓を追加。/_config.ymlの記述とズレないように注意。

# Custom RSS
## See `/_config.yml`.
rss_figure: /atom_figure.xml
rss_figure_review: /atom_figure_review.xml

head.ejsを↓のようにする。 theme.rssが既存部分。theme.rss_figuretheme.rss_figure_reviewが追加部分ね。

  <% if (theme.rss){ %>
    <link rel="alternate" href="<%= url_for(theme.rss) %>" title="<%= config.title %>" type="application/atom+xml">
  <% } %>
  <% if (theme.rss_figure){ %>
    <link rel="alternate" href="<%= url_for(theme.rss_figure) %>" title="<%= config.title + ' tag_figure' %>" type="application/atom+xml">
  <% } %>
  <% if (theme.rss_figure_review){ %>
    <link rel="alternate" href="<%= url_for(theme.rss_figure_review) %>" title="<%= config.title + ' tag_figure-review' %>" type="application/atom+xml">
  <% } %>

同じノリでheader.ejsも↓のようにする。

<% if (theme.rss){ %>
  <a id="nav-rss-link" class="nav-icon" href="<%- url_for(theme.rss) %>" title="<%= __('rss_feed') %>"></a>
<% } %>
<% if (theme.rss_figure){ %>
  <a id="nav-rss-figure-link" class="nav-icon" href="<%- url_for(theme.rss_figure) %>" title="<%= __('rss_figure_feed') %>"></a>
<% } %>
<% if (theme.rss_figure_review){ %>
  <a id="nav-rss-figure-review-link" class="nav-icon" href="<%- url_for(theme.rss_figure_review) %>" title="<%= __('rss_figure_review_feed') %>"></a>
<% } %>

header.stylを↓のようにする。

#nav-rss-figure-link
  &:before
    content: "\f143"

#nav-rss-figure-review-link
  &:before
    content: "\f143"

各言語用のキーワード名定義ファイルもいじる。 ja.ymlに↓を追加する。他の言語用のymlファイルはお好みで。

rss_figure_feed: RSSフィード(フィギュア)
rss_figure_review_feed: RSSフィード(フィギュアレビュー)

最後に、clean&generateすればOK。 stylをいじったので、cleanが必須。注意。

解説と蛇足

add_custom_rss.jsgenerator.jsのざっくり設計解説

  • add_custom_rss.jsは本家様と結構変えている
    • 設定ファイルからRSSのファイル名を取り出せるように。
    • フィルタリング用の関数はここで定義
      • 定義だけして、実際のフィルタリングはgenerator.jsで実施
  • generator.jsは本家様とほぼ同じ
    • 好きにフィルタリングできるよう、フィルタリング関数を受け取れるようにした
    • テンプレート用atom.xmlnode_module/hexo-generator-feed/の直下を直接見るように

header.stylのざっくり解説

いじる理由は、RSSフィードへのリンクのアイコンを設定するため。

なお、全記事用RSSとフィギュア用RSSとで区別できるようにした。 "_f09e"が全記事用、"_f143"がフィギュア用。 アイコンの絵面は Awesome Fontの公式ページ で調べられる。

その他

hexo.extend.generator.register("atom_figure" ..."atom_figure"の文字列は好きに決めて良いようだ。 てっきり、特定のキーワードに反応して関数実行するのかと思ったが、違うみたい。

Bio
Bio icon

tenpaMk2

🇯🇵インドアクソオタク。

Tag cloud
  1. AI生成
  2. ALTER
  3. ANIPLEX
  4. Astro
  5. AZT8-45
  6. A列車で行こう
  7. Borderline Toybox
  8. BROCCOLI
  9. Building Gadgets MOD
  10. Chart.js
  11. cocoen-js
  12. Create MOD
  13. DDDy
  14. Excalibur.js
  15. Fate
  16. Fate/Apocrypha
  17. Fate/EXTELLA
  18. Fate/EXTRA CCC
  19. Fate/stay night
  20. FGO
  21. FLARE
  22. FREEing
  23. GameDev
  24. Gatsby
  25. Gatsby Theme
  26. Git
  27. Google Analytics
  28. Hexo
  29. Hexo時代の記事
  30. HOBBY STOCK
  31. hololive
  32. homebrew
  33. JavaScript
  34. KDcolle
  35. KDColle
  36. Kon'D
  37. Lightroom
  38. Mac
  39. Markdown
  40. Minecraft
  41. MVStudio
  42. NEKOPARA
  43. NEKOYOME
  44. Node.js
  45. npm
  46. NSFW
  47. Phat!
  48. PLUM
  49. plusone
  50. POP UP PARADE
  51. PSO
  52. quesQ
  53. RAW現像
  54. Re:ゼロから始める異世界生活
  55. React
  56. RSS
  57. SAO
  58. spiritale
  59. SSG
  60. Stable Diffusion
  61. Tailwind CSS
  62. Thermal MOD
  63. To LOVEる
  64. Tony
  65. Twitter
  66. TypeScript
  67. Vite
  68. VOCALOID
  69. VSCode
  70. WebP
  71. Wonderful Works
  72. zsh
  73. ご注文はうさぎですか?
  74. はじまるA列車
  75. はにかみ、彼女は恋をする
  76. ひげを剃る。そして女子高生を拾う。
  77. りゅうおうのおしごと
  78. アクアマリン
  79. アストルフォ
  80. アズキ
  81. アズールレーン
  82. アトリエシリーズ
  83. アリスグリント
  84. アルトリア オルタ
  85. イリヤスフィール・フォン・アインツベルン
  86. イージーエイト
  87. ウイング
  88. エリス・ボレアス・グレイラット
  89. エルドール
  90. オーキッドシード
  91. キャスター
  92. クドわふたー
  93. クロエ・フォン・アインツベルン
  94. グッドスマイルカンパニー
  95. グリザイア:ファントムトリガー
  96. ゲーム
  97. ココナツ
  98. コトブキヤ
  99. ゴールデンヘッドプラス
  100. サキちゃん
  101. ショコラ
  102. ジェネ
  103. ジャベリン
  104. ジャンヌ・ダルク
  105. ステファニー・ドーラ
  106. ストロンガー
  107. ソフィーのアトリエ
  108. ソードアート・オンライン
  109. ダイキ工業
  110. チノ
  111. チノちゃん
  112. チョコメロ
  113. ティアーユ・ルナティーク
  114. ディ・モールト ベネ
  115. デート・ア・ライブ
  116. ドール
  117. ドールグッズ紹介
  118. ナナ・アスタ・デビルーク
  119. ネコぱら
  120. ネロ・クラウディウス
  121. ノーゲーム・ノーライフ
  122. バニラ
  123. バニー
  124. フィギュア
  125. フィギュアレビュー
  126. ブログ
  127. プラスワン
  128. プラフタ
  129. プリズマ☆イリヤ
  130. プログラミング
  131. マシュ・キリエライト
  132. マックスファクトリー
  133. マヤ
  134. ミーア
  135. ミーシャ・ネクロン
  136. メグ
  137. メディコス・エンタテインメント
  138. モモ・ベリア・デビルーク
  139. モンスター娘のいる日常
  140. ユニオンクリエイティブ
  141. ライザのアトリエ
  142. ライザリン・シュタウト
  143. ララ・サタリン・デビルーク
  144. リューノス
  145. レム
  146. レーシング
  147. 五島潤
  148. 冴えない彼女の育てかた
  149. 初音ミク
  150. 加藤恵
  151. 喵屋
  152. 天使の3P!
  153. 巡音ルカ
  154. 時崎狂三
  155. 東京フィギュア
  156. 水澄華実
  157. 氷堂美智留
  158. 深見玲奈
  159. 無職転生
  160. 玉藻の前
  161. 画像比較スライダー
  162. 白銀ノエル
  163. 百瀬くるみ
  164. 空銀子
  165. 結城明日奈
  166. 結城美柑
  167. 美咲詩絵
  168. 美遊・エーデルフェルト
  169. 能美クドリャフカ
  170. 花偶堂
  171. 荻原沙優
  172. 西連寺春菜
  173. 遠坂凛
  174. 雛鶴あい
  175. 霞ヶ丘詩羽
  176. 魔王学院の不適合者
  177. 黒咲芽亜