Skip to content

Commit 51426a4

Browse files
BUMP: v3.2.9 subscribe WHITELIST feature
1 parent d8a1425 commit 51426a4

25 files changed

Lines changed: 1364 additions & 265 deletions

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## Version 3.2.9
4+
5+
### Subscribed Channels Import + Whitelist Flow
6+
7+
- **Subscribed channels import**: Added a Tab View flow that imports the active YouTube account's subscriptions into whitelist.
8+
- **YouTube tab startup hardening**: The import now reuses or opens a YouTube tab, routes it to `/feed/channels`, and waits for the FilterTube bridge before starting.
9+
- **Whitelist semantics clarified**: `Import Only` keeps the current blocklist untouched, while `Import + Turn On Whitelist` follows the existing blocklist-to-whitelist migration behavior.
10+
- **Documentation refresh**: Added dedicated import docs plus updated architecture, technical, pipeline, renderer, and developer docs for the new flow.
11+
312
## Version 3.2.8
413

514
### Theme Refresh + Scenic UI Shell Tuning

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<h1>FilterTube</h1>
44
<p>Peace of Mind for your Digital Space</p>
55

6-
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/varshneydevansh/FilterTube) ![Version](https://img.shields.io/badge/version-3.2.8-blue.svg) ![License](https://img.shields.io/badge/license-MIT-green.svg) ![Lines of Code](https://img.shields.io/badge/total%20lines-87.1k-brightgreen.svg) ![JavaScript LoC](https://img.shields.io/badge/javascript-53.0k%20lines-yellow.svg) ![Top Language](https://img.shields.io/github/languages/top/varshneydevansh/FilterTube?color=f1e05a) ![Repo Size](https://img.shields.io/github/repo-size/varshneydevansh/FilterTube?color=orange)
6+
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/varshneydevansh/FilterTube) ![Version](https://img.shields.io/badge/version-3.2.9-blue.svg) ![License](https://img.shields.io/badge/license-MIT-green.svg) ![Lines of Code](https://img.shields.io/badge/total%20lines-87.9k-brightgreen.svg) ![JavaScript LoC](https://img.shields.io/badge/javascript-53.2k%20lines-yellow.svg) ![Top Language](https://img.shields.io/github/languages/top/varshneydevansh/FilterTube?color=f1e05a) ![Repo Size](https://img.shields.io/github/repo-size/varshneydevansh/FilterTube?color=orange)
77

88
</div>
99

css/tab-view.css

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,13 +1273,28 @@ html[data-theme="dark"] .card::after {
12731273
}
12741274

12751275
.subscriptions-import-trigger {
1276+
display: inline-flex;
1277+
align-items: center;
1278+
gap: 0.58rem;
12761279
min-height: 44px;
12771280
white-space: nowrap;
12781281
justify-content: center;
12791282
flex: 0 0 auto;
12801283
border-radius: 999px;
12811284
}
12821285

1286+
.subscriptions-import-trigger.is-loading::before {
1287+
content: '';
1288+
width: 0.92rem;
1289+
height: 0.92rem;
1290+
border-radius: 50%;
1291+
border: 2px solid currentColor;
1292+
border-right-color: transparent;
1293+
flex: 0 0 auto;
1294+
opacity: 0.82;
1295+
animation: ft-subscriptions-import-spin 0.78s linear infinite;
1296+
}
1297+
12831298
.subscriptions-import-inline {
12841299
display: grid;
12851300
grid-template-columns: minmax(0, 1fr) auto;
@@ -1300,6 +1315,24 @@ html[data-theme="dark"] .card::after {
13001315
line-height: 1.4;
13011316
}
13021317

1318+
.subscriptions-import-inline.is-loading .subscriptions-import-status {
1319+
display: inline-flex;
1320+
align-items: center;
1321+
gap: 0.62rem;
1322+
}
1323+
1324+
.subscriptions-import-inline.is-loading .subscriptions-import-status::before {
1325+
content: '';
1326+
width: 0.88rem;
1327+
height: 0.88rem;
1328+
border-radius: 50%;
1329+
border: 2px solid currentColor;
1330+
border-top-color: transparent;
1331+
flex: 0 0 auto;
1332+
opacity: 0.7;
1333+
animation: ft-subscriptions-import-spin 0.78s linear infinite;
1334+
}
1335+
13031336
.subscriptions-import-actions {
13041337
display: flex;
13051338
align-items: center;
@@ -1356,6 +1389,12 @@ html[data-theme="dark"] .card::after {
13561389
flex-wrap: wrap;
13571390
}
13581391

1392+
@keyframes ft-subscriptions-import-spin {
1393+
to {
1394+
transform: rotate(360deg);
1395+
}
1396+
}
1397+
13591398
:root[data-theme="dark"] .subscriptions-import-inline {
13601399
border-color: rgba(176, 135, 70, 0.32);
13611400
background: linear-gradient(180deg, rgba(46, 38, 29, 0.94), rgba(33, 28, 23, 0.92));
@@ -1387,6 +1426,13 @@ html[data-theme="dark"] .card::after {
13871426
color: var(--ft-color-text-secondary-dark);
13881427
}
13891428

1429+
@media (prefers-reduced-motion: reduce) {
1430+
.subscriptions-import-trigger.is-loading::before,
1431+
.subscriptions-import-inline.is-loading .subscriptions-import-status::before {
1432+
animation: none;
1433+
}
1434+
}
1435+
13901436
.text-input {
13911437
flex: 1;
13921438
padding: var(--ft-space-sm) var(--ft-space-md);

data/release_notes.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22
{
33
"_comment": "Shared release notes source for the dashboard What's New tab and the YouTube release banner. Keep entries sorted newest-first by version."
44
},
5+
{
6+
"version": "3.2.9",
7+
"headline": "Subscribed Channels Import + Whitelist + PIN protection bug fix",
8+
"summary": "Import the active YouTube account's subscriptions into whitelist with clearer mode-switch semantics and a more reliable /feed/channels startup flow.",
9+
"bannerSummary": "Build whitelist from your current YouTube subscriptions with inline progress and clearer whitelist activation behavior.",
10+
"highlights": [
11+
"New Tab View import: subscribed channels can be imported from the active signed-in YouTube account into whitelist.",
12+
"Import startup now reuses a live YouTube tab, routes it to /feed/channels, and waits for the FilterTube bridge before starting.",
13+
"Whitelist copy is clearer: Import Only leaves the blocklist untouched, while Import + Turn On Whitelist follows the existing blocklist-to-whitelist migration path.",
14+
"Documentation now covers the import architecture, FEchannels request flow, renderer sources, and whitelist semantics in much more detail."
15+
],
16+
"detailsUrl": "https://github.com/varshneydevansh/FilterTube/releases/tag/v3.2.9"
17+
},
518
{
619
"version": "3.2.8",
720
"headline": "Theme Refresh + Scenic UI Shell Tuning",

docs/ARCHITECTURE.md

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Architecture Documentation (v3.2.8)
1+
# Architecture Documentation (v3.2.9)
22

33
## Overview
44

@@ -86,6 +86,71 @@ const switchToWhitelist = async (profile, copyBlocklist = true) => {
8686
};
8787
```
8888

89+
## Subscribed Channels Import Architecture (v3.2.9 follow-up)
90+
91+
The subscriptions importer adds a second whitelist acquisition path alongside manual whitelist entry. It is intentionally an on-demand architecture, separate from passive feed filtering.
92+
93+
Detailed reference: `docs/SUBSCRIBED_CHANNELS_IMPORT.md`
94+
95+
### High-level flow
96+
97+
```mermaid
98+
graph TD
99+
A["Tab View: Import Subscribed Channels"] --> B["Resolve signed-in YouTube tab"]
100+
B --> C["Route tab to /feed/channels"]
101+
C --> D["Wait for page + FilterTube bridge"]
102+
D --> E["Isolated bridge forwards import request"]
103+
E --> F["MAIN world injector reads page seed"]
104+
F --> G["MAIN world injector requests FEchannels browse pages"]
105+
G --> H["Normalized subscription channel list"]
106+
H --> I["StateManager validates profile + lock state"]
107+
I --> J["Background merges into main.whitelistChannels"]
108+
J --> K{"Turn On Whitelist?"}
109+
K -->|No| L["Whitelist stored only"]
110+
K -->|Yes| M["Existing mode-switch path merges blocklist into whitelist"]
111+
```
112+
113+
### Why this architecture exists
114+
115+
- The importer must run from a real signed-in YouTube page context.
116+
- Popup/background-only state is not enough because the request depends on the authenticated YouTube tab plus the FilterTube bridge in that tab.
117+
- `/feed/channels` gives a predictable subscription page shell and lets the importer use both page-local data and active `youtubei` browse requests.
118+
119+
### Two whitelist-construction paths
120+
121+
Architecturally, FilterTube now needs to document two ways the whitelist can be built:
122+
123+
- **Direct whitelist population**
124+
- writes rows straight into `whitelistChannels` / `whitelistKeywords`
125+
- includes subscriptions import and normal whitelist-specific additions
126+
- **Mode-switch migration**
127+
- activates when `FilterTube_SetListMode('whitelist')` runs
128+
- merges current blocklist channels/keywords into whitelist and clears the blocklist
129+
130+
The subscriptions importer can stop after the first path or continue into the second one.
131+
132+
### Startup contract
133+
134+
```ascii
135+
UI (tab-view)
136+
-> choose a YouTube tab
137+
-> move it to /feed/channels if needed
138+
-> ping the subscriptions-import receiver
139+
-> inject isolated bridge if missing
140+
-> wait for MAIN-world injector ready signal
141+
-> start import
142+
```
143+
144+
### Persistence boundary
145+
146+
The page never writes storage directly. The trust boundary is:
147+
148+
- page / bridge world: collect and normalize subscription rows
149+
- `state_manager.js`: validate active profile + session lock state
150+
- `background.js`: merge, dedupe, persist, refresh tabs
151+
152+
This keeps whitelist imports aligned with the rest of profile-aware storage rules.
153+
89154
## UI/UX Architecture (v3.2.2)
90155

91156
### Extension Shell Layer (v3.2.8 follow-up)
@@ -239,6 +304,7 @@ function stashNetworkSnapshot(data, dataName) {
239304
- **Purpose**: Extract channel identity from multiple data sources
240305
- **Key Functions**: `searchYtInitialDataForVideoChannel()`, `extractCollaboratorsFromDataObject()`
241306
- **Data Sources**: Stashed snapshots, page globals, DOM data
307+
- **Subscriptions Import Role**: Handles `FilterTube_RequestSubscriptionImport`, collects `/feed/channels` page seed, then follows `FEchannels` browse pages and posts progress/results back to the isolated bridge.
242308

243309
### Isolated World Components
244310

@@ -247,6 +313,11 @@ function stashNetworkSnapshot(data, dataName) {
247313
- **Key Functions**: `prefetchIdentityForCard()`, `injectFilterTubeMenuItem()`, `handleBlockChannelClick()`
248314
- **Data Flow**: Main World → UI Updates → Background Messages
249315

316+
#### `content/bridge_settings.js` - Import Bridge Gateway
317+
- **Purpose**: Keeps the subscriptions-import bridge alive in isolated world, forwards requests to MAIN world, relays progress to the UI/runtime, and waits for injector-ready signals.
318+
- **Key Functions**: `waitForMainWorldImportBridgeReady()`, `FilterTubeRequestSubscribedChannelsFromMainWorld()`
319+
- **Data Flow**: Tab View/StateManager → runtime message → postMessage to MAIN world → progress/response relay
320+
250321
#### `dom_fallback.js` - DOM Processing
251322
- **Purpose**: Process DOM elements when structured data fails
252323
- **Key Functions**: `applyDOMFallback()`, `shouldHideContent()`
@@ -258,6 +329,7 @@ function stashNetworkSnapshot(data, dataName) {
258329
- **Purpose**: Persistent storage, API calls, enrichment scheduling
259330
- **Key Functions**: `handleAddFilteredChannel()`, `schedulePostBlockEnrichment()`, `fetchChannelInfo()`
260331
- **Storage**: Chrome storage API, profile management
332+
- **Subscriptions Import Role**: `FilterTube_BatchImportWhitelistChannels` merges imported rows into `ftProfilesV4.main.whitelistChannels` and backfills legacy whitelist mirrors plus `channelMap`.
261333

262334
## Data Flow Architecture
263335

docs/CHANNEL_BLOCKING_SYSTEM.md

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,61 @@ Result: Multi-channel menus appear instantly on watch/home/search.
245245

246246
---
247247

248+
## 5. Subscribed Channels -> Whitelist Import (v3.2.9 follow-up)
249+
250+
This feature gives whitelist mode a second acquisition path besides manual add/import files.
251+
252+
### Scope
253+
254+
- main YouTube only
255+
- main profile whitelist only
256+
- driven from Tab View channel management
257+
- sourced from the active YouTube account in the selected tab
258+
259+
### Flow
260+
261+
```mermaid
262+
graph TD
263+
A["Tab View: Import Subscribed Channels"] --> B["Move selected YouTube tab to /feed/channels"]
264+
B --> C["Wait for bridge + MAIN-world injector"]
265+
C --> D["Collect subscription rows"]
266+
D --> E["Normalize channel identity"]
267+
E --> F["Background batch merge into whitelistChannels"]
268+
F --> G{"Turn on whitelist?"}
269+
G -->|No| H["Whitelist stored only"]
270+
G -->|Yes| I["Existing blocklist merged into whitelist and cleared"]
271+
```
272+
273+
### Important semantics
274+
275+
- FilterTube currently has **two ways to build whitelist**:
276+
- direct whitelist population, such as subscribed-channels import or other whitelist-specific adds
277+
- blocklist-to-whitelist migration when whitelist mode is activated
278+
- `Import Only` appends subscriptions to `main.whitelistChannels`
279+
- it does **not** change the current blocklist
280+
- `Import + Turn On Whitelist` calls the existing mode-switch path
281+
- that current path merges the profile's blocklist channels and keywords into whitelist and clears the blocklist
282+
283+
### Identity quality
284+
285+
Imported subscription rows are normalized like other channel entries:
286+
287+
- prefer stable `UC...` IDs
288+
- keep `@handle` when present
289+
- keep `customUrl` when present
290+
- keep best available name/logo
291+
292+
### Persistence behavior
293+
294+
The background batch import:
295+
296+
- dedupes against existing whitelist entries
297+
- updates weak existing rows with stronger imported metadata
298+
- mirrors the result into legacy whitelist storage where needed
299+
- updates `channelMap` when new handle/custom URL mappings are learned
300+
301+
This keeps subscriptions import compatible with the rest of the blocking/allowing system instead of creating a second storage model.
302+
248303
## 6. Blocking Flow (3-dot Menu → Resolve → Persist → Hide)
249304

250305
### 6.1 Menu injection and click detection
@@ -920,4 +975,3 @@ The menu injection background enrichment should not do network fetches.
920975

921976
Now:
922977
- Menu “background fetch” uses `fetchIdForHandle(handle, { skipNetwork: true })`.
923-

docs/CODEMAP.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ FilterTube/
9191
**New Features:**
9292
- **Debounced Refresh**: `scheduleSettingsRefreshFromStorage()` with 250ms minimum intervals
9393
- **Storage Filtering**: Ignores channelMap-only changes to prevent excessive reprocessing
94+
- **Subscriptions Import Bridge**: Keeps a dedicated MAIN-world import bridge alive, forwards `FilterTube_ImportSubscribedChannels`, and relays progress/errors safely back to the UI.
9495

9596
### `js/filter_logic.js` - **OPTIMIZED v3.2.1+**
9697
**Context:** Main World (YouTube pages)
@@ -104,6 +105,7 @@ FilterTube/
104105
**Improvements:**
105106
- **Non-configurable Property Safety**: Checks for `configurable: false` before attempting to define properties
106107
- **Error Handling**: Try-catch blocks around `ytInitialData` hook installation
108+
- **Subscriptions Import Coordinator**: Handles `FilterTube_RequestSubscriptionImport`, builds `FEchannels` request profiles from `ytcfg`, normalizes channel rows, emits progress, and returns the final channel list.
107109

108110
### `js/seed.js` - **SAFETY ENHANCED v3.2.1+**
109111
**Context:** Main World (YouTube pages)
@@ -119,6 +121,7 @@ FilterTube/
119121
**Enhancements:**
120122
- **Source-based Enrichment Logic**: Import channels skip unnecessary enrichment
121123
- **Storage Change Filtering**: Ignores channelMap-only changes to reduce listeners
124+
- **Subscribed Channels Import**: `importSubscribedChannelsToWhitelist()` validates tab/profile/lock state, requests the channel roster from the selected YouTube tab, then persists it through background batch import.
122125

123126
### `js/io_manager.js`
124127
**Context:** UI contexts (Tab View) + future sync modules
@@ -187,6 +190,20 @@ FilterTube/
187190
- Reads both hash (`#whatsnew`) and query parameters (`?view=whatsnew`) so banner deep-links auto-select the correct view.
188191
- Import/Export card calls into `io_manager.js` for all serialization logic.
189192

193+
#### Tab view additions (v3.2.9 follow-up)
194+
- `resolveSubscriptionsImportTab()` reuses a main YouTube tab, moves it to `/feed/channels`, and waits for the page + bridge to be ready.
195+
- `waitForYoutubeTabReady()` distinguishes page-loading, bridge-startup, sign-in, and receiver-unavailable states.
196+
- `renderSubscriptionsImportState()` and `syncSubscriptionsImportControls()` drive the inline import status shell and loading animation.
197+
- `confirmSubscriptionsImportModeChoice()` explains the difference between plain whitelist import and whitelist activation.
198+
199+
### `js/background.js` - subscriptions import additions
200+
| Function / Action | Description |
201+
| :--- | :--- |
202+
| `mergeImportedWhitelistChannels()` | Normalizes/dedupes imported whitelist rows and returns merge counts. |
203+
| `FilterTube_BatchImportWhitelistChannels` | Persists imported rows into `ftProfilesV4` plus legacy whitelist mirrors and `channelMap`. |
204+
| `FilterTube_EnsureSubscriptionsImportBridge` | Injects the isolated import bridge into a YouTube tab when the receiver is missing. |
205+
| `FilterTube_SetListMode` | Existing mode-switch handler that the import flow can call after persistence. |
206+
190207
### `js/render_engine.js`
191208
**Context:** UI Contexts
192209
**Purpose:** Handles DOM updates and rendering of lists (keywords, channels).

0 commit comments

Comments
 (0)