Repository navigation
Conversation
With cookies configured, members-only videos are attempted like any other
video, and Youtarr remembers whether the account in the cookies could open
each one. Without cookies nothing changes: downloads keep filtering
members-only videos out.
- video_members_access stores one verdict per video (confirmed or denied)
with the SHA-256 of the cookie file it was made with. A verdict counts for
24 hours and only while the same cookies are configured, so replacing or
editing the file (external files included) retires every verdict, while
settings saves and restarts keep them.
- Only a "Join this channel" refusal from a run that passed the cookies
records a denial. Timeouts, bot checks, anonymous auto-retries and cookies
YouTube rejected ("cookies are no longer valid", rotated) record nothing
about membership.
- Every successful yt-dlp read or download of a members-only video records
confirmed access, including after an earlier denial.
- Scheduled channel sweeps, playlist downloads, retries and Download All
leave fresh denials out through the yt-dlp match filter (id!~=), so a
denied video is not requested again for 24 hours. Manual downloads clear
the requested videos' verdicts and always ask again.
- Channel and playlist listings return members_access (no_cookies,
unchecked, confirmed, denied) for every video known to be members-only,
including videos of untracked channels, with one cookie-key lookup per
request. Both pages share one selection rule (isMembersOnlyBlocked) and
one chip; a local file or its removal takes priority over membership.
- The video modal checks access when it opens an unchecked video and offers
"Check again" for a denied one (POST /api/videos/:id/members-access/check,
rate-limited). Manual URL checks explain no cookies, no membership and
rejected cookies separately.
Refs DialmasterOrg#857
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Thanks for submitting this PR. You addressed a lot of the gaps from #851.
There are still some things to fix before this can go in. I left inline comments on each one.
A few things are in files the PR doesn't touch, so I couldn't comment on them inline:
- These still say members-only videos can't be downloaded:
client/src/components/PlaylistPage.tsxline 473client/src/components/Subscriptions/PlaylistHelpDialog.tsxline 75client/src/utils/playlistCounts.tsline 11docs/USAGE_GUIDE.mdline 214docs/YOUTARR_VS_ALTERNATIVES.mdline 94
- Swagger needs the new fields on
/getchannelvideos,/api/videos/{id}/metadataand/api/checkYoutubeVideoURL. - The playlist page doesn't pass
onAvailabilityDetected, so a row doesn't update after a check in the modal.
Can you also add tests for these? Right now they could break without any test failing:
- the query behind the denied list (
deniedonly, same cookie key, under 24 hours) - scheduled channel runs passing the denial filter and the no-cookies filter to yt-dlp
- the real
isBlockedState(the shared mock has its own copy) - the members-only chip and checkbox in the channel views
- Only 11-character video ids are stored or put in the yt-dlp filter. - A denial needs yt-dlp to really get --cookies (spawnYtDlp reports cookiesPassed) and a live login cookie in the file. - Playlist jobs drop fresh denials before queuing instead of relying on the match filter, which yt-dlp checks only after fetching a video URL. - External API requests keep members-only videos out. - A successful read of a video that is no longer members-only forgets it; the modal treats a null check answer as not members-only. - The modal notifies the parent once per check answer (no render loop), offers Check access for unchecked videos, and the playlist page passes onAvailabilityDetected. - Playlist rows: file status comes before the members-only rule. - Manual download list shows the members chip for openable videos. - Members-only UUM playlists are listed anonymously again. - Swagger fields, stale "cannot be downloaded" texts, and tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the thorough review. Everything is in 02ff63b:
Backend 6929 / frontend 5631 tests pass; lint and typecheck are clean. |
dialmaster
left a comment
There was a problem hiding this comment.
Thanks for addressing my first review so quickly! I resolved all the existing comments, but when I re-reviewed I found a few more issues. See my new comments.
- Downloads keep availability!=subscriber_only when the cookie file has no live login or cannot be read, since such refusals never record a denial and every run would retry them. - Auto-retries of External API requests carry an externalRequest flag, so they keep the members-only filter despite their Auto-retry label. - The members-only overlay offers Check again without cookies too, so a video that went public can be cleared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the second look! All three comments are addressed in 7a1763a, each with a test, and |
dialmaster
left a comment
There was a problem hiding this comment.
Thanks again for the additional changes. Everything I raised in the last round was addressed, but I thought about this a bit more and added a few more comments.
Also, a few comments for files that are not in the diff that need a line each:
- docs/EXTERNAL_API.md ("Create requests"): External API requests never use the owner's membership, so members-only videos are skipped. Only CONFIG.md says this right now
- docs/API_INTEGRATION.md (POST /api/videos/download): downloads with a legacy API key can now get members-only videos using the configured cookies. That's a change for anyone who has given out a key.
Can you add those too?
Once that's done... I think we've covered everything and this will be good to merge :)
| <Typography variant="body2" className="text-muted-foreground"> | ||
| Cookies let yt-dlp act as a signed-in YouTube account, which can get past | ||
| "Sign in to confirm you're not a bot" checks. | ||
| "Sign in to confirm you're not a bot" checks. They also unlock members-only |
There was a problem hiding this comment.
This text now gives people a reason to use cookies from their real account, since that's usually the one with the membership. But YouTube can restrict or ban accounts that are used for automated downloads, and we don't say that anywhere here. The card right below says "use a throwaway account", but only because the file gives access to the account.
Can you add a sentence here saying that using a main account is at the user's own risk, with a link to the yt-dlp warning? https://github.com/yt-dlp/yt-dlp/wiki/Extractors#exporting-youtube-cookies
I think the same note should go in the "Members-Only Videos" section of docs/CONFIG.md, and the warning in CookieFileCard.tsx should mention the ban risk too so they all say the same thing.
| channel, or add it manually: a manual download always asks again. | ||
| - Nothing is recorded about membership when the cookies could not really be | ||
| used: the file has no signed-in YouTube session, its login cookies have | ||
| expired, YouTube rejected them (rotated), or the file could not be read for |
There was a problem hiding this comment.
I don't think this is right for rejected cookies. The file still has unexpired login cookies, so loginCookiesLive is true and the old filter is dropped. The refusal isn't saved as a denial either, and nothing remembers that the cookies were rejected. So each scheduled channel run tries the members-only videos again until the cookies are replaced.
It's the same loop as the expired cookies one, just a different way in. Can you either remember the rejection for that cookie key and keep availability!=subscriber_only while it stands, or change this sentence to match what happens?
Refs #857
Lets Youtarr download members-only videos when the account in the configured cookies is a member, while keeping today's behavior without cookies. It is built around the review points in #857 and on #851 (I've been running a simpler version of this in my own build and wanted the gaps you listed closed). Happy to fold this into #851 instead if @Agile86 is still working on it.
How it works
Access records (
video_members_access,server/modules/membersAccess.js): one row per members-only video withaccess(confirmed/denied),cookie_key(SHA-256 of the cookie file) andchecked_at.readExternalCookies).What counts as a denial: only a "Join this channel" refusal from a run where yt-dlp really got
--cookies(spawnYtDlpreportscookiesPassed; preparation drops the flag for an unusable file), the file holds a live login cookie (cookieDetails), and nocookies are no longer valid/ rotated warning was printed. Timeouts, bot checks, network errors, anonymous auto-retries, logged-out or expired files and rejected cookies record nothing about membership.Ids: only
[A-Za-z0-9_-]{11}ids are stored or put into the filter, and the filter checks again, since ids come from yt-dlp output an uploader can shape with a title.Recovery: any successful yt-dlp read or download of a members-only video records
confirmed, including after an earlier denial (validation, modal metadata fetch, downloads). A successful read showing another availability deletes the row and resets the channelvideos availability, so a video that went public stops reading as members-only.Downloads
availability!=subscriber_onlystays in every match filter, exactly as today.id!~='^(?:id1|id2)$'for fresh denials (your suggestion on feat: support members-only video downloads when cookies are enabled #851), so a denied video is skipped at the flat-listing stage without another request. Checked against yt-dlp 2026.10.26'smatch_filter_func.availability!=subscriber_only, so an External API key never uses the owner's membership. Legacy API-key jobs behave like manual downloads.Listings and UI
members_access(no_cookies|unchecked|confirmed|denied) for every video known to be members-only. A recorded check also covers videos from untracked channels on playlist pages.isMembersOnlyBlockedinvideoStatus.tsx) and one chip (shared/MembersAccessChip) with the same colors on both pages. A local file, or its removal, takes priority over membership..info.jsonis never re-fetched) and Check again for a denied one (POST /api/videos/:youtubeId/members-access/check, 10/min). Anullanswer means the video is no longer members-only. The channel and playlist pages both update the row from the modal's answer.Cookie settings text,
docs/CONFIG.md, the playlist texts in the app anddocs/USAGE_GUIDE.md/docs/YOUTARR_VS_ALTERNATIVES.mddescribe the behavior; Swagger covers the new fields on/getchannelvideos,/api/videos/{id}/metadataand/api/checkYoutubeVideoURL;docs/DATABASE.md,CLAUDE.mdand the rule files are updated.Testing
npm run lintandnpm run lint:tsare clean.membersAccess.test.js(including the denied-list query:deniedonly, same cookie key, under 24 hours; crafted ids),membersAccessState.test.js(the realisBlockedState, shared by the Jest mock), scheduled channel runs passing the denial and no-cookies filters, playlist denial pre-filtering, External API jobs,cookiesPassed/usedCookies, the check route, validation/metadata/finalizer paths, listing fields, the members chip and checkbox in the card, list and table views,MembersAccessChip,useMembersAccessCheck, the modal check flow (including an inline parent callback that sets state) and the shared selection rule.npm run docs:check(website deps not installed; no links were added).🤖 Generated with Claude Code