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

MiniFlux

MiniFluxは、シンプルで軽量なオープンソースのRSSフィードリーダー。セルフホスティングも可能。Webベースのインターフェースを提供し、ブラウザからどのデバイスからでもアクセス可能である。

主な機能として広告や不要な要素を取り除く「クリーンな記事表示機能」、未読・既読の管理、スター機能(お気に入り登録)、タグ付け、キーボードショートカットのサポートなどがある。

Miniflux - Minimalist and Opinionated Feed Reader

Miniflux Overview
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モーダルダイアログを閉じる

関連記事

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)

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

国立国会図書館サーチ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で得られる。

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://というカスタムプロトコルがこのアプリケーションに関連付けられる。ここでいちどこのアプリケーションを起動しておく。