feat: add video duration to YouTube announcements
Fetches duration via InnerTube player API for new videos at announcement time. Displayed as compact h:mm:ss before views/likes. Gracefully omitted for Shorts and unavailable content. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ from plugins.youtube import ( # noqa: E402
|
||||
_derive_name,
|
||||
_errors,
|
||||
_extract_channel_id,
|
||||
_format_duration,
|
||||
_is_youtube_url,
|
||||
_load,
|
||||
_parse_feed,
|
||||
@@ -827,18 +828,26 @@ class TestCmdYtCheck:
|
||||
}
|
||||
_save(bot, "#test:news", data)
|
||||
|
||||
def fake_duration(video_id):
|
||||
return {"def456": 1105, "ghi789": 62}.get(video_id, 0)
|
||||
|
||||
async def inner():
|
||||
with patch.object(_mod, "_fetch_feed", _fake_fetch_ok):
|
||||
with (
|
||||
patch.object(_mod, "_fetch_feed", _fake_fetch_ok),
|
||||
patch.object(_mod, "_fetch_duration", fake_duration),
|
||||
):
|
||||
await cmd_yt(bot, _msg("!yt check news"))
|
||||
announcements = [s for t, s in bot.sent if t == "#test"]
|
||||
assert len(announcements) == 2
|
||||
assert "[news]" in announcements[0]
|
||||
assert "Calculus" in announcements[0]
|
||||
# Verify metadata suffix (views, likes, date)
|
||||
assert "| " in announcements[0]
|
||||
# Verify metadata suffix (duration, views, likes, date)
|
||||
assert "18:25" in announcements[0]
|
||||
assert "820kv" in announcements[0]
|
||||
assert "32klk" in announcements[0]
|
||||
assert "2026-02-01" in announcements[0]
|
||||
# Second announcement has 1:02 duration
|
||||
assert "1:02" in announcements[1]
|
||||
|
||||
asyncio.run(inner())
|
||||
|
||||
@@ -930,7 +939,10 @@ class TestPollOnce:
|
||||
_channels[key] = data
|
||||
|
||||
async def inner():
|
||||
with patch.object(_mod, "_fetch_feed", fake_big):
|
||||
with (
|
||||
patch.object(_mod, "_fetch_feed", fake_big),
|
||||
patch.object(_mod, "_fetch_duration", lambda vid: 0),
|
||||
):
|
||||
await _poll_once(bot, key, announce=True)
|
||||
messages = [s for t, s in bot.sent if t == "#test"]
|
||||
# 5 individual + 1 "... and N more"
|
||||
@@ -1177,3 +1189,33 @@ class TestCompactNum:
|
||||
|
||||
def test_fractional_m(self):
|
||||
assert _compact_num(2_500_000) == "2.5M"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestFormatDuration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestFormatDuration:
|
||||
def test_zero(self):
|
||||
assert _format_duration(0) == ""
|
||||
|
||||
def test_negative(self):
|
||||
assert _format_duration(-1) == ""
|
||||
|
||||
def test_seconds_only(self):
|
||||
assert _format_duration(45) == "0:45"
|
||||
|
||||
def test_minutes_and_seconds(self):
|
||||
assert _format_duration(125) == "2:05"
|
||||
|
||||
def test_exact_minutes(self):
|
||||
assert _format_duration(600) == "10:00"
|
||||
|
||||
def test_hours(self):
|
||||
assert _format_duration(3661) == "1:01:01"
|
||||
|
||||
def test_large_hours(self):
|
||||
assert _format_duration(36000) == "10:00:00"
|
||||
|
||||
def test_one_second(self):
|
||||
assert _format_duration(1) == "0:01"
|
||||
|
||||
Reference in New Issue
Block a user