Skip to content

feat(sources): add YouTube search connector (yt-dlp) - #177

Merged
joshpanka merged 12 commits into
obris-dev:mainfrom
matthewdonsemail-lab:feat/youtube-search-connector
Sep 1, 2026
Merged

feat(sources): add YouTube search connector (yt-dlp)#177
joshpanka merged 12 commits into
obris-dev:mainfrom
matthewdonsemail-lab:feat/youtube-search-connector

Conversation

@matthewdonsemail-lab

Copy link
Copy Markdown
Contributor

Summary

Adds YouTube search capability to OpenMagpie using yt-dlp library, following the same patterns as the Twitter connector.

What's Changed

New Files

  • apps/core/sources/connectors/youtube/__init__.py
  • apps/core/sources/connectors/youtube/client.py (YtDlpClient wrapper)
  • apps/core/sources/connectors/youtube/connector.py (YouTubeSearchConnector)
  • apps/core/sources/connectors/youtube/errors.py (YouTubeError taxonomy)
  • apps/core/sources/connectors/youtube/payloads.py (NewVideoPayload)

Modified Files

  • packages/openmagpie-schema/src/openmagpie_schema/configs.py - Added YouTubeSearchSourceSpec
  • apps/core/pyproject.toml - Added yt-dlp>=2026.07.04 dependency
  • apps/core/sources/connectors/__init__.py - Exported YouTubeSearchConnector

Design Decisions

  1. Search API: Uses yt-dlp's ytsearch<N>:<query> URI scheme - no authentication required for public content
  2. Error Handling: 6 error codes with retry semantics (video_unavailable, rate_limited, js_runtime_missing, network_error, date_parse_error, yt_dlp_error)
  3. Payload Mapping: YouTube metadata -> SourcePayload with title, description, metrics (views/likes/comments), thumbnails, duration
  4. Watermark Filtering: Same dedup pattern as Twitter connector using last_event_at

Testing

Live search verified:

Found 2 videos for "AI news" query
- Zuckerberg makes 'STUNNING' announcement on AI race... (Fox News Clips, 69742 views)
- The AI Safety Tests Are Broken. All Of Them... (AI Revolution, 26092 views)

Full specification in: RESEARCH/YT_DLP_OPENMAGPIE_INTEGRATION.md

This PR stacks on top of PR #175 (X/Twitter connector). Merge that first.

Inference Ops added 3 commits August 13, 2026 14:24
- Import YouTubeSearchConnector
- Add to _REGISTRY dict with kind: youtube_search

The connector code was merged but the registry entry was missing.
This fixes the 'unknown source kind' error when polling YouTube feeds.
Adds YouTube search capability to OpenMagpie using yt-dlp library.

New files:
- apps/core/sources/connectors/youtube/__init__.py
- apps/core/sources/connectors/youtube/client.py (YtDlpClient)
- apps/core/sources/connectors/youtube/connector.py (YouTubeSearchConnector)
- apps/core/sources/connectors/youtube/errors.py (YouTubeError taxonomy)
- apps/core/sources/connectors/youtube/payloads.py (NewVideoPayload)
- RESEARCH/YT_DLP_OPENMAGPIE_INTEGRATION.md

Modified:
- packages/openmagpie-schema/src/openmagpie_schema/configs.py
  - Added YouTubeSearchSourceSpec
  - Added to _BuiltinSourceSpec union
- apps/core/pyproject.toml
  - Added yt-dlp>=2026.07.04 dependency
- apps/core/sources/connectors/__init__.py
  - Exported YouTubeSearchConnector

Features:
- Public YouTube search via ytsearch<N>:<query> URI scheme
- No authentication required for public content
- Error taxonomy with retry semantics
- Watermark-based deduplication
- Metrics extraction (views, likes, comments)
- Thumbnail media attachment
- Duration tracking

Design follows existing Twitter connector patterns for consistency.

Signed-off-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Co-authored-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Signed-off-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Co-authored-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
@matthewdonsemail-lab
matthewdonsemail-lab force-pushed the feat/youtube-search-connector branch from 6781b33 to 44838c5 Compare August 13, 2026 07:26
Inference Ops and others added 9 commits August 13, 2026 14:31
Signed-off-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Co-authored-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Signed-off-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Co-authored-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Signed-off-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Co-authored-by: Inference Ops <7b4bd76efa854d8ae6847735c3c888594039cab6a498c249f289d78a1c0e65ca@inferencesaver.communities.buzz.xyz>
Keep-both resolution across the connector registration points, plus:
- configs.py and README.md restored to main's LF line endings (the branch
  edits were CRLF-rewritten, which is what conflicted the whole files);
  the YouTube spec and README additions re-applied on top.
- schema.json + web generated.ts regenerated for youtube_search (83 models).
- YouTubeError is now a real Exception; raising/catching the previous
  dataclass was a TypeError at runtime on any yt-dlp failure.
- Recency comes from YouTube's last-7-days upload filter (sp=EgIIAw==) +
  the watermark: YouTube removed sort-by-upload-date from search
  (yt-dlp/yt-dlp#15898), and the original relevance-ranked ytsearch let
  old popular videos starve new mentions out of the N slots. Count rides
  playlistend.
- Search results keep videos only (playlists/channels filtered), and one
  unextractable entry (age-gated, deleted) no longer aborts the page
  (ignoreerrors).
- occurred_at prefers full-resolution timestamp/release_timestamp over the
  day-granular upload_date, and floors to midnight (never now()) when both
  are missing; the watermark filter skips strictly-older only, so
  same-day midnight-floored videos re-yield and dedup absorbs them.
- NewVideoPayload mirrored into openmagpie_schema.feed_payloads +
  FeedItemData; schema.json and web generated.ts regenerated (84 models).
- youtube_search added to the builtin-kind invariant test.
- Connector test suite added (15 tests); ruff RUF022/SIM105 fixed.
- Verified live: week-window search pulls current videos end to end.
The dev compose flow re-syncs the venv inside the runtime image (uv run
over the mounted workspace), which fetches the git-pinned twikit dep;
without git the container fails at startup.
apps/core/credentials/<connector>/ holds session material (twitter cookie
exports, youtube cookies.txt); everything but the README is gitignored so
credentials can't be committed. TWITTER_CREDENTIALS_DIR's bare
'credentials' default resolves relative to CWD (which differs between
runserver and gunicorn --chdir), so the README documents absolute paths.
@joshpanka
joshpanka merged commit eba9c80 into obris-dev:main Sep 1, 2026
4 checks passed
@joshpanka

Copy link
Copy Markdown
Contributor

@matthewdonsemail-lab Merged, thanks again for the contribution! I added some changes on top before merging: a starter example, a YOUTUBE_COOKIES_FILE env var (with a credentials dir convention that covers the twitter cookie files too), and a fix to the search itself. YouTube removed sort-by-upload-date entirely (why yt-dlp dropped ytsearchdate), so it now uses the "this week" upload filter to keep results recent. Got it running locally end to end through a feed and watch and things look good.

Will look at the facebook piece next.

@matthewdonsemail-lab

Copy link
Copy Markdown
Contributor Author

@matthewdonsemail-lab Merged, thanks again for the contribution! I added some changes on top before merging: a starter example, a YOUTUBE_COOKIES_FILE env var (with a credentials dir convention that covers the twitter cookie files too), and a fix to the search itself. YouTube removed sort-by-upload-date entirely (why yt-dlp dropped ytsearchdate), so it now uses the "this week" upload filter to keep results recent. Got it running locally end to end through a feed and watch and things look good.

Will look at the facebook piece next.

good to see you're finally checking the repo! @joshpanka I dont know how keen you are on sending this to the moon but we could make a good bit of money working together. I am about to set this up https://x.com/matthewsoldit/status/2094814046654759086 we're going to use openmagpie to send out 100k sms's to book meetings and we have a sales team. would be cool to collab because alot of this is going to be built around openmagpie.

I saw someone on twitter steal this exact idea https://www.stalkr.ai/ and I dont want to sell it as a saas product. I want to make it more of a service based thing. I am really f*ckin good at building lead acquisition systems so if we combined forces and made alot of unofficial api's I think we could really do something big with this!

https://listeningkit-frontend-web.vercel.app/ check it out will be up next week and we'll start our campaign.

joshpanka added a commit that referenced this pull request Sep 1, 2026
…he examples README

The twitter connector (#175) shipped without a starter, and the youtube
starter (#177) was never added to the examples README list. Adds the
twitter feed+watch starter mirroring youtube's, and lists both.
joshpanka added a commit that referenced this pull request Sep 1, 2026
…ate limits, docs)

Follow-ups on the X/Twitter (#175) and YouTube (#177) connectors after running
both live, plus a shared cleanup.

Twitter:
- Session config (cookies/proxy/credentials dir) flows through Django settings
  (TWITTER_COOKIES_JSON / COOKIE_AUTH_TOKEN / COOKIE_CT0 / COOKIES_FILE /
  CREDENTIALS_DIR / PROXY), resolved per search call so a rotated cookie export
  applies without a restart; the documented-but-unwired JSON/file routes now
  work. CREDENTIALS_DIR defaults to an absolute path.
- Skip timestamp-less tweets instead of minting now() (which would poison the
  watermark and strand every older-but-new tweet).
- Honor X's rate-limit reset with an in-cycle retry loop (mirrors Reddit's 429
  loop; X's x-rate-limit-reset is an absolute epoch), ticking the poll-lease
  heartbeat through the wait.
- Watermark filter uses strict < not <= (same-second tweets were lost).
- Pin twikit to the locked rev.

Shared:
- Promote sleep_with_heartbeat and rate_limit_delay to connectors/base so
  Reddit and Twitter share one definition each.
- Rename ListenerError -> TwitterError (mirrors YouTubeError; drops the
  'Listener' name AGENTS.md reserves).

Docs/examples:
- Twitter starter (feed + watch); list twitter + youtube in the examples README.
- Per-connector credentials/ directory convention (gitignored) with a guide for
  generating the Twitter and YouTube cookie exports.
- README: product mentions in 'What it does', YouTube in the listeners + diagram,
  and the changelog-style section replaced by a credentials/changelog pointer.

Both connectors verified live end to end (feed -> watch -> judge). Full core
suite, ruff, and ty green; reviewed via /review-pr with findings addressed.
joshpanka added a commit that referenced this pull request Sep 1, 2026
…ate limits, docs) (#180)

Follow-ups on the X/Twitter (#175) and YouTube (#177) connectors after running
both live, plus a shared cleanup.

Twitter:
- Session config (cookies/proxy/credentials dir) flows through Django settings
  (TWITTER_COOKIES_JSON / COOKIE_AUTH_TOKEN / COOKIE_CT0 / COOKIES_FILE /
  CREDENTIALS_DIR / PROXY), resolved per search call so a rotated cookie export
  applies without a restart; the documented-but-unwired JSON/file routes now
  work. CREDENTIALS_DIR defaults to an absolute path.
- Skip timestamp-less tweets instead of minting now() (which would poison the
  watermark and strand every older-but-new tweet).
- Honor X's rate-limit reset with an in-cycle retry loop (mirrors Reddit's 429
  loop; X's x-rate-limit-reset is an absolute epoch), ticking the poll-lease
  heartbeat through the wait.
- Watermark filter uses strict < not <= (same-second tweets were lost).
- Pin twikit to the locked rev.

Shared:
- Promote sleep_with_heartbeat and rate_limit_delay to connectors/base so
  Reddit and Twitter share one definition each.
- Rename ListenerError -> TwitterError (mirrors YouTubeError; drops the
  'Listener' name AGENTS.md reserves).

Docs/examples:
- Twitter starter (feed + watch); list twitter + youtube in the examples README.
- Per-connector credentials/ directory convention (gitignored) with a guide for
  generating the Twitter and YouTube cookie exports.
- README: product mentions in 'What it does', YouTube in the listeners + diagram,
  and the changelog-style section replaced by a credentials/changelog pointer.

Both connectors verified live end to end (feed -> watch -> judge). Full core
suite, ruff, and ty green; reviewed via /review-pr with findings addressed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants