メインコンテンツへスキップ

IT

Ghostty

Ghosttyはクロスプラットフォームのターミナルエミュレーター。動作が軽くカスタマイズ性が高い。 Macへのインストールと設定 # brewでインストールした。 brew install --cask ghostty 各種の設定はGUIではなくconfigファイルで行う。 ~/Library/Application Support/com.mitchellh.ghostty/config 設定ファイルをターミナル内から再読み込みするのはcmd+shift+, ターミナルから以下のコマンドを入力すると、使用できる(インストールされている)フォントファミリのリストが表示される。 ghostty +list-fonts 以下のコマンドでインストールされているテーマのリストが表示される。 ghostty +list-themes このようにプレビューを表示しながらテーマを選ぶことができる。 現時点での設定ファイルは以下の通り。 theme = "Dracula" font-family = "UDEV Gothic" font-thicken font-size = 20 mouse-hide-while-typing window-theme = dark background-opacity = 0.8 background-blur = true macos-titlebar-style = transparent window-inherit-working-directory = true window-save-state = always 設定可能な項目の説明はConfigurationにある。かなりの数があるが、自分は必要最低限のものに絞った。 Alfred関連の設定 # デフォルトで使用するターミナルをGhosttyに変更するため、Alfredの設定変更とワークフローの修正を行った。 Teminalの設定変更 # 以下のGhosttyとの連携スクリプトを手順に従って設定する。 zeitlings/alfred-ghostty-script: AppleScript for Ghostty Alfred integration terminalfinderワークフローの修正 # ファインダとターミナルを行き来するのに使っているAlfredのtermmialfinderワークフローにGhostty用のトリガーとスクリプトを追加した。

ObsidianのVimモードでNORMALモードに移行したらIMEをOFFにする(Mac)

ObsidianのVimモードでNORMALモードに移行した際、デフォルの動作ではIMEのON/OFFが変わらないため、日本語のテキストを書いているときに不便なので、im-select + Karabiner-Elementsを使ってEsc押下でIME OFFにする設定を追加した。 自分の環境では既にターミナルやGhostty用にその設定を入れているので、対象とするアプリとしてObisidianを追加するだけだった。 具体的にはbundle_identifiersに"^md\\.obsidian$"を追加した。 Obsidianのbundle_identifierは以下のようにして調べた。 osascript -e 'id of app "Obsidian"' md.obsidian 以下はKarabiner-Elementsに表示で入っている「TerminalまたはMacVimでESCあるいは^[を押したときに日本語入力を英数に切り替える」にGhosttyとObsidianを追加したもの。 { "description": "指定したアプリでESCあるいは^[を押したときに日本語入力を英数に切り替える。", "manipulators": [ { "conditions": [ { "bundle_identifiers": [ "^com\\.apple\\.Terminal$", "^org\\.vim\\.", "^com\\.mitchellh\\.ghostty$", "^md\\.obsidian$" ], "type": "frontmost_application_if" } ], "from": { "key_code": "escape", "modifiers": { "optional": ["any"] } }, "to": [ { "key_code": "japanese_eisuu" }, { "key_code": "escape" } ], "type": "basic" }, { "conditions": [ { "bundle_identifiers": [ "^com\\.apple\\.Terminal$", "^org\\.vim\\." ], "type": "frontmost_application_if" } ], "from": { "key_code": "open_bracket", "modifiers": { "mandatory": ["left_control"] } }, "to": [ { "key_code": "japanese_eisuu" }, { "key_code": "escape" } ], "type": "basic" } ] }

Elo Rating

イロレーティング(Elo rating) とは、対戦型の競技(2人のプレイヤーまたは2つのチームが対戦して勝敗を決めるタイプの競技)において、相対評価で実力を表すために使われる指標の一つ。数学的裏付けのある最も著名なレーティングシステムである。 イロレーティングは、もともとチェスの実力を表すために考案されたものだが、FIFAランキングや将棋や囲碁のオンライン対局場など様々な競技に応用されている。 イロレーティング - Wikipediaより 個人的 J-League ELO Ratings system レーティングについて | National Chess Society of Japan - NCS イロ・レーティングの意味と求め方を完全解説 | ワイズ サッカーファンでも意外と知らない!?最新FIFAランキングの決め方とは | Football Tribe Japan 二人対戦のプレイヤ強さを数値化したい - Thoth Children サッカーくじ totoの予想をしてみる。-レーティング計算- - Extrasensory Perception + ER + Blog

日付キーワードからフォーマットされた日付テキストを生成するAlfred Workflow

azooKeyという高精度なニューラルかな漢字変換エンジンをサポートしたiOS用キーボードアプリのmacOS版を試している。 ライブ変換もサポートしていて、標準のIMEより変換精度が高くて素晴らしい。問題なく常用できそうな感じ。 ただ、いくつかのIMEでサポートしている「きょう」で本日の日付に変換する機能がないんですよね。これはXでも話題になっていて、以下のような改善策も見つけました。 azooKey-Desktop で日付を入力する - terfnoのScrapbox しかし、個人的には「昨日」や「明日」「先週の金曜日」などの日付を得たいケースが多く、ChatGPTでAlfred用のワークフローを作成した。 動作イメージ # ワークフローを起動して、キーワードを入力すると対応するものがサジェストされる。 キーワードを決定すると、複数の日付フォーマットを選択できる。 対応キーワード # today:今日 yesterday:昨日 tomorrow:明日 dayaftertomorrow(略:dat):明後日 daybeforeyesterday(略:dby):一昨日 mon / tue / wed / thu / fri / sat / sun:各曜日 曜日を入力した場合は 「直近の過去の同曜日」「次に来る同曜日」を候補に表示 出力フォーマット # ISO形式:YYYY-MM-DD ISO + 曜日:YYYY-MM-DD(Fri) 日本語形式:YYYY年M月D日(金) 和暦形式(令和):令和X年M月D日(金) ダウンロード # 作成したワークフローを置いておきます。Alfred 5.7で動作確認しました。

IPコード

IPコード(Ingress Protection Code)は防塵と防水を示す国際規格。IPコードは「IP」の後に2つの数字が並び、以下を表す。 1桁目:防塵等級(固体異物に対する保護) 0〜6 の範囲で評価(6が完全防塵) 2桁目:防水等級(水に対する保護) 0〜9K の範囲で評価(9Kが最高等級:高圧・高温の水流にも耐える) 「X」が付く場合は、その桁の評価をしていない/規定していないことを意味する。 等級の例 等級 保護内容 使用イメージ IPX4 あらゆる方向からの水の飛沫に耐える 雨や水しぶきがかかる環境で使用可能。 例:ランニング中のイヤホン、キッチンで使うスピーカー IPX5 あらゆる方向からの噴流水に耐える(12.5L/分の水を3分以上) シャワーを直接かけても問題ない。 例:屋外スピーカー、アウトドア用機器 IPX7 一時的な水没(深さ1mで30分)に耐える 浴槽や水たまりに落としても問題ない(短時間なら)。 例:防水スマホ、アクションカメラ

radiko APIを使って都道府県ごとのラジオ局一覧を出力する

require 'net/http' require 'uri' require 'rexml/document' prefecture_names = { "1" => "北海道", "2" => "青森県", "3" => "岩手県", "4" => "宮城県", "5" => "秋田県", "6" => "山形県", "7" => "福島県", "8" => "茨城県", "9" => "栃木県", "10" => "群馬県", "11" => "埼玉県", "12" => "千葉県", "13" => "東京都", "14" => "神奈川県", "15" => "新潟県", "16" => "富山県", "17" => "石川県", "18" => "福井県", "19" => "山梨県", "20" => "長野県", "21" => "岐阜県", "22" => "静岡県", "23" => "愛知県", "24" => "三重県", "25" => "滋賀県", "26" => "京都府", "27" => "大阪府", "28" => "兵庫県", "29" => "奈良県", "30" => "和歌山県", "31" => "鳥取県", "32" => "島根県", "33" => "岡山県", "34" => "広島県", "35" => "山口県", "36" => "徳島県", "37" => "香川県", "38" => "愛媛県", "39" => "高知県", "40" => "福岡県", "41" => "佐賀県", "42" => "長崎県", "43" => "熊本県", "44" => "大分県", "45" => "宮崎県", "46" => "鹿児島県", "47" => "沖縄県" } prefectures = (1..47).map(&:to_s) prefectures.each do |pref_code| url = "https://radiko.jp/v2/station/list/JP#{pref_code}.xml" uri = URI.parse(url) begin response = Net::HTTP.get_response(uri) if response.is_a?(Net::HTTPSuccess) document = REXML::Document.new(response.body) puts "## #{prefecture_names[pref_code]}(#{pref_code})" document.elements.each('stations/station') do |station| station_name = station.elements['name'].text station_id = station.elements['id'].text puts "- #{station_name}(#{station_id})" end puts "" else puts "Failed to retrieve data for prefecture code #{pref_code}" end rescue => e puts "Error: #{e.message} for prefecture code #{pref_code}" end end 北海道(1) # HBCラジオ(HBC) STVラジオ(STV) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) AIR-G’(FM北海道)(AIR-G) FM NORTH WAVE(NORTHWAVE) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(札幌)(JOIK) NHK-FM(東京)(JOAK-FM) 青森県(2) # RAB青森放送(RAB) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム青森(AFB) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(仙台)(JOHK) NHK-FM(東京)(JOAK-FM) 岩手県(3) # IBCラジオ(IBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム岩手(FMI) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(仙台)(JOHK) NHK-FM(東京)(JOAK-FM) 宮城県(4) # TBCラジオ(TBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) Date fm エフエム仙台(DATEFM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(仙台)(JOHK) NHK-FM(東京)(JOAK-FM) 秋田県(5) # ABSラジオ(ABS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム秋田(AFM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(仙台)(JOHK) NHK-FM(東京)(JOAK-FM) 山形県(6) # YBC山形放送(YBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) Rhythm Station エフエム山形(RFM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(仙台)(JOHK) NHK-FM(東京)(JOAK-FM) 福島県(7) # RFCラジオ福島(RFC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) ふくしまFM(FMF) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(仙台)(JOHK) NHK-FM(東京)(JOAK-FM) 茨城県(8) # TBSラジオ(TBS) 文化放送(QRR) ニッポン放送(LFR) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) interfm(INT) TOKYO FM(FMT) J-WAVE(FMJ) LuckyFM 茨城放送(IBS) ラジオ日本(JORF) BAYFM78(BAYFM78) NACK5(NACK5) FMヨコハマ(YFM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 栃木県(9) # TBSラジオ(TBS) 文化放送(QRR) ニッポン放送(LFR) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) interfm(INT) TOKYO FM(FMT) J-WAVE(FMJ) CRT栃木放送(CRT) RADIO BERRY(RADIOBERRY) ラジオ日本(JORF) BAYFM78(BAYFM78) NACK5(NACK5) FMヨコハマ(YFM) LuckyFM 茨城放送(IBS) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 群馬県(10) # TBSラジオ(TBS) 文化放送(QRR) ニッポン放送(LFR) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) interfm(INT) TOKYO FM(FMT) J-WAVE(FMJ) FM GUNMA(FMGUNMA) ラジオ日本(JORF) BAYFM78(BAYFM78) NACK5(NACK5) FMヨコハマ(YFM) LuckyFM 茨城放送(IBS) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 埼玉県(11) # TBSラジオ(TBS) 文化放送(QRR) ニッポン放送(LFR) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) interfm(INT) TOKYO FM(FMT) J-WAVE(FMJ) ラジオ日本(JORF) BAYFM78(BAYFM78) NACK5(NACK5) FMヨコハマ(YFM) LuckyFM 茨城放送(IBS) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 千葉県(12) # TBSラジオ(TBS) 文化放送(QRR) ニッポン放送(LFR) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) interfm(INT) TOKYO FM(FMT) J-WAVE(FMJ) ラジオ日本(JORF) BAYFM78(BAYFM78) NACK5(NACK5) FMヨコハマ(YFM) LuckyFM 茨城放送(IBS) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 東京都(13) # TBSラジオ(TBS) 文化放送(QRR) ニッポン放送(LFR) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) interfm(INT) TOKYO FM(FMT) J-WAVE(FMJ) ラジオ日本(JORF) BAYFM78(BAYFM78) NACK5(NACK5) FMヨコハマ(YFM) LuckyFM 茨城放送(IBS) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 神奈川県(14) # TBSラジオ(TBS) 文化放送(QRR) ニッポン放送(LFR) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) interfm(INT) TOKYO FM(FMT) J-WAVE(FMJ) ラジオ日本(JORF) BAYFM78(BAYFM78) NACK5(NACK5) FMヨコハマ(YFM) LuckyFM 茨城放送(IBS) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 新潟県(15) # BSNラジオ(BSN) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM NIIGATA(FMNIIGATA) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 富山県(16) # KNBラジオ(KNB) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FMとやま(FMTOYAMA) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(名古屋)(JOCK) NHK-FM(東京)(JOAK-FM) 石川県(17) # MROラジオ(MRO) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム石川(HELLOFIVE) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(名古屋)(JOCK) NHK-FM(東京)(JOAK-FM) 福井県(18) # FBCラジオ(FBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM福井(FMFUKUI) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(名古屋)(JOCK) NHK-FM(東京)(JOAK-FM) 山梨県(19) # YBSラジオ(YBS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM FUJI(FM-FUJI) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 長野県(20) # SBCラジオ(SBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM長野(FMN) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(東京)(JOAK) NHK-FM(東京)(JOAK-FM) 岐阜県(21) # CBCラジオ(CBC) TOKAI RADIO(TOKAIRADIO) ぎふチャン(GBS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) ZIP-FM(ZIP-FM) FM GIFU(FMGIFU) FM AICHI(FMAICHI) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(名古屋)(JOCK) NHK-FM(東京)(JOAK-FM) 静岡県(22) # SBSラジオ(SBS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) K-MIX(K-MIX) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(名古屋)(JOCK) NHK-FM(東京)(JOAK-FM) 愛知県(23) # CBCラジオ(CBC) TOKAI RADIO(TOKAIRADIO) ぎふチャン(GBS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) ZIP-FM(ZIP-FM) FM AICHI(FMAICHI) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(名古屋)(JOCK) NHK-FM(東京)(JOAK-FM) 三重県(24) # CBCラジオ(CBC) TOKAI RADIO(TOKAIRADIO) ぎふチャン(GBS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) ZIP-FM(ZIP-FM) レディオキューブ FM三重(FMMIE) FM AICHI(FMAICHI) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(名古屋)(JOCK) NHK-FM(東京)(JOAK-FM) 滋賀県(25) # ABCラジオ(ABC) MBSラジオ(MBS) OBCラジオ大阪(OBC) FM COCOLO(CCL) FM802(802) FM大阪(FMO) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) KBS京都ラジオ(KBS) Kiss FM KOBE(KISSFMKOBE) e-radio FM滋賀(E-RADIO) ラジオ関西(CRK) α-STATION FM KYOTO(ALPHA-STATION) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(大阪)(JOBK) NHK-FM(東京)(JOAK-FM) 京都府(26) # ABCラジオ(ABC) MBSラジオ(MBS) OBCラジオ大阪(OBC) FM COCOLO(CCL) FM802(802) FM大阪(FMO) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) KBS京都ラジオ(KBS) Kiss FM KOBE(KISSFMKOBE) α-STATION FM KYOTO(ALPHA-STATION) ラジオ関西(CRK) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(大阪)(JOBK) NHK-FM(東京)(JOAK-FM) 大阪府(27) # ABCラジオ(ABC) MBSラジオ(MBS) OBCラジオ大阪(OBC) FM COCOLO(CCL) FM802(802) FM大阪(FMO) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) Kiss FM KOBE(KISSFMKOBE) ラジオ関西(CRK) KBS京都ラジオ(KBS) α-STATION FM KYOTO(ALPHA-STATION) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(大阪)(JOBK) NHK-FM(東京)(JOAK-FM) 兵庫県(28) # ABCラジオ(ABC) MBSラジオ(MBS) OBCラジオ大阪(OBC) FM COCOLO(CCL) FM802(802) FM大阪(FMO) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) ラジオ関西(CRK) Kiss FM KOBE(KISSFMKOBE) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(大阪)(JOBK) NHK-FM(東京)(JOAK-FM) 奈良県(29) # ABCラジオ(ABC) MBSラジオ(MBS) OBCラジオ大阪(OBC) FM COCOLO(CCL) FM802(802) FM大阪(FMO) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) Kiss FM KOBE(KISSFMKOBE) ラジオ関西(CRK) α-STATION FM KYOTO(ALPHA-STATION) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(大阪)(JOBK) NHK-FM(東京)(JOAK-FM) 和歌山県(30) # ABCラジオ(ABC) MBSラジオ(MBS) OBCラジオ大阪(OBC) FM COCOLO(CCL) FM802(802) FM大阪(FMO) wbs和歌山放送(WBS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) Kiss FM KOBE(KISSFMKOBE) ラジオ関西(CRK) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(大阪)(JOBK) NHK-FM(東京)(JOAK-FM) 鳥取県(31) # BSSラジオ(BSS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム山陰(FM-SANIN) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(広島)(JOFK) NHK-FM(東京)(JOAK-FM) 島根県(32) # BSSラジオ(BSS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム山陰(FM-SANIN) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(広島)(JOFK) NHK-FM(東京)(JOAK-FM) 岡山県(33) # RSKラジオ(RSK) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM岡山(FM-OKAYAMA) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(広島)(JOFK) NHK-FM(東京)(JOAK-FM) 広島県(34) # RCCラジオ(RCC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) 広島FM(HFM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(広島)(JOFK) NHK-FM(東京)(JOAK-FM) 山口県(35) # KRY山口放送(KRY) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム山口(FMY) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(広島)(JOFK) NHK-FM(東京)(JOAK-FM) 徳島県(36) # JRT四国放送(JRT) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM徳島(FM807) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(松山)(JOZK) NHK-FM(東京)(JOAK-FM) 香川県(37) # RNC西日本放送(RNC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム香川(FMKAGAWA) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(松山)(JOZK) NHK-FM(東京)(JOAK-FM) 愛媛県(38) # RNB南海放送(RNB) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM愛媛(JOEU-FM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(松山)(JOZK) NHK-FM(東京)(JOAK-FM) 高知県(39) # RKC高知放送(RKC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム高知(HI-SIX) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(松山)(JOZK) NHK-FM(東京)(JOAK-FM) 福岡県(40) # RKBラジオ(RKB) KBCラジオ(KBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) LOVE FM(LOVEFM) CROSS FM(CROSSFM) FM FUKUOKA(FMFUKUOKA) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM) 佐賀県(41) # ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム佐賀(FMS) NBCラジオ(NBC) RKBラジオ(RKB) KBCラジオ(KBC) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM) 長崎県(42) # NBCラジオ(NBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM長崎(FMNAGASAKI) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM) 熊本県(43) # RKKラジオ(RKK) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FMKエフエム熊本(FMK) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM) 大分県(44) # OBSラジオ(OBS) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム大分(FM_OITA) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM) 宮崎県(45) # 宮崎放送(MRT) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) エフエム宮崎(JOYFM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM) 鹿児島県(46) # MBCラジオ(MBC) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) μFM(MYUFM) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM) 沖縄県(47) # RBCiラジオ(RBC) ラジオ沖縄(ROK) ラジオNIKKEI第1(RN1) ラジオNIKKEI第2(RN2) FM沖縄(FM_OKINAWA) オーディオ高校野球(ABCR-KOSHIEN) NHKラジオ第1(福岡)(JOLK) NHK-FM(東京)(JOAK-FM)

Kindleの蔵書一覧を生成するRubyスクリプト

MacにインストールされているKindleアプリのデータから蔵書一覧を生成 kindle_books_mac.rbrequire 'rexml/document' require 'date' require 'csv' # Kindle for Mac KINDLE_XML = "#{ENV['HOME']}/Library/Containers/com.amazon.Kindle/Data/Library/Application Support/Kindle/Cache/KindleSyncMetadataCache.xml" doc = REXML::Document.new(File.read(KINDLE_XML)) csv = CSV.generate(force_quotes: true) do |csv| doc.elements.each("//response/add_update_list/meta_data") do |e| csv << [ e.elements["ASIN"].text, e.elements["title"].text, e.elements["authors"].map{|item| item.text}.join('/'), e.elements["publishers"].map{|item| item.text}.join('/'), e.elements["publication_date"].text, e.elements["purchase_date"].text ] end end puts csv

話した内容をテキスト化しTextwellへ追記するショートカット

このエントリは生成AIで書いたものです このテキストをClaudeでブログ用にリライトしてくださいと指示しました。 「月に数百件のメモが溜まる」「入力速度はキーボード入力と比較して約3倍に」など書いてないこともシレッと入れてくるので、書き手としても読み手としても注意が必要です😅 これとは別に、音声入力したテキストをClaudeに投げて構造化、Markdown化するショートカットも作っていますが、テキスト化に時間がかかり、気軽にメモをする感じではないので用途によって使い分けしています。 iPhoneの音声入力を劇的に効率化する自作ショートカット # スマートフォンでの文字入力に悩まされていませんか?

syslogのメッセージ重大度

RFC 5424 - The Syslog Protocol Level Description debug 詳細なデバッグ情報 info 一般的な情報メッセージ notice 正常だが重要なイベント warning 警告条件 error エラー条件 critical 危機的な状況 alert 直ちに行動を起こさなければならない emergency システムが使用できない

PopClip for Macのエクステンション・スニペット

スニペット # Mac用のツールPopClip for Macの自作スニペット。スニペット全体を選択すればPopClipのメニューからインストールできる。 アイコンの指定方法はIcons — PopClip Developerにある。 物書堂の辞書を検索する # #popclip extension to search MkDictionaries name: MkDictionaries icon: square filled 物 url: mkdictionaries:///?text=*** Googleのカスタム検索 # 検索エンジンIDは定義したカスタム検索のIDを指定する Programmable Search - すべての検索エンジン #popclip extension to Googleカスタム検索 name: Googleカスタム検索 icon: circle filled G url: https://cse.google.com/cse?cx=検索エンジンID#gsc.tab=0&gsc.q=***&gsc.sort= エクステンション # 全角→半角変換 # 選択文字列中の全角英数字・記号・カナを半角に変換する。たまにある半角文字しか受け付けないサイトなどで使う。 PopClip-Extensions/ZenHan.popclipext at main · htakeuchi/PopClip-Extensions 半角→全角変換 # 選択文字列中の半角英数字・記号・カナを全角に変換する。ついでなので逆変換もつくった。

todo.txtで始めるタスク管理

Remeber the Milk、Todoist、Appleのリマインダーなどを経て、ここ1〜2年はMicorosoft To Doを使ってタスク管理をしている。 私がタスク管理アプリに求めるものは、 期限管理できること 繰り返しタスク(毎週、毎月など)が使えること コンテキスト、プロジェクトなどでタスクをカテゴライズできること であり、有償のタスク管理サービスで提供される豊富な機能は、自分にとってオーバースペックで、機能的にはシンプルなMicrosoft To Doで必要十分だ。 しかし、データがサーバに保管されクローズドであり、かつエクスポートすらできない点がずっと気になっていた。 このため、テキストファイルを使ったタスク管理であるTodo.txtを使い始めることにした。 iOSではSwiftoDoを使うことにした。 標準でDropboxをサポートしており、ファイルアプリでアクセスできるようにしていれば、iCloudやGoodle Driveなどにもファイルを置けるが、私はDropboxへ置くことにした。 DropboxはGoogle DriveやiCloudなどと比較し同期が速く、この手の運用をするのであれば未だに優位性があると思う。 MacとWindowsではオープンソースのsleekを使いはじめた。 todo.txtの標準シンタクスには定義されていない、繰り返しタスクの記法(例 rec:1w)がSwiftoDoと共通なのが嬉しくUIも美しい。 ransome1/sleek: todo.txt manager for Linux, Windows and MacOS, free and open-source (FOSS) ただ、sleekには登録済みタスクのテキストを修正できない不具合があるっぽい。(2025/02/17現在 Macで再現) 2025/03/05追記 sleekは私の環境(Mac)では動作が不安定なため使用を止めた。

QuartzでExcalidrawで描いた図を使う運用

概要 # Obsidianの中だけでExcalidrawを使うのあれば、obsidian-excalidrawプラグインを使えば良い。 しかし、本サイトではObsidianの保管庫をQuartzでHTMLへ変換し公開しているため、 [image.excalidraw](image.excalidraw) のように書いてもQuartzでは上記形式が処理されず、せっかく描いた図をWeb上で公開できない。このため以下のように運用することにした。 Excalidraw Pluginを使って図を描き保存する Excalidrawファイルを作ったフォルダにPNGが自動生成される ノートを作成し2.のPNGを画像として参照する 上の図もこの流れで公開している。 Excalidrawプラグインの設定内容 # Basic # Excalidraw folder: _Excalidraw Excalidrawのファイルが保存されるフォルダ。PNGもここに出力される Filename # prefix: なし Excalidraw専用フォルダを作成するためprefixは不要 Embedding Excalidraw into your Notes and Exporting # Export Settings > Auto-export Settings > Auto-export PNG: ON quartz.config.tsの設定 # QuartzがExcalidrawのファイルを処理しないよう、quartz.config.tsのignorePatterns へ追加する。

MiniFlux

MiniFluxは、シンプルで軽量なオープンソースのRSSフィードリーダー。セルフホスティングも可能。Webベースのインターフェースを提供し、ブラウザからどのデバイスからでもアクセス可能である。 主な機能として広告や不要な要素を取り除く「クリーンな記事表示機能」、未読・既読の管理、スター機能(お気に入り登録)、タグ付け、キーボードショートカットのサポートなどがある。 Miniflux - Minimalist and Opinionated Feed Reader Screenshot of the unread page キーボードショートカット # セクションナビゲーション # キー アクション g u 未読ページへ移動 g b ブックマークページへ移動 g h 履歴ページへ g f フィードページへ移動 g c カテゴリページへ g s 設定ページへ移動 ? キーボードショートカットのヘルプを表示 アイテムナビゲーション # キー アクション p または k または ◄ 前の項目へ移動 n または j または ► 次の項目へ進む F フィードページへ移動 ページナビゲーション # キー アクション h 前のページへ戻る l 次のページへ アクション # キー アクション o または Enter 選択した項目を開く v 元のリンクを新しいタブで開く V 元のリンクを現在のタブに開く (エントリ ビューのみ) c コメントリンクを新しいタブで開く C コメントリンクを現在のタブに開く m 既読/未読を切り替え、次にフォーカス M 既読/未読を切り替え、前の項目にフォーカス A 現在のページを既読にする d 元のウェブページを取得し、スクレーパールールまたは読みやすさを適用 f 現在のエントリにスターを付けたり外したりする s 設定されたサードパーティのサービスに記事を保存する a エントリ添付ファイルを開く/閉じるを切り替える z t 項目を一番上までスクロール R バックグラウンドですべてのフィードを更新 # 現在のフィードを削除 / 検索フォームにフォーカスを設定する Escape モーダルダイアログを閉じる

iOSのファイルアプリからGoogle DriveやDropboxへアクセスする

iOSのファイルアプリからGoogle Driveのファイルへアクセスするための設定手順。 Google Driveアプリをインストールしてログインする ファイルアプリを開き以下の手順で追加する ブラウザタブ → 右上の…→編集 Google Driveをオンにする 完了をタップ 設定後、原因不明のエラーで表示できませんというメッセージが表示されたが、ファイルアプリ、Googleドライブアプリを終了後、再起動したら動いた。 Dropboxも同様の手順で設定できる。

Appleのヘルスケアデータから体重を抜き出してCSVファイルを作成する

Appleのヘルスケアアプリのデータを使って、他のアプリでグラフを描こうと思ったが、エクスポートされるデータはXMLのため、そのままでは扱いにくい。 このため、XMLファイルから体重データを抜き出してCSVを出力するRubyスクリプトを書いた。 require 'nokogiri' require 'date' reader = Nokogiri::XML::Reader File.read('export.xml') data = [] reader.each do |node| if node.name == 'Record' && node.attributes['type'] == 'HKQuantityTypeIdentifierBodyMass' d = Date.parse(node.attributes['startDate']).strftime("%Y-%m-%d") data << [d, node.attributes['value']] end end data.sort.each {|d| puts d.join(',') } =begin <Record type="HKQuantityTypeIdentifierBodyMass" sourceName="1byone Health" sourceVersion="63" device="&lt;&lt;HKDevice: 0x3013e9b30&gt;, name:iPhone, manufacturer:Apple, model:iPhone, hardware:iPhone, firmware:iPhone, software:17.5.1, localIdentifier:ja_JP, UDIDeviceIdentifier:ja_JP, creation date:2024-06-02 22:17:36 +0000&gt;" unit="kg" creationDate="2024-06-17 07:17:17 +0900" startDate="2024-06-17 07:17:17 +0900" endDate="2024-06-17 07:17:17 +0900" value="71.9"/> =end 以下、ChatGPTにヘルスケアデータの構造を聞いた回答 Appleのヘルスケアアプリでエクスポートされるデータは、XML (Extensible Markup Language) フォーマットで提供されます。このフォーマットは、ヘルスデータを構造化して格納するための標準的な形式です。 データの構造と内容 # XMLヘッダー エクスポートされたファイルは、XMLヘッダーから始まります。 <?xml version="1.0" encoding="UTF-8"?> 全体のルート要素 データ全体は <HealthData> というルート要素でラップされています。 <HealthData locale="en_US"> ... </HealthData> データの各エントリー ヘルスケアのデータは、それぞれの記録を <Record> 要素として格納します。主な属性は以下の通りです: type: データの種類(例: 歩数、心拍数など)。 sourceName: データのソース名(iPhone、Apple Watchなど)。 sourceVersion: ソースのバージョン情報。 unit: データの単位(例: count, bpm, kcal など)。 creationDate: データが作成された日時。 startDate: データの開始日時(測定期間の開始)。 endDate: データの終了日時(測定期間の終了)。 value: データの値(例: 歩数の数値、体重の値など)。 例:

URLスキームに対応していないアプリを限定的に対応させる

Kofax Power PDF for MacというURLスキームに対応していないアプリを対応させるためのメモ。このアプリ限定の手順ではないので他のアプリへも応用可能だと思います。 プロトコルハンドラ用のアプリケーション作成 # AppleScriptを使ってKofax Power PDF for MacでPDFファイルを開くスクリプトを作成する。スクリプトエディタで以下のスクリプトを入力。 on open location theURL set pdfPath to do shell script "echo " & quoted form of theURL & " | sed 's/kofaxpdf:\\/\\/open\\?file=//g'" if (do shell script "test -f " & quoted form of pdfPath & " && echo true || echo false") is "true" then do shell script "open -a 'Kofax Power PDF for Mac'.app " & quoted form of pdfPath else display dialog "ファイルが見つかりません: " & pdfPath buttons {"OK"} default button "OK" end if end open location このスクリプトはkofaxpdf://open?file=/path/to/file.pdfで呼び出さると、ファイルパスを抽出しそのファイルを指定してKofax Power PDF for Macを起動するもの。スクリプトを保存し、ファイルメニューの書き出すでアプリケーションとして書き出す。 このアプリケーションがカスタムプロトコルを処理することになる。 Info.plistの編集 # Finderで上記で作成したアプリケーションを右クリックしパッケージの内容を表示を選択する Content/Info.plistを開き以下の内容を追加する <key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLName</key> <string>MyApp Protocol</string> <key>CFBundleURLSchemes</key> <array> <string>kofaxpdf</string> </array> </dict> </array> これによりkofaxpdf://というカスタムプロトコルがこのアプリケーションに関連付けられる。ここでいちどこのアプリケーションを起動しておく。

デジタルインベーダーのようなゲームを作った

crisp-game-libという軽量ゲームライブラリを使って、昔なつかしいデジタルインベーダーのようなゲームを作った。下の画像をクリックすると実際に遊べます。 デジタルインベーダーはカシオから1980年に発売されたMG880という電卓に搭載されていたゲームで、右からせまってくる数字と同じ数字を合わせて消していくシンプルなもの。 表示された数字を早く消すほど得点が高い(10~60点)のだが、直近で消した2つの数字の合計が10になるとUFO(n表示)が登場し300点得られるため、高得点を目指すにはやみくもに早く消せば良いわけではないのです。 crisp-game-libは初めて使いましたが、割り切った仕様がシンプルでわかりやすかったです。こういうカジュアルなゲームを作るのは楽しいですね。 それにしても当時かなり遊んだ記憶があるのに、すっかり存在を忘れていたなぁ…と思ったら復刻していた。 カシオ ゲーム電卓 手帳タイプ 10桁 SL-880-N

crisp-game-lib

crisp-game-libはブラウザゲームを開発するための軽量なゲームライブラリ。JavaScriptでゲームを開発し簡単に公開することができる abagames/crisp-game-lib: Minimal JavaScript library for creating classic arcade-like mini-games running in the browser

BBCode

BBCode (Bulletin Board Code) は、掲示板やフォーラムなどのオンラインプラットフォームで、ユーザーが投稿内容を装飾するために使用するマークアップ言語。 HTMLと似た形式だがが、よりシンプルで安全な方法でテキストを装飾できるように設計されている。 BBCodeのタグは、角括弧 [ ] を使い、開始タグと終了タグで囲むことで効果を適用する。 タグ 説明 使用例 [b] 太字 [b]太字のテキスト[/b] [i] 斜体 [i]斜体のテキスト[/i] [u] 下線 [u]下線付きテキスト[/u] [s] 取り消し線 [s]取り消し線のテキスト[/s] [url] URLリンク [url]https://example.com[/url] [url=] テキストリンク [url=https://example.com]リンク[/url] [img] 画像を挿入 [img]https://example.com/image.jpg[/img] [quote] 引用 [quote]引用したいテキスト[/quote] [code] コードブロック [code]コード例[/code] [list] 順序なしリスト [list][*]アイテム1[*]アイテム2[/list] [list=1] 順序付きリスト [list=1][*]アイテム1[*]アイテム2[/list] [color=] テキストの色を指定 [color=red]赤いテキスト[/color] [size=] テキストサイズを指定 [size=20]大きいテキスト[/size] [font=] フォントを指定 [font=Arial]Arialフォントのテキスト[/font] [center] テキストを中央揃え [center]中央揃えのテキスト[/center] [right] テキストを右揃え [right]右揃えのテキスト[/right] [left] テキストを左揃え [left]左揃えのテキスト[/left] [email] メールアドレスをリンク [email]example@example.com[/email] [table] 表を作成 [table][tr][td]セル1[/td][td]セル2[/td][/tr][/table] [tr] 表の行を作成 [tr][td]セル1[/td][td]セル2[/td][/tr] [td] 表のセルを作成 [td]セル1[/td] [spoiler] ネタバレ部分を隠す [spoiler]隠された内容[/spoiler] [youtube] YouTube動画を埋め込む [youtube]動画ID[/youtube] [align=] テキストの配置を指定 [align=center]中央揃え[/align] [hr] 水平線を挿入 [hr]

ソートして重複行を削除するTextwellのアクション

ファイルの内容を昇順にソートして重複した行を1行にするTextwellのアクションを書いた。 いわゆる$ sort foobar | uniqするアクションです。 https://gist.github.com/htakeuchi/fd9e36227ad1688b31e9b84eafbf17a9 const { text, range } = T; const selectionStart = range.len > 0 ? range.loc : 0; const selectionEnd = range.len > 0 ? selectionStart + range.len : text.length; const lines = text.split('\n'); let pointerStart = 0; let replacingRangeLoc = 0; const hitLines = []; for (const line of lines) { const pointerEnd = pointerStart + line.length; if (pointerStart > selectionEnd) break; if ( (pointerStart <= selectionStart && selectionStart <= pointerEnd) || (pointerStart <= selectionEnd && selectionEnd <= pointerEnd) || (selectionStart < pointerStart && pointerEnd < selectionEnd) ) { if (hitLines.length === 0) replacingRangeLoc = pointerStart; hitLines.push(line); } pointerStart = pointerEnd + 1; // 1 means a line break. } const blankLines = []; const numLines = []; const strLines = []; hitLines.forEach((content) => { const intContent = parseInt(content); if (!content.match(/\S/)) { blankLines.push(content); // Ignore blank line } else if (isNaN(intContent)) { strLines.push(content); } else { numLines.push({ num: intContent, str: content }); } }); numLines.sort((a, b) => a.num - b.num); const sortedNumLines = numLines.map(({ str }) => str); strLines.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); const sortedLines = [...blankLines, ...sortedNumLines, ...strLines]; // Remove duplicates const uniqueLines = [...new Set(sortedLines)]; // Join lines into text const replacingText = uniqueLines.join('\n'); T('replaceRange', { text: replacingText, replacingRange: { loc: replacingRangeLoc, len: selectionEnd - replacingRangeLoc }, selectingRange: { loc: replacingRangeLoc + replacingText.length, len: 0 }, });

Obsidianの検索機能

Obsidianの標準の検索機能に関するメモ。検索 - Obsidian 日本語ヘルプ - Obsidian Publishより良く使うものを抜粋した。 ショートカットキー # Ctrl+Shift+Fまたは⌘+⇧+F 検索結果表示のオプション # 検索窓の右にあるボタンを押すと検索結果表示のオプションを設定できる。検索結果が多い場合などは、折りたたんでノート名であたりをつけたりするのに便利。 検索結果の埋め込み # ```query embed OR search ``` などとするとノート内に検索結果を埋め込むことができる。この機能はObsidian PublishでもQuartzでもサポートされていない。 検索クエリ # 検索条件 書きかた 説明 AND検索 foo bar fooとbarが含まれる OR検索 foo OR bar fooまたはbarが含まれる NOT検索 foo -bar fooを含むがbarを含まない foo スペースを含む文字列の検索 “foo bar” foo barという文字列を探す 特殊記号のエスケープ \" \\に続けて書くと通常の文字して検索 グループ化 ((a OR b) (c OR d)) 検索条件のグループ化 正規表現 /f../ //の中に正規表現を書く ファイル名検索 file:".jpg" ファイル名に.jpgを含むファイルを検索 ファイルパス検索 path:“journal/” ファイルパスにjournalを含むファイルを検索 タグ検索 tag:#mac タグに#macを含むノートを検索 大文字小文字を無視 ignore-case:(ABC) 大文字小文字を区別せずにABCを検索 大文字小文字を区別 match-case:(ABC) 大文字小文字を区別してABCを検索 ユースケース # 特定のディレクトリ配下を除外して検索 # 検索結果にノイズが含まれてしまうため、作業ログを置いているjournalというディレクトリは除外して検索したい。

Macのキーボード特殊記号の表記

Macで良く使う特殊記号をコピペ用に置いておく。それにしても、⌅↖︎↘︎⇟⇞なんて覚えられないよね。 記号 キー ⌘ commandキー ⌥ optionキー または altキー ⇧ shiftキー ⌃ controlキー ⌅ Enterキー ↵ Returnキー ⇥ Tabキー ⌦ deleteキー ⌫ forward deleteキー ⎋ escキー ↖︎ homeキー ↘︎ endキー ↑↓←→ カーソルキーの上下左右 ⇟ page downキー ⇞ page upキー

Amazonの商品紹介Markdownをつくるブックマークレット

このサイトはObsidianで管理しているノートをQuartzで公開しており、全てのテキストはMarkdownで書いている。 このため、Amazonの商品紹介をするのがなかなか面倒であり、これを改善するためのブックマークレットを書いた。 使い方 # Amazonの商品ページに行ってこのブックマークレットを起動すると、以下のようなMarkdownが生成され、クリップボードへコピーされる。 ![[オン] ランニングシューズ Cloudmonster メンズ](https://a.media-amazon.com/images/I/51lG1xvL7nL._AC_SY200_.jpg) [[オン] ランニングシューズ Cloudmonster メンズ](https://www.amazon.co.jp/gp/product/B0CN337TNH/?tag=namaraiicom-22) 出力はこんな感じ。殺風景だけどMarkdownだけで書いていて、専用のCSSを当てていないのでやむをえない。 [オン] ランニングシューズ Cloudmonster メンズ 実装 # 実装はこんな感じ。Amazon(JP)で複数ジャンルの商品ページのHTMLを確認し、チェックした範囲では動作しているが、うまく動かないページもあると思う。 もし、動かないページをみつけたらこちらまでご連絡いただければ幸い。ブックマークレットへの変換は以下のページが便利です。 Bookmarklet スクリプト変換 (function() { function copyToClipboard(text) { navigator.clipboard.writeText(text).then(function() { alert('クリップボードにコピーされました'); }).catch(function(error) { console.error('クリップボードへのコピーに失敗しました', error); }); } function getElement(selector) { return document.querySelector(selector); } try { var size = 200; var asinElement = getElement('input#ASIN'); if (!asinElement) throw new Error('ASINが見つかりませんでした'); var asin = asinElement.value; var titleElement = getElement('span#productTitle'); if (!titleElement) throw new Error('製品名が見つかりませんでした'); var title = titleElement.textContent.trim(); var thumbnailUrl = getElement('img#landingImage')?.src || getElement('img[src*="_SY"]')?.src || getElement('input#productImageUrl')?.value || getElement('img[src*="_SX"]')?.src; if (!thumbnailUrl) { throw new Error('サムネイル画像が見つかりませんでした'); } var productUrl = 'https://www.amazon.co.jp/gp/product/' + asin + '/?tag=namaraiicom-22'; var modifiedUrl; var sizeMatch = thumbnailUrl.match(/_(_SY|_SX)(\d+)_/); if (sizeMatch && sizeMatch[2]) { modifiedUrl = thumbnailUrl.replace(/_(_SY|_SX)\d+_/, `_${sizeMatch[1]}${size}_`); } else if (thumbnailUrl.match(/_(SY|SX)(\d+)_/)) { modifiedUrl = thumbnailUrl.replace(/_(SY|SX)\d+_/, `_SY${size}_`); } else { modifiedUrl = thumbnailUrl; } var markdownContent = `![${title}](${modifiedUrl})\n\n[${title}](${productUrl})`; copyToClipboard(markdownContent); } catch (error) { console.error(error); } })();

RubyからBlueskyへの投稿

作成しているツールでRubyからBlueskyへ投稿したかったので、ShreyanJain9/bskyrb: Ruby Gem for interacting with BlueSky/AT Protocolというライブラリを使うことにした。 使用例を見ると、 require 'bskyrb' username = 'your_username' password = 'your_password' pds_url = 'https://bsky.social' credentials = Bskyrb::Credentials.new(username, password) session = Bskyrb::Session.new(credentials, pds_url) bsky = Bskyrb::RecordManager.new(session) post_uri = bsky.create_post("Hello world from bskyrb!")["uri"] bsky.like(post_uri) bsky.repost(post_uri) bsky.create_reply(post_uri, "Replying to post from bskyrb") とあり、一見簡単そうに見えるがBlueskyの場合はXやMastodonのAPIとは異なり、投稿するメッセージの中に、 URLがあってもリンクは設定されない ハッシュタグがあってもリンクは設定されない URLのコンテンツにOGBタグがあってもリンクカードは表示されない という仕様であり、なかなか面倒であることがわかった。 Ruby から Bluesky に投稿してみる Links, mentions, and rich text | Bluesky そんなわけで上記のドキュメントを参照しつつ、それぞれどうやれば良いのか調べてみた。 リンクの設定 # メッセージ中にURLがあれば、その部分を自動的にリンク設定したかったため、Rich text facetsの例にある以下の例を参考に、 { text: 'Go to this site', facets: [ { index: { byteStart: 6, byteEnd: 15 }, features: [{ $type: 'app.bsky.richtext.facet#link', uri: 'https://example.com' }] } ] } 以下のようなコードでfacetを作成した。 def create_facets_for_urls(text) facets = [] text_copy = text.dup URI.extract(text_copy, ['http', 'https']).each do |url| byte_start = text_copy[0, text_copy.index(url)].encode('UTF-8').bytesize byte_end = byte_start + url.bytesize facets << { 'index' => {'byteStart' => byte_start, 'byteEnd' => byte_end}, 'features' => [ { 'uri' => url, '$type' => 'app.bsky.richtext.facet#link', }, ], } text_copy[text_copy.index(url), url.length] = "\0" * url.length end facets end ハッシュタグの設定 # メッセージ中にハッシュタグがあれば、その部分を自動的にリンク設定したかったため、同じようなロジックでfacetを作成する。

Macのボイスメモで録音したファイルをmp3に変換する

事前にfmpegをインストールしておく。 ボイスメモのすべての録音で変換したいファイルを選択する 右クリックしサービス - Finderに表示 を選択 ターミナルを開きffmpeg -i を入力し2.をDrag&Dropする 続けて-acodec libmp3lame hogehoge.mp3と入力して実行する この例ではhogehoge.mp3というファイル名でMP3ファイルが作成される 入力するコマンドの全体はこうなる。 ffmpeg -i foo.m4a -acodec -acodec libmp3lame hogehoge.mp3 以下のようにすれば192kbpsのビットレートでMP3を作成できる。(デフォルトは128kbps) ffmpeg -i foo.m4a -acodec -acodec libmp3lame -b:a 192k hogehoge.mp3

FFmpeg

FFmpegは、音声・動画の変換、再生、録画、ストリーミングを行うためのオープンソースのフリーソフトウェアです。FFmpegはコマンドラインベースのツールで、幅広いメディアフォーマット(例えば、MP4、AVI、MP3、WAV、MPEG、FLVなど)をサポートしています。また、動画や音声のエンコード、デコード、フィルタリング、トランスコーディングなどを行うことができます。 FFmpegの主な機能には、以下があります: 動画・音声ファイルの形式変換 動画のトリミングや結合 画質やビットレートの調整 サウンドトラックの抽出や交換 ストリーミングメディアの録画 MacでのFFmpegのインストール方法 # Macでは、Homebrewを使うのが最も簡単な方法です。 1. Homebrewのインストール(未インストールの場合) # まず、Homebrewがインストールされていない場合は、以下のコマンドをターミナルに入力してインストールします: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 2. FFmpegのインストール # Homebrewがインストールされたら、以下のコマンドでFFmpegをインストールします: brew install ffmpeg インストールが完了したら、ターミナルで ffmpeg と入力して、動作確認を行います。 WindowsでのFFmpegのインストール方法 # 1. FFmpegの公式ビルドをダウンロード # Windowsでは、公式サイトからFFmpegのビルドをダウンロードします。 FFmpeg公式ダウンロードページにアクセスします。 Windows用のビルドを選択します。通常は「Windows builds by BtbN」などのリンクを選びます。 リンク先のページで「Download Build」をクリックし、ZIPファイルをダウンロードします。 2. ダウンロードしたファイルを解凍 # ダウンロードしたZIPファイルを解凍します。解凍したフォルダをCドライブのルート(例: C:\ffmpeg)などに配置します。

radikoを録音してPoscastプレイヤーで再生する

miyagawaさんの以下のプログラムを組み合わせて使うことで、radikoで録音した番組をPodcastプレイヤーで再生することができる。 miyagawa/ripdiko: Rip radiko programs miyagawa/dircaster: Generate RSS 2.0 podcast out of directory with *.mp3s おおまかな流れは以下の感じ。 cronで起動されたripdikoがradikoを録音し、指定フォルダにMP3ファイルで保存する cronで起動したdircasterが指定フォルダにあるMP3ファイルを解析し、Podcast配信用のXMLを出力する Podcastプレイヤーで2で生成されるXMLを購読しておけば、番組が更新されるたびに通知され聞くことができる ただし、1のMP3ファイル、2のXMLはインターネットから参照できる場所に出力する必要がある(HTTPサーバが必要) Radikoプレミアムへの対応 # ただ、残念なことにripdikoはradikoプレミアムへは対応しておらず、エリアフリーで他県の番組を録音することができない。 このため、uru2/rec_radiko_ts: Radiko timefree program recorderを参考にして、radikoプレミアムへログインしエリアフリーで番組を録音できるようにした。 環境変数RIPDIKO_MAILでメールアドレス、RIPDIKO_PASSにパスワードを設定しておくことで、他県の番組も録音することができる。 diff --git a/bin/ripdiko b/bin/ripdiko index ebc756a..2ca3b70 100755 --- a/bin/ripdiko +++ b/bin/ripdiko @@ -8,6 +8,7 @@ require 'open-uri' require 'nokogiri' require 'date' require 'fileutils' +require 'json' module Notifier SCRIPTS = ENV['RIPDIKO_SCRIPTS'] || "#{ENV['HOME']}/.ripdiko/scripts" @@ -61,6 +62,43 @@ class DownloadTask @output = ENV['RIPDIKO_OUTPUT'] == nil ? "file" : ENV['RIPDIKO_OUTPUT'] end + def login + uri = URI.parse("https://radiko.jp/v4/api/member/login") + data = { + "mail" => ENV['RIPDIKO_MAIL'], + "pass" => ENV['RIPDIKO_PASS'] + } + + return "" if data["mail"].nil? || data["pass"].nil? + encoded_data = URI.encode_www_form(data) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = (uri.scheme == "https") + + request = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'application/x-www-form-urlencoded'}) + request.body = encoded_data + response = http.request(request) + + return JSON.parse(response.body)["radiko_session"] || "" + end + + def logout + uri = URI.parse("https://radiko.jp/v4/api/member/logout") + data = { + "radiko_session" => @radiko_session + } + encoded_data = URI.encode_www_form(data) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = (uri.scheme == "https") + + request = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'application/x-www-form-urlencoded'}) + request.body = encoded_data + response = http.request(request) + + @radiko_session = "" + end + + + def authenticate res = fetch 'https://radiko.jp/apps/js/playerCommon.js?_=20171113', {} if /new RadikoJSPlayer\(.*?'pc_html5',\s*'(\w+)'/ === res.body @@ -79,11 +117,16 @@ class DownloadTask abort "auth1 failed" end + @radiko_session = login + value = URI.encode_www_form_component(@radiko_session) + + auth2_url_param = @radiko_session.size > 0 ? "?radiko_session=#{value}" : "" + length = res['X-Radiko-KeyLength'].to_i offset = res['X-Radiko-KeyOffset'].to_i partial_key = Base64.strict_encode64(auth_key.byteslice(offset, length)) - res = fetch "https://radiko.jp/v2/api/auth2", + res = fetch "https://radiko.jp/v2/api/auth2#{auth2_url_param}", 'X-Radiko-User' => "dummy_user", 'X-Radiko-Device' => "pc", 'X-Radiko-Authtoken' => token, @@ -106,6 +149,40 @@ class DownloadTask def now_playing(station, area) now = Time.now + + station_list = { + "802" => "JP25", "ABC" => "JP25", "ABCR-KOSHIEN" => "JP1", "ABS" => "JP5", + "AFB" => "JP2", "AFM" => "JP5", "AIR-G" => "JP1", "ALPHA-STATION" => "JP25", + "BAYFM78" => "JP8", "BSN" => "JP15", "BSS" => "JP31", "CBC" => "JP21", + "CCL" => "JP25", "CRK" => "JP25", "CROSSFM" => "JP40", "CRT" => "JP9", + "DATEFM" => "JP4", "E-RADIO" => "JP25", "FBC" => "JP18", "FM-FUJI" => "JP19", + "FM-OKAYAMA" => "JP33", "FM-SANIN" => "JP31", "FM807" => "JP36", + "FMAICHI" => "JP21", "FMF" => "JP7", "FMFUKUI" => "JP18", "FMFUKUOKA" => "JP40", + "FMGIFU" => "JP21", "FMGUNMA" => "JP10", "FMI" => "JP3", "FMJ" => "JP8", + "FMK" => "JP43", "FMKAGAWA" => "JP37", "FMMIE" => "JP24", "FMN" => "JP20", + "FMNAGASAKI" => "JP42", "FMNIIGATA" => "JP15", "FMO" => "JP25", "FMS" => "JP41", + "FMT" => "JP8", "FMTOYAMA" => "JP16", "FMY" => "JP35", "FM_OITA" => "JP44", + "FM_OKINAWA" => "JP47", "GBS" => "JP21", "HBC" => "JP1", "HELLOFIVE" => "JP17", + "HFM" => "JP34", "HI-SIX" => "JP39", "IBC" => "JP3", "IBS" => "JP8", + "INT" => "JP8", "JOAK" => "JP8", "JOAK-FM" => "JP1", "JOBK" => "JP25", + "JOCK" => "JP16", "JOEU-FM" => "JP38", "JOFK" => "JP31", "JOHK" => "JP2", + "JOIK" => "JP1", "JOLK" => "JP40", "JORF" => "JP8", "JOYFM" => "JP45", + "JOZK" => "JP36", "JRT" => "JP36", "K-MIX" => "JP22", "KBC" => "JP40", + "KBS" => "JP25", "KISSFMKOBE" => "JP25", "KNB" => "JP16", "KRY" => "JP35", + "LFR" => "JP8", "LOVEFM" => "JP40", "MBC" => "JP46", "MBS" => "JP25", + "MRO" => "JP17", "MRT" => "JP45", "MYUFM" => "JP46", "NACK5" => "JP8", + "NBC" => "JP41", "NORTHWAVE" => "JP1", "OBC" => "JP25", "OBS" => "JP44", + "QRR" => "JP8", "RAB" => "JP2", "RADIOBERRY" => "JP9", "RBC" => "JP47", + "RCC" => "JP34", "RFC" => "JP7", "RFM" => "JP6", "RKB" => "JP40", + "RKC" => "JP39", "RKK" => "JP43", "RN1" => "JP1", "RN2" => "JP1", + "RNB" => "JP38", "RNC" => "JP37", "ROK" => "JP47", "RSK" => "JP33", + "SBC" => "JP20", "SBS" => "JP22", "STV" => "JP1", "TBC" => "JP4", + "TBS" => "JP8", "TOKAIRADIO" => "JP21", "WBS" => "JP30", "YBC" => "JP6", + "YBS" => "JP19", "YFM" => "JP8", "ZIP-FM" => "JP21" + } + # 都道府県コードの変換(自地域以外のラジオ局を指定された場合) + area = station_list[station] if station_list[station] != area + doc = Nokogiri::XML(URI.parse("https://radiko.jp/v3/program/now/#{area}.xml").open) node = doc.xpath(%Q|//station[@id="#{station}"]|).first @@ -209,6 +286,8 @@ class DownloadTask when "fifo" FileUtils.rm tempfile end + + logout end def fetch(url, headers)

Power AutomateでOutlookの件名に複数キーワードをマッチさせトリガーする

Power Automateで電子メールの件名に特定のキーワード含まれている場合…という条件でトリガーしたかったため、Copilotに聞いて件名フィルターに至急 OR 重要などと書いてみたがうまく動かない。 これはお得意のハルシネーションかも…とググったところ、やはり複数キーワードでトリガーする場合、設定タブでトリガーの条件を記述する必要があるようだ。 このフィールドで、 @or(contains(triggerBody()?['Subject'], '至急'),contains(triggerBody()?['Subject'], '重要')) のように書いたところうまく動いた。

Percollate - WebページをPDF, EPUB, HTML, Markdownに変換するコマンドラインツール

WebページをPDF, EPUB, HTML, Markdownに変換するコマンドラインツール danburzo/percollate: A command-line tool to turn web pages into readable PDF, EPUB, HTML, or Markdown docs. $ percollate pdf https://example.com -o my-example.pdf 出力フォーマットを最初の引数に与える。以下を指定可能。 pdf epub html md 複数のページを1つのファイルにまとめることもできる。 以下はフォント、タイトル、著者名を指定した例。複数ページをまとめた場合、表紙と目次が追加されるがタイトルと著者名は表紙の表示される。 $ percollate pdf -o example.pdf https://example.com/page1 https://example.com/page2 https://example.com/page3 --css ":root { --main-font: 'Helvetica Neue', 'Helvetica', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Arial', 'Yu Gothic', 'Meiryo', sans-serif; }" --title="タイトル" --author="著者名"

Platypus - RubyスクリプトをMac OSのアプリに変換できるツール

sveinbjornt/Platypus: Create native Mac applications from command line scripts. スクリプト(Shellスクリプト、Perl、Python、Rubyなど)を標準のMac OS Xアプリケーション(.app)に変換するツール。 Platypusを使用すると、コマンドラインやターミナルを介さずに、スクリプトを直接実行可能なアプリケーションとして実行できる。 生成されるappのinfo.plistを編集することで、スクリプトをURLスキームに対応させることができる。 RubyではURLスキームで指定された文字列へARGVでアクセスできた。

20年前の写真をスキャンして高解像度化(Topaz Photo AI)

20年以上前に撮影したプリントしかない写真をデータ化したが、スキャンしただけでは画質的に厳しかったためTopaz Photo AIとPixelmator Proを使って高解像度化した。 写真のスキャン # Scan Snap iX1500の最高画質で写真をスキャンしてデータ化した。スマホやPCで参照する分には問題ないが、プロジェクターで投影するため解像感が欲しいところ。 Topaz Photo AIで高解像度化 # 4倍へアップスケール ノイズ除去、シャープ化、顔の補正、ライティング修正、色補正などてんこ盛り指定 これぐらいのサイズだとわかりにくいが、明らかに解像感が増し大画面へ写しても問題なさそうだ。ただ、色調があまり好みでなかった。 Pixelmator Proで色補正 # カラー調整でモダンフィルムのプリセットを選んだ(だけ) Topaz Photo AIで解像度を向上するのにしても、Pixelmator Proで色味を変えるにしても、細かくいくつものパラメタを指定する必要はなく、機能を選ぶだけでこれぐらいの編集ができてしまうんだから良い時代だな。

「写真」アプリで検索した写真の撮影日周辺の写真を確認する

[iPad Workers|https://podcasts.apple.com/jp/podcast/ipad-workers/id1567317147] の #113 で紹介されていた写真アプリの便利な小技が、まさに自分が欲していたものだった。 iPhoneの「写真」アプリで検索した写真の撮影日周辺の写真を参照したいことがあるが、その際、撮影日を確認してからカメラロールに戻り、その近辺の写真を探していたが、この手順がちょっと楽になる。 検索した写真の下のiアイコンをタップし、 すべての写真に表示をタップすれば… 撮影日時近辺のカメラロールが表示される。これは便利。 ただ、Podcastでも言及されていたが、なぜか Macの「写真」アプリにはすべての写真に表示がなく同じことをするための手段がわからない…と思っていたが、いま発見した。 検索した写真を右クリックすると表示されるメニューに"すべての写真”に表示があるので、それを選択すれば同じことができる。

Blueskyのカスタムフィードを作成する

SkyFeedのFeed Builderを使ってカスタムフィードをつくる - Blueskyを参考にBlueskyのカスタムフィードを作成してみた。ざっくりとした手順としては、 Blueskyでアプリケーションパスワードを生成する SkyFeedへアプリケーションパスワードを使用してログイン Feed Builder - Create your first feed Input Remove RegEX Sort Limit Stash+Popという種別のブロックを組み合わせて検索条件を作成する 名前を付けてPublishする という感じ。作成したカスタムフィードはこちら。 横浜FCの話題 - Bluesky

Amazonから書誌情報をTextwellへ取り込むブックマークレット

bibinfo-exporter/script.js at main · goryugocast/bibinfo-exporterを参考にAmazonから書誌情報をへ取り込むブックマークレットを作成した。 直接Obsidianに取り込むのは自分の運用に合わないためTextwellへ追記するように。 こちらは書籍専用で、著者名や出版社、出版日などを取り込む。 Amazonの書誌情報をTextwellの追記するブックマークレット javascript: (() => { const dest_path = 'notes'; //ファイルを格納するパス const amazon_id = 'namaraiicom-22'; // アフィリエイトID let p = document.getElementById("productTitle"); //書籍のタイトルの処理 p = p ? p : document.getElementById("ebooksProductTitle"); const title = p.innerText.trim(); let asin = document.getElementById('ASIN'); //ASIN番号の処理 const a = asin ? asin.value : document.getElementsByName('ASIN.0')[0].value; const url = `https://www.amazon.co.jp/exec/obidos/ASIN/${a}/${amazon_id}/`; const link = `[${title}](${url})`; let image = document.getElementById("imgTagWrapperId"); //書影の処理 image = image ? image : document.getElementById("ebooksImgBlkFront"); const imageurl = image.querySelector("img").getAttribute("src"); const c = document.getElementsByClassName('author'); const pub = []; const ct_list = []; // ctの各要素を保存する配列を新たに定義 for (let g = 0; g < c.length; g++) { const at = c[g].innerText.replace(/\r?\n/g, '').replace(/,/,''); const pu = at.match(/\(.+\)/); const ct = at.replace(/\(.+\)/,'').replace(/ /g,''); ct_list.push(ct); // ctを配列に追加 pub.push(`${pu} [[${ct}]]`); } const author = pub.join(' '); let h1title = `『${title}』`; h1title = h1title.replace(/[\\/:*?"<>|.]/g, char => ({ ':': ':', '\\': '\', '/': '/', '?': '?', '*': '*', '"': '”', '<': '<', '>': '>', '|': '|', '.': '.' }[char])); const mdimage = `[![|100](${imageurl})](${url})`; // 登録情報欄を取得 let detail = document.getElementById('detailBullets_feature_div'); if (!detail) { const subdoc = document.getElementById("product-description-iframe").contentWindow.document; detail = subdoc.getElementById("detailBullets_feature_div"); } const detailtext = detail.innerText; const pubdata = detailtext.split(/\n/); pubdata[2] = pubdata[2]?.slice(10); // 出版社 const date = new Date().toLocaleDateString('sv-SE'); const lines = `---%0D%0Atitle: "${h1title}"%0D%0Adate%3A%20${date}%0D%0Aupdated%3A%20${date}%0D%0Andl%3A%0D%0Atags%3A%20読書メモ%0D%0Adraft%3A%20true%0D%0A---%0D%0A${mdimage}%0D%0A-%20${link}%0D%0A-%20${author}%0d%0A-%20${pubdata[2]}%0D%0A%0D%0A%23%23 関連・思い出した本 %0d%0A%23%23 読書メモ%0d%0A`; const app = `textwell:///add?text=${lines}`; window.open(app); })(); こちらは一般的な商品の画像と商品名を取り込む。 Amazon商品の商品名と画像へのリンクをTextwellに追加するブックマークレット javascript: (() => { const dest_path = "notes"; //ファイルを格納するパス const amazon_id = "namaraiicom-22"; // アフィリエイトID let p = document.getElementById("productTitle") || document.getElementById("ebooksProductTitle"); //書籍のタイトルの処理 const title = p.innerText.trim(); const asinElement = document.getElementById('ASIN') || document.getElementsByName('ASIN.0')[0]; //ASIN番号の処理 const a = asinElement.value; const url = `https://www.amazon.co.jp/exec/obidos/ASIN/${a}/${amazon_id}/`; const link = `[${title}](${url})`; const image = document.getElementById("landingImage"); const imageurl = image.getAttribute("src"); // 自分が必要なパラメータに変換 let h1title = title.replace(/[\\/:*?"<>|.]/g, char => ({ ':': ':', '\\': '\', '/': '/', '?': '?', '*': '*', '"': '”', '<': '<', '>': '>', '|': '|', '.': '.' }[char])); const mdimage = `[![|100](${imageurl})](${url})`; const lines = `${mdimage}%0D%0A%0D%0A${link}%0D%0A`; const app = `textwell:///add?text=${lines}`; window.open(app); })();

📔 Macの「メモ」をMarkdown・HTMLでエクスポートする方法

以下のエントリにも書いた通り、Apple謹製の「メモ」アプリは継続的に改善されており、自分の用途を考えると機能的な不満はない。 🗒️ メモ管理の現状 - namaraii.com ただ、「メモ」アプリにはPDF形式でしかデータをエクスポートできないという大きな問題点がある。 PDFでエクスポートできたとしても、他のメモアプリにインポートするのは困難であり事実上のロックインだ。 とは言え、なんとかエクスポートする方法はないか調べたところ、Exporterというアプリがあった。このアプリは、 フォルダ構造を維持してMarkdownまたはHTMLでエクスポート可能 作成日、更新日を維持(Markdownの場合、FrontMatterへ出力可能) jpeg、png、gif、tiff、bmp、pdf、ics、m4a、mov、mp3の添付ファイルの書き出しに対応 という優れもので、無料で全ノートをエクスポートすることができる。 課金するとフィルタビューという機能が提供され、選択したノートブックのみエクスポートできるようだ。 全体のエクスポートはそれなりに時間がかかるので、頻繁にバックアップを取得したい人は課金すると良いかも。 蛇足 # エクスポート方法を調べる過程で知ったが、メモアプリのデータはSQlite3で管理しているらしい。 ~/Library/Group Containers/group.com.apple.notes/NoteStore.sqliteにあるファイルをワークエリアへコピーしてsqlite3コマンドでテーブル一覧を参照できた。 $ sqlite3 NoteStore.sqlite SQLite version 3.39.5 2022-10-14 20:58:05 Enter ".help" for usage hints. sqlite> .tables ACHANGE ZICINVITATION Z_METADATA ATRANSACTION ZICLOCATION Z_MODELCACHE ATRANSACTIONSTRING ZICNOTEDATA Z_PRIMARYKEY ZICCLOUDSTATE ZICNOTEPARTICIPANT ZICCLOUDSYNCINGOBJECT ZICSERVERCHANGETOKEN sqlite> select * from ZICNOTEDATA LIMIT 1; 10917|18|1|107431||| メモアプリのデータへアクセスするAPIが公開されたりしたらありがたいが、そんなことはしないだろうな。

〰️ USB Type-Cの複雑さ

iPhone 15でLightningからUSB Type-Cへ変更される記念?で、なんとなくしか理解していないUSB Type-Cの仕様についてまとめてみた。 USB Type-Cとは # USB Type-Cは、物理的なコネクタの形状を指すものである。その背後にあるデータ転送や電力供給の仕様は、USB Type-Cの形状だけでは特定できない。 USBの仕様 # 仕様 データ転送速度 注意点 USB 2.0 最大480 Mbps 新しいデバイスとの互換性が必要な場合には注意が必要 USB 3.2 Gen 1 最大5 Gbps USB 3.2 Gen 2 最大10 Gbps USB 3.2 Gen 2x2 最大20 Gbps USB4 最大40 Gbps Thunderbolt 3との互換性があるが、すべてのUSB4デバイスやケーブルがThunderbolt 3と互換性があるわけではない USB Power Delivery (USB PD) # USB Type-Cコネクタを使用するデバイス間での高出力の電力供給を可能にするもので最大100W (20V@5A)の電力供給が可能。

Hugoのショートコードをうまく使い回す方法

Hugoには他のCMSではよくある、機能を拡張するためのプラグインを提供する仕組みがない。 このため、あるテーマの中に便利なショートコードがあったとして、それを別のテーマで使用しようとした場合、 テーマディレクトリ/layouts/shortcodes配下のショートコードのソースを、別のテーマの/layouts/shortcodes配下、またはルート配下の/layouts/shortcodesへコピーする ショートコードにスタイルが設定されている場合、スタイルシートの中から必要な部分を抜き出し、別のテーマのスタイルシートへコピーする という煩雑な手順が必要となるが、先日、以下のショートコードのインストール手順を読んでいたら、うまい方法でショートコードを提供していた。 mfg92/hugo-shortcode-gallery: A theme components with a gallery shortcode for the static site generator hugo. まずショートコードのコードをテーマディレクトリ配下へcloneする。その後、config.tomlでテーマを以下のように指定する。 theme = ["your-main-theme", "hugo-shortcode-gallery"] そもそもテーマを複数指定できることを知らなかったが、この設定によりyour-main-themeテーマに加えhugo-shortcode-galleryテーマも参照されることになる。 そして、hugo-shortcode-gallery配下は以下のようなファイル構成になっている。 hugo-shortcode-gallery ├── LICENSE.md ├── README.md ├── assets │ └── shortcode-gallery │ ├── filterbar.sass │ └── font-awesome │ ├── compress-alt-solid.svg │ ├── expand-alt-solid.svg │ └── license.txt ├── config.toml ├── layouts │ └── shortcodes │ └── gallery.html └── static └── shortcode-gallery ├── jquery-3.7.0.min.js ├── justified_gallery │ ├── LICENSE │ ├── jquery.justifiedGallery.js │ ├── jquery.justifiedGallery.min.js │ ├── justifiedGallery.css │ └── justifiedGallery.min.css ├── lazy │ ├── jquery.lazy.js │ └── jquery.lazy.min.js └── swipebox ├── css │ ├── swipebox.css │ └── swipebox.min.css ├── img │ ├── icons.png │ ├── icons.svg │ └── loader.gif └── js ├── jquery.swipebox.js └── jquery.swipebox.min.js 14 directories, 23 files テーマと言ってもlayouts配下にあるのはショートコードのみである。そしてassetsとstaticディレクトリ配下にショートコードで使用するファイルだけを置くというのは、汎用的にショートコードを提供する方法として手軽で良いと思った。

MacからWindowsへRDP接続する際にキーボードがUS配列になる場合

MacのMicrosoft Remote DesktopからWindowsにRDP接続する際、キーボードがUS(英語)配列で認識される場合の解消方法。MacからWindowsへRDP接続するとキーボードがJISではなくUS配列になるとき | 晴耕雨読からの引用です。 自分の環境では3.の前にWindowsを再起動する必要があった。 対処方法 # 接続先のWindowsで以下の作業を行います。 Win+R で regedit を入力してレジストリエディタを開き、以下のレジストリを開きます。 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000411 Layout File を「KBDJPN.DLL」から「kbd106.dll」に書き換えます。 MacOSからRDP接続し、日本語キーボードとして認識されているか確認します。

Hugoでイメージギャラリー hugo-shortcode-gallery

Hugoでイメージギャラリーを実装する方法を調べていたところ、hugo-shortcode-galleryというのがお手軽で良さそうだった。 mfg92/hugo-shortcode-gallery: A theme components with a gallery shortcode for the static site generator hugo. インストールするとgalleryというショートコードが使えるようになる。以下が実例。 このページがあるディレクトリにimagesというディレクトリを作成して画像を置き、ショートコードのパラメタにmatch="images/*"と書き、その他、必要なパラメタを指定するだけで、こんなサムネイルを表示してくれる。 インストールもthemeディレクトリでcloneしてconfig.tomlを修正するだけなので簡単だ。

Hugoのショートコード入力を支援するChrome拡張

自分用にHugoのショートコードの入力を支援するChrome拡張を書いた。 htakeuchi/HugoShortcoder: Chrome extension to copy Hugo shortcode to clipboard according to open URL. 機能 # たとえばHugoの公式ショートコードでTweetを引用しようとすると、 URLからユーザIDとTweet ID?を調べる。 URLがhttps://twitter.com/SanDiegoZoo/status/1453110110599868418だとすると {{< tweet user="SanDiegoZoo" id="1453110110599868418" >}}の形式でショートコードを書く ことになり、なかなか面倒だ。 この機能拡張はこれらを支援するもので、引用したいページを開きコンテキストメニューから機能拡張を起動すれば、URLからショートコードを組み立ててクリップボードへコピーされる。 サポートしているサービスは以下の通り。 gist Instagram X (Twitter) Vimeo YouTube これらのサービス以外のページでは、Markdown形式でページタイトルとURLのリンクが作成され、クリップボードへコピーされる。 使用方法 # せっかくなので、Chromeウェブストアで公開しようかと思ったが、機能拡張が悪用されるケース多いせいか、手続きが煩雑になっていたため、野良で使うことにした。 Release v1.0 · htakeuchi/HugoShortcoder からzipをダウンロードし、 任意のディレクトリで解凍する Chromeを起動し設定-拡張機能を選択する デベロッパーモードをオンにしてパッケージ化されていない拡張機能を読み込むボタンをクリック 1.のディレクトリを選択 以上でマウス右クリックで表示されるコンテキストメニューにHugoShortcoderが追加される。

Macで定期実行(launchd)

用途によって配置するフォルダを選ぶ # フォルダ 使用法 /System/Library/LaunchDaemons Appleが提供するシステムデーモン /System/Library/LaunchAgents Appleが提供するエージェント。すべてのユーザに対してユーザごとに適用されます /Library/LaunchDaemons システムデーモン /Library/LaunchAgents すべてのユーザに対してユーザごとに適用されます ~/Library/LaunchAgents ログイン中のユーザにのみ適用されます LaunchAgentsはログイン前提、LaunchDaemonsはログインに依存せず実行される。 有効化・無効化 # 有効化 launchctl load /path/to/your.plist 無効化 launchctl unload /path/to/your.plist plistのサンプル # 20秒毎に実行・ログ出力 # 標準出力、標準エラー出力をログに出力 コマンドのサーチパスを指定 ワーキングディレクトを指定 /usr/local/bin/node main.jsを実行 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.demo.daemon.plist</string> <key>RunAtLoad</key> <true/> <key>StartInterval</key> <integer>20</integer> <key>StandardErrorPath</key> <string>/Users/chet/demo/stderr.log</string> <key>StandardOutPath</key> <string>/Users/chet/demo/stdout.log</string> <key>EnvironmentVariables</key> <dict> <key>PATH</key> <string><![CDATA[/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin]]></string> </dict> <key>WorkingDirectory</key> <string>/Users/chet/demo</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/node</string> <string>main.js</string> </array> </dict> </plist> 指定日時に実行 # <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>sample</string> <key>Program</key> <string>/Users/user_name/script/sample.sh</string> <key>StartCalendarInterval</key> <dict> <key>Minute</key> <integer>0</integer> <key>Hour</key> <integer>3</integer> </dict> <key>StandardOutPath</key> <string>/Users/user_name/script/sample.out</string> <key>StandardErrorPath</key> <string>/Users/user_name/script/sample.err</string> </dict> </plist> 参考にしたページ # Macの「ターミナル」でのlaunchdを使ったスクリプトの管理 - Apple サポート (日本) Creating Launch Daemons and Agents A Simple Launchd Tutorial. Suppose you’ve written a little script… | by Chet Corcos | Medium

OAuthでアクセストークンを取得するスクリプト

OAuthでアクセストークン、アクセストークンシークレットを取得するサンプル。毎回、同じようなスクリプトを書いて使い捨てにするのはムダなのでメモしておく。 ブラウザで表示されるURLへアクセスする リダイレクトされたURLのoauth_verifierの値を入力 access_token.token、access_token.secretが表示される require 'oauth' URL = "https://www.tumblr.com" consumer_key = 'Cls**********yaqV4' consumer_secret = 'RG****3KwFy' oauth = OAuth::Consumer.new(consumer_key, consumer_secret, site: URL) request = oauth.get_request_token(exclude_callback: true) puts "Access this URL: #{request.authorize_url}" print "oauth_verifier: " verifier = gets.chomp access_token = request.get_access_token(oauth_verifier: verifier) pp access_token

TemplaterプラグインのテンプレートからMoment.jsを使用する

ObsidianのTemplaterプラグインでデイリーノートを生成しているが、テンプレートではIntroduction - Templaterを参考に前後のデイリーノートへ移動するためのリンクを追加するようにしていた。 << [[2025-02-04]] | [[2025-02-06]] >> 上記のテンプレートでは、当日のデイリーノート作成ではうまくいくが、未来や過去日付のデイリーノートを作成した場合、その日付の前後のリンクではなく実行した当日を基準にリンクが追加されるため、本来の目的を果たせない。 Templaterプラグインのドキュメントを読んだところテンプレートの中からMoment.jsの オブジェクトにアクセスできるようなので、以下のように修正した。 << [Invalid date](/journal/Invalid date) | [Invalid date](/journal/Invalid date) >> これにより、Calendarプラグインで任意の日をクリックすると、その日の前後のリンクが設定されたデイリーノートのひな形を生成することができる。

Pocketに保存したページを読み上げてPodcastとして配信

PocketへWebページを保存するとページの本文らしき部分を抽出し、Text-to-Speechでそれを読み上げた音声ファイルを作り、プライベートなPodcastとして配信するしかけを作った。 音声合成エンジンとしてVOICEVOXも試したが、手元の環境で安定して動作させるところまでいけず断念。 システムの概要 # flowchart TD Podcastアプリ[/Podcastアプリ\] --> |参照|nginx Pocket[/Pocket\] --> |新規保存|IFTTT IFTTT --> |Webhooks|nginx subgraph システムの概要 nginx nginx --> |PodcastのRSSフィード参照|index.rss nginx --> |PocketしたURLを通知|sinatra subgraph PodcastのRSSフィード生成 mp3 --> |参照|index.rss mp3 --> |参照|dropcaster cron --> |5分毎|dropcaster --> |存在するmp3からRSSフィード生成|index.rss end subgraph 音声ファイル生成 sinatra sinatra --> readability readability --> |ページの本文|texttospeech texttospeech --> |本文からmp3生成|mp3[(mp3)] end end 使用したライブラリやサービス # Webhooks Integrations - Connect Your Apps with IFTTT Webページから本文らしき部分を抽出する nerab/dropcaster: Simple Podcast Publishing Text-to-Speech: 自然な音声合成 | Google Cloud

Webページから本文らしき部分を抽出する

さまざまなパターンの日本語文章をデータとして欲しいケースがあったので、指定したURLから本文らしき内容を抽出するスクリプトを書いた。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 require 'playwright' require 'readability' require 'html2text' # 指定したURLから本文らしき内容を抽出して返却する def html2text(url) Playwright.create(playwright_cli_executable_path: 'npx playwright') do |playwright| playwright.chromium.launch(headless: true) do |browser| begin page = browser.new_page page.goto(url, waitUntil: 'load') doc = Readability::Document.new(page.content) sleep 1 return {:title => page.title, :content => Html2Text.convert(doc.content) } rescue return {:title => nil, :content => nil } end end end end url = ARGV.shift doc = html2text(url) puts "#{doc[:title]}\n#{doc[:content]}" Javascriptでコンテンツを生成するページに対応するためPlaywrightを使用。 使用したライブラリ # cantino/ruby-readability: Port of arc90’s readability project to Ruby YusukeIwaki/playwright-ruby-client: Playwright client for Ruby soundasleep/html2text_ruby: A Ruby component to convert HTML into a plain text format.

Textwell

Textwell - The Modeless Textbox for iPhone, iPad, iPod touch, Mac, and Watch. メモ、メッセージ、ブログ、検索、投稿など、あらゆる文章作成タスクに使用できる多目的テキストエディタ ファイリングやスタイリングのための機能はないが、JavaScriptベースのカスタマイズ可能なアクション、自動履歴、クラウド同期などをサポートしており、シンプルで拡張性が高い Mac版、iOS版、AppleWatch版がある Textwell | URL Schemes 自作のアクションなど # ソートして重複行を削除するTextwellのアクション Obsidianのデイリーノートへ追記するTextwellのアクション Amazonから書誌情報をTextwellへ取り込むブックマークレット

Obsidianのデイリーノートへ追記するTextwellのアクション

ObsidianのデイリーノートへTextwell から追記するためのアクション。TextwellからObsidianのデイリーノートを書く方法 - Jazzと読書の日々を参考にさせていただき以下の修正を行った。 デイリーノートのディレクトリ構成(YYYY/MM/YYYY-MM-DD.mdとした) 新規作成ではなく追記に変更 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // デイリーノートのルートディレクトリ Root = "journal"; url = "obsidian://"; d = new Date(); y = d.getFullYear(); m = Zero(d.getMonth()+1); // デイリーノートのディレクトリ構成対応(YYYY/MM/YYYY-MM-DD.md) Folder = Root + "/" + y + "/" + m; Title = y + "-" + m + "-" + Zero(d.getDate()); if(Folder) Title = Folder + "/" + Title; if(T.text) url+= "new?content=" + encodeURIComponent("\n") + T.stdin.text + "&file=" + encodeURIComponent(Title) + "&append"; T(url,{option:"cutWhole"}); function Zero(x){ return ("00"+x).slice(-2); }

ObsidianのURLスキーム

Using obsidian URI - Obsidian Help ノートを開く open obsidian://open?vault=my%20vault&file=path%2Fto%2Fmy%20note my vaultのpath/to/my noteを開く ノートを検索する search obsidian://search?vault=my%20vault&query=MOC my vaultでMOCを検索する ノートを作成する new obsidian://new?vault=my%20vault&path=path%2Fto%2Fmy%20note my vaultのpath/to/my note`を新規に作成する オプション vaultボールト名またはボールト ID のいずれか name作成するファイル名。これが指定されている場合、ファイルの場所は「新しいメモのデフォルトの場所」になる file名前を含むボールトの絶対パス。指定した場合はnameは上書きされる path 絶対パス content(オプション) メモの内容 silent(オプション) 新しいメモを開かない場合に指定 append(オプション) ファイルが存在する場合、そのファイルへ追加する overwrite(オプション) 既存のファイルが存在する場合は上書きする x-success(オプション) x-callback-urlを指定する 利用例 # Obsidianのデイリーノートへ追記するTextwellのアクション AlfredからObsidianの保管庫を検索する

AlfredからObsidianの保管庫を検索する

ObsidianにアクセスするためのAlfredワークフローがいくつか公開されている。 chrisgrieser/shimmering-obsidian: Alfred Workflow for various enhancements of Obsidian. hauselin/obsidian-alfred: Alfred workflow for Obsidian note-taking app. Open vaults and files in Obsidian. 単純に特定の保管庫を検索するだけであれば、AlfredのWeb Search機能へObsidianのURLスキームをもとに設定するだけで良い。 SearchURL obsidian://search?vault=保管庫名&query={query} Title Search Obsidian Keyword so

Alfred

Alfred - Productivity App for macOS Mac用のキーボードショートカットアプリ。キー入力だけでアプリを起動したり、Web検索をしたり、コンピュータ上のファイルを検索したり、システムコマンドを実行したりできる。 Alfred関連ノート # AlfredからObsidianの保管庫を検索する Alfred常用のための設定と導入ワークフロー AlfredでDeepL翻訳するワークフロー deepl-alfred-workflow2

Pro Git(第2版)

クリエィティブコモンズで公開されている Pro Git(第2版)のHTML, PDF, epub3。むかしasciidoctorで生成したもの。 Pro Git 第2版(日本語版)HTML Pro Git 第2版(日本語版)PDF Pro Git 第2版(日本語版)epub3

Numi: 日常の電卓以上、Excel未満の計算に便利な美しい計算アプリ(Mac)

日常生活で良くある「電卓以上Excel未満」の計算で便利なMac用のアプリ Numi. Beautiful calculator app for Mac. 起動するとApple標準のメモ.appよりもシンプルな画面が開く。 ここへおもむろに数式を入力していくと、リアルタイムに計算結果が右側へ表示される。 上の画面では2021-1997をタイプしたところで、計算結果の24が表示されている。 改行すると次の数式を入力でき、感覚としてはテキストエディタで数式混じりの文書を書くと勝手に計算されていくイメージ。 他にもビット演算や平均値の計算、各種変換(通貨・タイムゾーン・CSS・単位)、変数、関数などもサポートし手いる。 Documentation · nikolaeu/numi Wiki · GitHub 計算式 # 数値・ビット演算 # 平均・通貨 # 変数・関数 # タイムゾーン・CSS・単位の変換 # Numi関連のノート # Numiの使用例

iCloud Driveのパスは?(ターミナルからアクセス)

パスとしては/Users/ユーザ名/Library/Mobile\ Documents/配下になるが、iOSのアプリが作ったフォルダと、自分が直接iCloud Driveへ作成したドライブでこの後のパスが違ったりわずらわしいので、 Finderからドラッグ&ドロップするのがお手軽 FinderでiCloud Driveのフォルダを選択しターミナルへDrag&Dropする ターミナルへパスが展開される。ちゃんとスペースをエスケープされた状態で展開されるので、そのままコマンドで使用できる

Hugoのローカルサーバでページが表示されないパターン

Hugoで作成したページがhugo serveしても表示されない場合がある。 timeZoneを指定していない 以下の両条件を満たしていると実行する時刻によっては表示されない config.tomlでtimeZoneを明示していない timeZone = “Japan” ページのFront Matterのdateを日付のみ指定し時刻を省略 date: 2022-12-29 このパターンにハマってしまった。hugo serveのオプションを調べていて--buildFutureを付けてみたら表示され気付いた。 draftフラグを設定している ページのFront Matterがtrueになっている

Obsidianのノートを外部公開する場合のデッドリンク問題

ページタイトル変更時の問題 # Obsidianでノート名(ファイル名)を変更した場合、そのノートに張られているリンクは自動的に修正される。このため、Obsidian単体で使っている限りは気軽にノート名を変更して問題ない。 ページの内容が変化した場合、それにあわせてノート名を変更したくなるのは自然なことで、Obsidianはそれをアプリケーションの機能として実現しているわけだ。 ただ、Obsidian PublishやQuartzなどを使って、Obsidianのノートをインターネット上で公開する際には、外部サイトからリンクを張られる可能性がある点を考慮する必要がある。 Obsidianを単体で使うときのように気軽にノート名を変更してしまうと、外部サイトからのリンクがデッドリンクになってしまうからだ。 この問題に対してScrapboxは、 ページタイトルをURLにする(ページタイトルとは別に一意なページIDを持っている) 外部からのアクセス時、ページが無くなっていた場合は、そのURLのアクセスログからページIDを探し、ページIDから移動先へリダイレクトする という方法で対応しているようだ。 wikiでページのURLをIDにすると絶対にうまくいかない - 橋本商会 本サイトでの対応 # 本サイトではこのページタイトル問題について、以下のように対応している。 ノート名(ファイル名)は半角英数字と一部の記号(- _)だけを使用する Markdownファイルとしての取り扱いを考慮 ノート名(ファイル名)は基本的に変更しない(外部公開時のパーマリンク維持のため) それでも変更したくなったらFront Matterにaliasesを書いておけば大丈夫そう これはHugoの機能。ただしまだQuartzでは試していない。 ノートのタイトルはFront Matterのtitleを使用する URLの構成要素ではなくなるので気軽に変更できる ただ、これだとファイルエクスプローラなどで視認性が落ちてしまう… ファイル名表示をtitleがあればそれを使用するプラグインがあった obsidian-front-matter-title オプションのAlias titleをONにすれば、Aliasを本来の目的で使用できなくなるものの、Wikiリンクの補完時に日本語のtitle、ファイル名のどちらでも候補を検索できるようになる 現時点では大きな問題は無さそうだが、しばらく運用して再評価したい。

国立国会図書館サーチAPIでISBNから書誌情報と書影を取得する

国立国会図書館サーチAPIは個人・非営利団体等がデータ利用により利益を得ない場合は、利用申請不要で利用できる。 APIのご利用について « 国立国会図書館サーチについて ISBNをキーにして書誌情報を取得するのはOpenSearchがお手軽なようだ。 API仕様の概要 « 国立国会図書館サーチについて https://iss.ndl.go.jp/api/opensearch?isbn=ISBN 結果はXML(RSS)で得られる。 <?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:dcndl="http://ndl.go.jp/dcndl/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <channel> <title>9784253265218 - 国立国会図書館サーチ OpenSearch</title> <link>https://iss.ndl.go.jp/api/opensearch?isbn=9784253265218</link> <description>Search results for isbn=9784253265218 </description> <language>ja</language> <openSearch:totalResults>2</openSearch:totalResults> <openSearch:startIndex>1</openSearch:startIndex> <openSearch:itemsPerPage/> <item> <title>海が走るエンドロール = Umi ga hashiru Endroll</title> <link>https://iss.ndl.go.jp/books/R100000002-I031602764-00</link> <description> <![CDATA[<p>1,秋田書店,9784253265218</p> <ul><li>タイトル: 海が走るエンドロール = Umi ga hashiru Endroll</li> <li>タイトル(読み): ウミ ガ ハシル エンド ロール</li> <li>責任表示: たらちねジョン 著,</li> <li>シリーズ名: BONITA COMICS</li> <li>NDC(10): 726.1</li> <li>NDC(9): 726.1</li> </ul>]]> </description> <author>たらちねジョン 著,たらちね, ジョン,</author> <category>本</category> <guid isPermaLink="true">https://iss.ndl.go.jp/books/R100000002-I031602764-00</guid> <pubDate>Wed, 29 Sep 2021 09:00:00 +0900</pubDate> <dc:title>海が走るエンドロール = Umi ga hashiru Endroll</dc:title> <dcndl:titleTranscription>ウミ ガ ハシル エンド ロール</dcndl:titleTranscription> <dc:creator>たらちね, ジョン</dc:creator> <dcndl:creatorTranscription>タラチネ, ジョン</dcndl:creatorTranscription> <dcndl:volume>1</dcndl:volume> <dcndl:seriesTitle>BONITA COMICS</dcndl:seriesTitle> <dc:publisher>秋田書店</dc:publisher> <dc:date>2021.8</dc:date> <dcterms:issued xsi:type="dcterms:W3CDTF">2021</dcterms:issued> <dcndl:price>600円</dcndl:price> <dc:extent>158p ; 19cm</dc:extent> <dc:identifier xsi:type="dcndl:ISBN">9784253265218</dc:identifier> <dc:identifier xsi:type="dcndl:NDLBibID">031602764</dc:identifier> <dc:identifier xsi:type="dcndl:JPNO">23585081</dc:identifier> <dc:identifier xsi:type="dcndl:TOHANMARCNO">07471697</dc:identifier> <dcndl:genre>漫画</dcndl:genre> <dc:subject xsi:type="dcndl:NDLC">Y84</dc:subject> <dc:subject xsi:type="dcndl:NDC10">726.1</dc:subject> <dc:subject xsi:type="dcndl:NDC9">726.1</dc:subject> <dc:description>機器種別 : 機器不用</dc:description> <dc:description>キャリア種別 : 冊子</dc:description> <dc:description>表現種別 : テキスト</dc:description> <dc:description>表現種別 : 静止画</dc:description> <dc:description>NDC(9版)はNDC(10版)を自動変換した値である。</dc:description> <rdfs:seeAlso rdf:resource="http://id.ndl.go.jp/bib/031602764"/> <rdfs:seeAlso rdf:resource="https://opac.lib.city.yokohama.lg.jp/opac/OPP1500?SELDATA=TOSHO&amp;SSNO=3-0500928436"/> </item> <item> <title>海が走るエンドロール = Umi ga hashiru Endroll</title> <link>https://iss.ndl.go.jp/books/R100000098-I000372329-00</link> <description> <![CDATA[<p>1,秋田書店,9784253265218</p> <ul><li>タイトル: 海が走るエンドロール = Umi ga hashiru Endroll</li> <li>タイトル(読み): ウミ ガ ハシル エンド ロール</li> <li>責任表示: たらちねジョン 著,</li> <li>シリーズ名: BONITA COMICS</li> <li>NDC(9): 726.1</li> </ul>]]> </description> <author>たらちねジョン 著,,</author> <category>本</category> <guid isPermaLink="true">https://iss.ndl.go.jp/books/R100000098-I000372329-00</guid> <dc:title>海が走るエンドロール = Umi ga hashiru Endroll</dc:title> <dcndl:titleTranscription>ウミ ガ ハシル エンド ロール</dcndl:titleTranscription> <dc:creator></dc:creator> <dcndl:volume>1</dcndl:volume> <dcndl:seriesTitle>BONITA COMICS</dcndl:seriesTitle> <dc:publisher>秋田書店</dc:publisher> <dc:date>2021.08</dc:date> <dcterms:issued xsi:type="dcterms:W3CDTF">2021-08</dcterms:issued> <dcndl:price>600円</dcndl:price> <dc:extent>158p ; 19cm</dc:extent> <dc:identifier xsi:type="dcndl:ISBN">9784253265218</dc:identifier> <dc:identifier xsi:type="dcndl:JPNO">23585081</dc:identifier> <dc:subject xsi:type="dcndl:NDC9">726.1</dc:subject> <dc:description>単行本全巻ID : C451417</dc:description> <dc:description>マンガ単行本備考 : 機器種別 : 機器不用 / キャリア種別 : 冊子 / 表現種別 : テキスト / 表現種別 : 静止画 / NDC(9版)はNDC(10版)を自動変換した値である。</dc:description> <dc:description>ID : M852583</dc:description> <dc:description>登録番号(館固有割り振りID):031602764(国立国会図書館)</dc:description> <rdfs:seeAlso rdf:resource="https://mediaarts-db.bunka.go.jp/id/M852583"/> </item> </channel> </rss> 書影はhttps://iss.ndl.go.jp/thumbnail/ISBNで得られる。

Hugoで本文の先頭画像をアイキャッチとして使用する

Hugoでアイキャッチ画像を取り扱う場合、ページのfront matterでアイキャッチ画像を指定し、ページレベルパラメタで取得するのが確実だが、アイキャッチ画像が指定されていない場合、記事内の画像をアイキャッチ画像として扱えれば便利なケースが多い。 layouts/partials/summary.html 1 2 3 4 5 6 7 8 9 10 {{- $image := index (findRE "<img.*?src=([^>]*)?>" .Content 1) 0 }} {{- if $image }} <figure class="ellipsis card_img"> {{ $image | safeHTML }} </figure> {{- else }} <p class="ellipsis card_summary"> {{ .Summary }} </p> {{- end }} 上のコードは正規表現で<img>タグを検索し存在すれば画像を出力し、存在しなければサマリを出力するもの。

Hugoでローカルサーバのみ実行する処理を書く方法

Hugoでhugo serveで起動するローカルサーバでのみ実行する処理を書きたい場合、.Site.IsServerを参照すれば良い。ローカルサーバの場合Trueが設定されている。 Site Variables |Hugo {{- if .Site.IsServer }} {{- $notes = .Site.RegularPages.ByDate.Reverse}} {{- else }} {{- $notes = .Site.RegularPages.ByLastmod.Reverse }} {{- end }}

指定したWebページをMarkdownへ変換するRubyスクリプト

指定したWebページをMarkdownへ変換するRubyスクリプト require 'reverse_markdown' require 'open-uri' ReverseMarkdown.config do |config| config.unknown_tags = :bypass config.github_flavored = true config.tag_border = '' end def usage puts "usage: #{$0} uri" exit 1 end usage if ARGV.size != 1 html = OpenURI.open_uri(ARGV.shift).read print ReverseMarkdown.convert html

2ホップリンク

ホップ数 # flowchart LR B1 & B2 --> A G --> O1 H --> O1 A --> O1 & O2 O2 --> E F --> O2 B1:::hop1 B2:::hop1 O1:::hop1 O2:::hop1 F:::hop2 G:::hop2 H:::hop2 style A fill:#f9f,stroke:#333,stroke-width:4px classDef hop1 fill:#a9a classDef hop2 fill:#f96 ホップ数とは自身から任意のページまでたどり着くまでのリンクの数である。 このようなリンク関係がある場合、ページAから見て、B1 B2 O1 O2は1ホップリンクと呼ばれる。 2ホップリンクの意味 # このため、上記のリンク関係で言えば、ページAから見たG H E Fは2ホップリンクとなる。 Scrapboxでは2ホップリンクの内、アウトゴーイングリンクのページへリンクしているページ、つまり、 AからリンクしているO1へリンクしている Gと H AからリンクしているO2へリンクしている F を関連リンクとして表示している。これは、 A→C, B→Cというリンクが存在するとき、AとBの間にはなんらかの関連があると考えてよい。 「和歌山」→「みかん」、「愛媛県」→「みかん」 ならば「和歌山県」と「愛媛県」はみかんつながりになっているわけだし、 「増井」→「Rubyプログラミング」、「高林」→「Rubyプログラミング」 ならば「増井」⇔「高林」は意味がある。 出典: 2ホップリンクの考察 - 増井俊之 上記の考え方に基づく仕様であり、本サイトでもQuartzを拡張し同様の2ホップリンクを表示している(各ページ配下の「関連リンク」) QuartzにScrapbox的な2ホップリンクを追加する Obsidiaのリンク表示 参考情報 # 2ホップリンクの考察 - 増井俊之

Hugo

オープンソースの静的サイトジェネレーター。go言語で記述されておりページ生成の速度が速くカスタマイズ性が高い。 The world’s fastest framework for building websites | Hugo Hugo関連のノート # Hugoメモ Hugoのショートコードをうまく使い回す方法 Hugoのローカルサーバでページが表示されない場合 Hugoで本文の先頭画像をアイキャッチとして使用する Hugoでローカルサーバのみ実行する処理を書く方法 Hugoで楽譜を描画する(abcjs) Hugoのレンダーフックを使ったMermaidの利用 Hugoのショートコード入力を支援するChrome拡張 Hugoでイメージギャラリーを表示するhugo-shortcode-gallery Hugoで写真をリサイズしExif情報を表示する

画像ファイルをまとめてリサイズする

RubyからImageMagickを使うためにRMagickで画像を一括してリサイズするスクリプト。Obsidianへ移行したメモに添付されていた画像サイズを圧縮するためにやっつけで作成 require 'RMagick' include Magick MAX_PIXEL = 800 MAX_FILE_SIZE = 300000 ARGV.each do |f| next unless %w(.jpeg .jpg .png .heic .webp).index File.extname(f).downcase size = File.size(f) next if size < MAX_FILE_SIZE img = Magick::Image.read(f).first next if img.columns <= MAX_PIXEL && img.rows <= MAX_PIXEL img.resize_to_fit(MAX_PIXEL, MAX_PIXEL).write(f) puts "#{f}: #{size} -> #{File.size(f)}" end

Blender学習ログ(2022)

はじめに # 本エントリはオープンソースの総合3DCGソフトであるBlenderの勉強していた際の作業ログに少しだけ手を加えただけのものです。 主に自分向けの内容ですが、参考にさせてもらった動画はどれもわかりやすく、これからBlederを始めようという方には取っ掛かりとして参考なるかもしれません。 自分のようなBlender初学者の場合、チュートリアル動画で操作方法を端折られたりすると、一気に途方に暮れてしまうことになりがちですが、本エントリで多く参照している3D Bibiさんの動画は、操作を省略せず丁寧に解説されているため、とてもわかりやすかったです。多謝 Blenderの勉強は今年(2022年)の1月ごろにしていたのですが、そこからしばらくBlenderに触れられてなく、かなりの部分を忘れていると思われるので、このメモを見ながら復習しようと考えているところ。 Day1 Blenderの基本操作 # 初回は「超初心者向け」とうたわれている以下の動画で勉強を始めました。 この動画に沿って同じ操作を行うことで、Blenderの基本的な操作は理解できたと思います。 ライティングに関しては操作方法を含めて理解が不十分だったため、とても暗いですね(笑) Day2 モデリングの練習(AMONG US) # 見た目がシンプルなAMONG USのキャラクターを作るチュートリアル動画で、引き続きモデリングの練習です。 以下、作業メモです。 モデリング(胴体) キューブにサブディビジョンサーフェースを追加 S-Zでリサイズ 編集モードでCtrl+Rを押し横方向へループカット(マウスカーソルをうろうろさせると線がでてくてくる)し、下で確定させる 縦をループカット Shift+Zでワイヤフレームモードにして左上と右上の頂点を選択し、G-Zで形状を調整 背面を表示して2面(ループカットしたので)を選択し、Iキーを2回押してサイズを調整(脚) S-Yでサイズ調整 G-Zで脚をつくる Eで押し出すと選択が平らになる モディファイアプロパティのレベル数、レンダーを3にする スムーズシェイドをかける 編集モードで上の面を2つ選択しG-Yで傾ける ゴーグルとリュック リュックにもサブディビジョンサーフェースを追加 モディファイアプロパティのレベル数、レンダーを3にする 縦と横、それぞれをループカットしてべベルをかけて形を調整 ゴーグルにもサブディビジョンサーフェースを追加 モディファイアプロパティのレベル数、レンダーを3にする 縦をループカットして形を調整 着色 Zキーを押してレンダービューへ ゴーグルは荒さ0、伝播0.8ぐらい カメラ設定 Ctrl + Alt + 0(現在の視点にカメラを合わせる) ビュー→視点をそろえる→現在の視点にカメラを合わせるでもOK ライト設定 ポイントからエリアに変更する レンダープロパティのカラーマネジメント ルックをハイコントラスト、露出を0.8に かわいい。

Hugoのレンダーフックを使ったMermaidの利用

独自構文のテキストから、各種のダイアグラムを生成できるMermaidという便利なライブラリがありますが、現時点ではHugoでサポートされていません。 このため通常、 独自のショートコードを作成する 言語としてmermaidを指定したコードブロックを使用し、mermaidでlanguage-mermaidクラスの要素を変換対象として指定する などの実装が多かったと思うのですが、公式サイトを調べていたところv0.93.0からサポートされたcodeブロックに対するレンダーフックを用いた例が載っており、よりスマートに実装できそうでした。 ここでは公式サイトのレンダーフックを用いたMermaidの組み込み方法を説明します。 レンダーフックの追加 # codeブロックに対するレンダーフックを定義するには、 layouts/_default/_markup/render-codeblock-言語名.html というファイルを作成します。今回ファイル名を、 layouts/_default/_markup/render-codeblock-mermaid.html とすることで、 ```mermaid flowchart LR Start --> Stop ``` というような、言語にmermaidを指定したコードブロックに対してレンダーフックが起動します。レンダーフックの内容は以下の通りです。 <div class="mermaid"> {{- .Inner | safeHTML }} </div> {{ .Page.Store.Set "hasMermaid" true }} <div class="mermaid">でコードブロックの内容を囲む Mermaidの記述があることを表すhasMermaidフラグをtrueにする テンプレートの修正 # テンプレートについては、 layouts/_default/baseof.html layouts/_default/single.html など、Mermaidのコンテンツを表示したいテンプレートへ以下の内容を追加します。私の場合、通常エントリでのみ表記できれば良いためsingle.htmlへ追加しました。 {{ if .Page.Store.Get "hasMermaid" }} <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script> <script> mermaid.initialize({ startOnLoad: true }); </script> {{ end }} このコードはhasMermaidフラグがtrueの場合のみ、MermaidのJavascscript読み込みと初期化を行います。これにより、Mermaidの記述がないページではコードが展開されずムダな処理を抑止できるわけです。 上記処理をテンプレートへ追記する場合の注意点としては、.Contentが出現する箇所より下へ書く必要があるということです。Hugoでは.Contentが実行(評価)されるまではレンダーフックは処理されないためです。 参考サイト # mermaid.js で Markdown 中に UML 図を埋め込む - まくまくHugoノート Markdown Render Hooks mermaid - Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.

Kindle for Macの情報を使って蔵書一覧を作成する

Kindleで持っている本の一覧が欲しくなり方法を調べたところ、そのものズバリの情報があった。 Kindle蔵書一覧を取得する方法 - Qiita このページでは、以下の3つの方法が紹介されている。 コンテンツと端末の管理ページからスクレイピングする Kindle Cloud Readerが使っているWeb SQL Databaseのクライアント側のDBからそのまま取得する Kindle for PC(Kindle for Mac)で生成されるXMLを参照する いちばんお手軽そうな2.を試してみたが、自分の環境ではうまく動作しなかったため、3.のXMLをパースして蔵書一覧をCSVで出力するスクリプトを書いた。 出力されたCSVを眺めてみると、 最初に買ったKindle本は、2011年11月のThe Rails 3 Wayだった 所有情報は存在するが、読めなくなっている本があるようだ XML上はASINのみ出力され、書籍名は—などとなっており、どの本が読めなくなったのかわからない トータル1035冊あった Kindle Unlimitedで読んでいる本はXMLに出力されない などがわかり、なかなかおもしろかった。 Kindle for Macが生成するXMLからは、 ASIN 書籍名 著者 出版社 出版日 購入日 を取れるっぽいが、これに図書分類コード的なものがあれば、ジャンル別に分類したりもできたんだけどね。

Hugoで写真をリサイズしExif情報を表示する

以下のページを参考にして、Hugoで写真のリサイズとExif情報を表示するためのshortcodeを書いた。 Displaying Exif Tags with Hugo このShortcodeは画像をページリソースとして扱うことを前提にしているので注意が必要(static/imagesなどにおいてある画像には適用できない) Page Resources | Hugo GRのシャッタースピードが小数点表示されてる。ちょっと、まじめにExifを調べないとだめかな…