371 日記 | 過去3日分を古い順に再生 episode artwork

EPISODE · Jul 1, 2026 · 13 MIN

371 日記 | 過去3日分を古い順に再生

from TanaRadio · host たな

あるポッドキャストの過去3日分を古い順に再生できるようになりました(フェーズ5B完了)。フェーズ5B 詳細マニュアル 過去3日分だけを古い順に並べる 0. 今回の目標 5Aでは、RSSから取得したエピソードをそのまま playlist.m3u にしました。 5Bでは、そこに次の条件を加えます。 過去3日分だけを対象にする ↓ 古いエピソードから新しいエピソードへ並べる ↓ mpvで連続再生する つまり、こういう再生になります。 3日前のエピソード ↓ 2日前のエピソード ↓ 昨日のエピソード ↓ 今日のエピソード これは「最新からつまみ食い」ではなく、「数日分を朝のラジオのように順番に追いつく」ためのモードです。構想メモでも、この「3日前から古い順」は追いつき再生として位置づけられています。 今回はまだ、以下はやりません。 複数RSS 棚選び ボタン追加 LED表示 ロータリーエンコーダー 再生履歴 ここ、欲張らなくて正解です。5Bは地味ですが、ここで「時間順に声を流す」感覚が出てきます。ラジオらしさが一段増します。 ステップ1:作業フォルダに移動する Raspberry Piでターミナルを開きます。 cd ~/tanaradio5 確認します。 pwd ls 次のようなファイルが見えればOKです。 feed.txt make_playlist_5a.py playlist.m3u 5Aが成功しているので、たぶんこの状態になっているはずです。 ステップ2:5Aのファイルは残しておく 5Aのスクリプトは成功版として残します。 今回は新しく、 make_playlist_5b.py を作ります。 5Aを上書きしない方が安全です。工作でもプログラムでも、成功した状態を残すのは大事です。ここを雑にすると、あとで「昨日の自分、何をした?」となります。昨日の自分はだいたいメモを残していません。困ったものです。 ステップ3:5B用スクリプトを作る 次を入力します。 nano make_playlist_5b.py 開いたら、以下をそのまま貼り付けてください。 #!/usr/bin/env python3 import urllib.request import xml.etree.ElementTree as ET from pathlib import Path from datetime import datetime, timedelta, timezone from email.utils import parsedate_to_datetime FEED_FILE = "feed.txt" PLAYLIST_FILE = "playlist_5b.m3u" INFO_FILE = "playlist_5b_info.txt" # 5Bでは「過去3日分」を対象にする DAYS_BACK = 3 # 表示用。日本時間で確認できるようにする JST = timezone(timedelta(hours=9)) def read_feed_url(): path = Path(FEED_FILE) if not path.exists(): raise FileNotFoundError(f"{FEED_FILE} が見つかりません。") url = path.read_text(encoding="utf-8").strip() if not url: raise ValueError(f"{FEED_FILE} にRSS URLが書かれていません。") return url def download_rss(url): print(f"RSSを取得します: {url}") request = urllib.request.Request( url, headers={ "User-Agent": "TanaRadio-Pi/5B" } ) with urllib.request.urlopen(request, timeout=20) as response: return response.read() def find_audio_url(item): # RSSのenclosureタグから音声URLを探す enclosure = item.find("enclosure") if enclosure is not None: audio_url = enclosure.attrib.get("url") if audio_url: return audio_url # 念のため media:content 形式にも軽く対応する for child in item: if child.tag.endswith("content"): audio_url = child.attrib.get("url") if audio_url: return audio_url return None def get_text(item, tag_name): element = item.find(tag_name) if element is not None and element.text: return element.text.strip() return "" def parse_pub_date(item): text = get_text(item, "pubDate") if not text: return None # RSSのpubDateは多くの場合、次のような形式 # Tue, 30 Jun 2026 08:00:00 +0900 # Tue, 30 Jun 2026 00:00:00 GMT try: dt = parsedate_to_datetime(text) except Exception: return None # タイムゾーン情報がない場合はUTCとして扱う if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) # 比較しやすいようにUTCへ変換 return dt.astimezone(timezone.utc) def format_dt_jst(dt): return dt.astimezone(JST).strftime("%Y-%m-%d %H:%M") def collect_episodes(rss_data): root = ET.fromstring(rss_data) channel = root.find("channel") if channel is None: raise ValueError("RSSのchannelが見つかりません。") items = channel.findall("item") if not items: raise ValueError("RSS内にitemが見つかりません。") episodes = [] skipped_no_audio = 0 skipped_no_date = 0 for item in items: title = get_text(item, "title") audio_url = find_audio_url(item) pub_date = parse_pub_date(item) if not audio_url: skipped_no_audio += 1 continue if pub_date is None: skipped_no_date += 1 continue episodes.append({ "title": title if title else "タイトルなし", "audio_url": audio_url, "pub_date": pub_date, }) return episodes, skipped_no_audio, skipped_no_date def make_playlist(episodes, skipped_no_audio, skipped_no_date): now = datetime.now(timezone.utc) cutoff = now - timedelta(days=DAYS_BACK) selected = [ episode for episode in episodes if episode["pub_date"] >= cutoff ] # 古い順に並べる selected.sort(key=lambda episode: episode["pub_date"]) info_lines = [] info_lines.append("TanaRadio Pi フェーズ5B プレイリスト情報") info_lines.append("") info_lines.append(f"対象期間: 過去{DAYS_BACK}日分") info_lines.append(f"現在時刻: {format_dt_jst(now)} JST") info_lines.append(f"対象開始: {format_dt_jst(cutoff)} JST") info_lines.append("") info_lines.append(f"RSSから取得した音声付きエピソード数: {len(episodes)} 件") info_lines.append(f"音声URLなしでスキップ: {skipped_no_audio} 件") info_lines.append(f"公開日時なし/日時解析失敗でスキップ: {skipped_no_date} 件") info_lines.append(f"今回プレイリストに入れる数: {len(selected)} 件") info_lines.append("") info_lines.append("今回入れるエピソード:") info_lines.append("") for i, episode in enumerate(selected, start=1): info_lines.append( f"{i}. {format_dt_jst(episode['pub_date'])} JST | {episode['title']}" ) Path(INFO_FILE).write_text( "\n".join(info_lines) + "\n", encoding="utf-8" ) if not selected: raise ValueError( f"過去{DAYS_BACK}日分のエピソードが見つかりませんでした。" f"確認用に {INFO_FILE} を見てください。" ) playlist_lines = ["#EXTM3U"] for episode in selected: playlist_lines.append( f"# {format_dt_jst(episode['pub_date'])} JST | {episode['title']}" ) playlist_lines.append(episode["audio_url"]) Path(PLAYLIST_FILE).write_text( "\n".join(playlist_lines) + "\n", encoding="utf-8" ) print(f"{PLAYLIST_FILE} を作成しました。") print(f"{INFO_FILE} を作成しました。") print(f"対象エピソード数: {len(selected)} 件") def main(): try: feed_url = read_feed_url() rss_data = download_rss(feed_url) episodes, skipped_no_audio, skipped_no_date = collect_episodes(rss_data) make_playlist(episodes, skipped_no_audio, skipped_no_date) print("完了しました。") except Exception as e: print("エラーが発生しました。") print(e) if __name__ == "__main__": main() 保存します。 Ctrl + O Enter Ctrl + X ステップ4:実行できるようにする 次を入力します。 chmod +x make_playlist_5b.py ただし、前回のような「import: コマンドが見つかりません」系の事故を避けるため、今回はまず python3 で実行します。 python3 make_playlist_5b.py うまくいくと、次のように表示されます。 RSSを取得します: https://listen.style/p/xxxxx/rss playlist_5b.m3u を作成しました。 playlist_5b_info.txt を作成しました。 対象エピソード数: 3 件 完了しました。 ステップ5:作成されたファイルを確認する 次を入力します。 ls 次のようなファイルが見えればOKです。 feed.txt make_playlist_5a.py make_playlist_5b.py playlist.m3u playlist_5b.m3u playlist_5b_info.txt 今回重要なのはこの2つです。 playlist_5b.m3u playlist_5b_info.txt ステップ6:プレイリスト情報を確認する まず、確認用ファイルを開きます。 cat playlist_5b_info.txt たとえば次のような表示になります。 TanaRadio Pi フェーズ5B プレイリスト情報 対象期間: 過去3日分 現在時刻: 2026-06-30 10:20 JST 対象開始: 2026-06-27 10:20 JST RSSから取得した音声付きエピソード数: 10 件 音声URLなしでスキップ: 0 件 公開日時なし/日時解析失敗でスキップ: 0 件 今回プレイリストに入れる数: 3 件 今回入れるエピソード: 1. 2026-06-28 08:00 JST | エピソードタイトルA 2. 2026-06-29 09:00 JST | エピソードタイトルB 3. 2026-06-30 07:30 JST | エピソードタイトルC ここで見るべき点は2つです。 1. 対象開始が「約3日前」になっているか 2. エピソードが古い順に並んでいるか この2つが合っていれば、5Bの中心部分は成功です。 ステップ7:playlist_5b.m3u の中身を確認する 次を入力します。 head playlist_5b.m3u 次のように表示されればOKです。 #EXTM3U # 2026-06-28 08:00 JST | エピソードタイトルA https://... # 2026-06-29 09:00 JST | エピソードタイトルB https://... 件数も確認できます。 grep -v '^#' playlist_5b.m3u | sed '/^$/d' | wc -l これで表示される数字が、playlist_5b_info.txt の「今回プレイリストに入れる数」と同じならOKです。 ステップ8:mpvで再生する いよいよ再生します。 mpv playlist_5b.m3u 音が出れば成功です。 mpvの基本操作は5Aと同じです。 スペースキー:一時停止/再開 Enter:次へ q:終了 聴いてみて、古いエピソードから順に流れているか確認してください。 ステップ9:うまくいったら通常名にコピーする 5B版がうまく動いたら、これを通常の playlist.m3u として使えるようにしておきます。 cp playlist_5b.m3u playlist.m3u その後は、いつものように次で再生できます。 mpv playlist.m3u ただし、今後しばらくは確認しやすいので、 mpv playlist_5b.m3u のまま使っても構いません。 ステップ10:5Bの成功条件 フェーズ5Bの成功条件は、次の4つです。 1. make_playlist_5b.py を作れた 2. playlist_5b.m3u が作成された 3. playlist_5b_info.txt で「過去3日分・古い順」を確認できた 4. mpv playlist_5b.m3u で再生できた この4つができれば、5Bは成功です。 よくあるエラー 1. 過去3日分のエピソードが見つかりませんでした これは、必ずしも失敗ではありません。 原因は単純で、RSS内に本当に過去3日分のエピソードがない可能性があります。 まず確認します。 cat playlist_5b_info.txt もし「RSSから取得した音声付きエピソード数」はあるのに、「今回プレイリストに入れる数」が0なら、日付条件に合うエピソードがなかったということです。 テストだけしたい場合は、スクリプト内のここを一時的に変えます。 DAYS_BACK = 3 たとえば30日にします。 DAYS_BACK = 30 保存して、もう一度実行します。 python3 make_playlist_5b.py これで動けば、スクリプト自体は正常です。最後に DAYS_BACK = 3 に戻してください。 2. 公開日時なし/日時解析失敗でスキップ が多い RSS内の pubDate がうまく読めていない可能性があります。 LISTENの通常RSSなら、おそらく大丈夫だと思います。 ただし、別サービスのRSSでは日付形式が少し違う場合があります。 この段階では、まずLISTENの公開RSSで試すのが安全です。 3. 音声URLなしでスキップ が多い これは、RSSに音声ファイルのURLが入っていない場合です。 原因としては次が考えられます。 RSS URLが番組配信用ではない 限定配信で取得できない RSS形式が特殊 5Bではまだ公開RSSだけで進めましょう。限定配信や購入済み音声は、ここに混ぜない方がいいです。沼に足を入れるのは、長靴を履いてからです。 4. 実行するとまた import: コマンドが見つかりません と出る その場合は、次のように実行してください。 python3 make_playlist_5b.py ./make_playlist_5b.py ではなく、python3 を前につけます。 それでもダメなら、ファイルの先頭行を確認します。 head -n 3 make_playlist_5b.py 先頭がこうなっていればOKです。 #!/usr/bin/env python3 import urllib.request import xml.etree.ElementTree as ET もし #!/usr/bin/env python3 より前に余計な文字や空行がある場合は、nano で直してください。 nano make_playlist_5b.py 今回はここで止めてよいです 5Bではここまでで十分です。 RSSを読む ↓ 公開日時を見る ↓ 過去3日分だけ選ぶ ↓ 古い順に並べる ↓ mpvで再生する これができれば、次の5Cで「RSSを複数本にする」準備が整います。 ここまで来ると、TanaRadio Piはかなり「フォロー中ラジオ」の原型に近づいてきます。まだボタンもLEDも増えていませんが、中身は確実にラジオらしくなっています。#声日記 #TanaRadioPi LISTENで開く

Episode metadata supplied by the publisher feed · Published Jul 1, 2026

Embed this episode

NOW PLAYING

371 日記 | 過去3日分を古い順に再生

0:00 13:10

No transcript for this episode yet

We transcribe on demand. Request one and we'll notify you when it's ready — usually under 10 minutes.

No similar episodes found.

No similar podcasts found.

Frequently Asked Questions

How long is this episode of TanaRadio?

This episode is 13 minutes long.

When was this TanaRadio episode published?

This episode was published on July 1, 2026.

Can I download this TanaRadio episode?

Yes. Use the download control on the episode player to save the publisher-provided media file.
URL copied to clipboard!