feat(sources): add YouTube search connector (yt-dlp) - #177
Conversation
- 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>
6781b33 to
44838c5
Compare
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.
|
@matthewdonsemail-lab Merged, thanks again for the contribution! I added some changes on top before merging: a starter example, a 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. |
…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.
…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.
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__.pyapps/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 YouTubeSearchSourceSpecapps/core/pyproject.toml- Added yt-dlp>=2026.07.04 dependencyapps/core/sources/connectors/__init__.py- Exported YouTubeSearchConnectorDesign Decisions
ytsearch<N>:<query>URI scheme - no authentication required for public contentTesting
Live search verified:
Full specification in:
RESEARCH/YT_DLP_OPENMAGPIE_INTEGRATION.mdThis PR stacks on top of PR #175 (X/Twitter connector). Merge that first.