Add a "Discover from playlists" tab to the Channel manager that lists
channels appearing in the user's playlists they don't subscribe to, with
a one-click Subscribe.
- GET /api/channels/discovery: local join (playlist_items -> videos ->
channels) minus the user's subscriptions and their own channel. Enriches
stub channels' metadata up front (title/thumbnail/subscriber count via the
API key) so the user can judge a channel before subscribing; videos are
not pulled (the scheduler picks those up).
- POST /api/channels/{id}/subscribe: write-scope gated, subscriptions.insert
+ local Subscription with the returned resource id.
- YouTubeClient.insert_subscription / get_my_channel_id.
- users.yt_channel_id (migration 0019) caches the user's own channel id so
discovery can exclude it.
- Frontend: ChannelDiscovery DataTable, Channels tab toggle (persisted),
api methods, trilingual strings. Subscribe ships a typed notification
payload (ChannelSubscribedMeta) for the inbox to act on.
124 lines
6.7 KiB
JSON
124 lines
6.7 KiB
JSON
{
|
|
"intro": "Set a channel's <0>priority</0> to push its videos up when you sort by “Channel priority”, attach your own <1>tags</1> to filter the feed, or <2>hide</2> a channel to drop it from the feed without unsubscribing.",
|
|
"filterPlaceholder": "Filter channels…",
|
|
"syncSubscriptions": "Read subscriptions from YouTube",
|
|
"syncSubscriptionsHint": "Re-import your subscription list from YouTube — adds channels you've newly followed and drops ones you've unfollowed. The videos themselves keep syncing automatically in the background; this does not re-fetch them.",
|
|
"backfillEverything": "Backfill everything",
|
|
"backfillEverythingHint": "Request full back-catalog backfill for every channel you're subscribed to. Older videos and search become complete as the shared daily quota allows — this can take a while.",
|
|
"tabs": {
|
|
"subscribed": "Subscriptions",
|
|
"discovery": "Discover from playlists"
|
|
},
|
|
"discovery": {
|
|
"intro": "Channels that appear in your playlists but that you don't subscribe to. Subscribe to follow them — their new uploads will start showing in your feed (subscribing costs a little YouTube quota; existing videos aren't re-fetched).",
|
|
"loading": "Finding channels…",
|
|
"empty": "No new channels — every channel in your playlists is one you already follow.",
|
|
"subscribe": "Subscribe",
|
|
"subscribeHint": "Subscribe to this channel on YouTube (changes your real account; uses a little API quota). Its videos arrive on the next background sync.",
|
|
"needWriteHint": "Enable playlist editing in Settings to subscribe on YouTube.",
|
|
"subscribedTitle": "Subscribed on YouTube",
|
|
"subscribedBody": "You're now following {{name}} — its new uploads will start arriving in your feed.",
|
|
"subscribeFailed": "Subscribe failed",
|
|
"cols": {
|
|
"inPlaylists": "In playlists",
|
|
"inPlaylistsHint": "{{videos}} video(s) from this channel across {{playlists}} of your playlist(s)."
|
|
}
|
|
},
|
|
"filters": {
|
|
"all": "All",
|
|
"needsFull": "Needs full history",
|
|
"fullySynced": "Fully synced",
|
|
"hidden": "Hidden"
|
|
},
|
|
"tags": {
|
|
"yourTags": "Your tags",
|
|
"manage": "Manage tags",
|
|
"yourTagsHint": "Your personal labels. Attach them to channels below, then filter the feed by tag from the sidebar. (Separate from the automatic language/topic tags.)",
|
|
"newTag": "new tag",
|
|
"createTag": "Create tag"
|
|
},
|
|
"loading": "Loading channels…",
|
|
"empty": "No channels.",
|
|
"pager": {
|
|
"prev": "Previous",
|
|
"next": "Next",
|
|
"page": "Page {{page}} of {{total}}"
|
|
},
|
|
"cols": {
|
|
"priority": "Prio",
|
|
"channel": "Channel",
|
|
"stored": "Stored",
|
|
"subs": "Subscribers",
|
|
"lastUpload": "Last upload",
|
|
"length": "Length",
|
|
"types": "N/S/L",
|
|
"typesHint": "Videos by type: Normal / Shorts / Live",
|
|
"sync": "Sync",
|
|
"tags": "Tags",
|
|
"actions": "Actions"
|
|
},
|
|
"stats": {
|
|
"channels": "Channels",
|
|
"channelsHint": "Channels you're subscribed to.",
|
|
"recentSynced": "Recent synced",
|
|
"recentSyncedHint": "Channels whose recent uploads are in the local DB and show in your feed.",
|
|
"fullHistory": "Full history",
|
|
"fullHistoryHint": "Channels whose entire back-catalog is fetched, out of the ones you've requested full history for.",
|
|
"left": "left",
|
|
"leftHint": "Rough estimate to finish full-history backfill of {{count}} pending channel(s), based on the shared daily quota.",
|
|
"myVideos": "My videos",
|
|
"myVideosHint": "Total videos available across your channels.",
|
|
"quotaLeft": "Quota left",
|
|
"quotaLeftHint": "Shared YouTube API budget left today (resets midnight US Pacific)."
|
|
},
|
|
"row": {
|
|
"stored": "{{formatted}} stored",
|
|
"subs": "{{formatted}} subs",
|
|
"openOnYouTube": "Open on YouTube",
|
|
"editTags": "Edit tags",
|
|
"filterFeedByTag": "Filter the feed by “{{name}}”",
|
|
"priorityHint": "Your ranking for this channel. Sort the feed by “Channel priority” to bring higher-priority channels to the top.",
|
|
"raisePriority": "Raise priority",
|
|
"lowerPriority": "Lower priority",
|
|
"recent": "recent",
|
|
"recentSyncedHint": "Recent uploads synced.",
|
|
"recentNotSyncedHint": "Recent uploads not fetched yet.",
|
|
"full": "full",
|
|
"fullHint": "Full history fetched.",
|
|
"fullNotFetchedHint": "Full back-catalog not fetched yet — use the backfill action in the Actions column.",
|
|
"fullySynced": "fully synced",
|
|
"fullySyncedHint": "Recent uploads and the full back-catalog are both in.",
|
|
"backfillThis": "Backfill this channel",
|
|
"backfillThisHint": "Fetch this channel's full back-catalog (older videos + complete search). Click again to cancel the request.",
|
|
"resetBackfill": "Reset & re-fetch this channel",
|
|
"resetHint": "Admin: re-fetch this channel from scratch (recent + full back-catalog), regardless of its current sync state.",
|
|
"fullHistoryQueued": "full history queued",
|
|
"fullHistoryComing": "full history coming",
|
|
"queuedRequestedHint": "Full history requested — this channel's whole back-catalog will backfill as the shared quota allows. Click to cancel your request.",
|
|
"queuedByOtherHint": "Another subscriber already requested this channel's full history, so its whole back-catalog is on its way to everyone — nothing to do here.",
|
|
"getFullHistory": "get full history",
|
|
"getFullHistoryHint": "Only recent uploads so far. Click to request this channel's full back-catalog (older videos + complete search).",
|
|
"hiddenHint": "Hidden — this channel's videos are kept out of your feed. Click to show them again.",
|
|
"hideHint": "Hide this channel's videos from your feed. It stays subscribed; this doesn't unsubscribe on YouTube.",
|
|
"unhide": "Unhide",
|
|
"hideFromFeed": "Hide from feed",
|
|
"unsubscribeHint": "Unsubscribe from this channel on YouTube (changes your real account). Read-only mode hides this — use Hide instead.",
|
|
"unsubscribeOnYoutube": "Unsubscribe on YouTube",
|
|
"thisChannel": "This channel"
|
|
},
|
|
"notify": {
|
|
"synced": "Synced {{count}} subscriptions",
|
|
"syncFailed": "Subscription sync failed",
|
|
"unsubscribed": "Unsubscribed on YouTube",
|
|
"unsubscribeFailed": "Unsubscribe failed",
|
|
"fullHistoryRequested": "Full history requested for {{count}} channels",
|
|
"fullHistoryFailed": "Couldn't request full history",
|
|
"needYouTube": "Connect your YouTube account to do this.",
|
|
"connect": "Connect",
|
|
"filteredByTag": "Feed filtered by tag: {{name}}",
|
|
"resetDone": "Channel reset — re-fetching now",
|
|
"resetFailed": "Couldn't reset this channel"
|
|
},
|
|
"confirmUnsubscribe": "Unsubscribe from \"{{name}}\" on YouTube? This changes your real YouTube account. To just remove it from your feed, hide it instead.",
|
|
"confirmReset": "Re-fetch \"{{name}}\" from scratch? This re-runs its backfill (recent + full history) and spends some API quota."
|
|
}
|