Let saved searches from different sources be read as one
A saved search is one source, one channel on it, that channel's filters and the words. Two of them on different sources had no way to be read together, and a channel group is not it: every channel in a group belongs to the same server and shares one query, where the interesting pair is "uncensored JAV over here" and "amateur 1080p over there". So a saved search can now hold several of those instead of one. Opening it feeds a single grid from all of them, taken alternately -- one from each in turn, the way a group zips its channels -- rather than one source's page and then the next. Each member keeps its own source, channel, filters and words, because having its own words is most of why it was worth saving separately. Every entry is read as a list of members, a plain search being a merge of one. That is what lets the daily check, mark-all-read and the bookmark go on working without learning what a merge is: they ask each member and zip the answers, so the badge counts what the grid would have shown. The search box is turned off while a merge is open and says why -- there is nothing for one box to say for several members. A tag or an uploader tapped on a card steps out of the merge onto one real channel instead, since otherwise it would reload the same videos and look broken. A merge answers to no single server, so it is not stored as a channel preference and is not looked up from a server's status: its members ride on the session, and a reload rebuilds it from the saved search so a rename is picked up rather than frozen in. One source failing drops that member and leaves the rest readable. Merging a merge flattens it, so "the searches in it" means the same however it was assembled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPZFnNdHbPGDTqQUNiE4ZN
This commit is contained in:
@@ -164,6 +164,7 @@ App.session = App.session || {};
|
||||
// channels as a toggleable multi-select, so the user can browse "All <group>"
|
||||
// while disabling individual channels.
|
||||
App.session.GROUP_CHANNELS_OPTION_ID = '__groupChannels';
|
||||
App.session.MERGED_MEMBERS_OPTION_ID = '__mergedMembers';
|
||||
|
||||
// Options/session helpers that power channel selection and filters.
|
||||
App.session.serializeOptions = function(options) {
|
||||
@@ -233,6 +234,45 @@ App.session = App.session || {};
|
||||
};
|
||||
};
|
||||
|
||||
// Builds a pseudo-channel representing a merged saved search: several
|
||||
// saved searches, each with its own source, channel, filters and words,
|
||||
// browsed as one listing. A channel group does something similar but
|
||||
// cannot express this -- every channel in a group belongs to the same
|
||||
// server and shares one query, where a merge is held together by nothing
|
||||
// except the reader having put it together (see fetchMergedBatch).
|
||||
//
|
||||
// The members are carried on the channel itself rather than looked up from
|
||||
// a server's status, because no single server knows about them. That is
|
||||
// also what lets a merged session survive a reload.
|
||||
App.session.buildMergedChannel = function(entry) {
|
||||
if (!entry || !Array.isArray(entry.members) || entry.members.length === 0) return null;
|
||||
const members = entry.members.map((member, i) => Object.assign({}, member, {
|
||||
// Stable within the merge, so a member toggle keeps meaning the
|
||||
// same member even if two of them name the same channel.
|
||||
memberId: member.memberId || `m${i}`
|
||||
}));
|
||||
const memberOptions = members.map((member) => ({
|
||||
id: member.memberId,
|
||||
title: member.channelName || member.channelId
|
||||
}));
|
||||
return {
|
||||
id: `merged:${entry.id}`,
|
||||
name: entry.name || 'Merged search',
|
||||
isMerged: true,
|
||||
savedSearchId: entry.id,
|
||||
members: members,
|
||||
// Same shape as a group's "Channels" filter, so the existing
|
||||
// filter UI shows it without knowing what a merge is.
|
||||
options: memberOptions.length > 0 ? [{
|
||||
id: App.session.MERGED_MEMBERS_OPTION_ID,
|
||||
title: 'Searches',
|
||||
multiSelect: true,
|
||||
selectAllDefault: true,
|
||||
options: memberOptions
|
||||
}] : []
|
||||
};
|
||||
};
|
||||
|
||||
// Resolves a stored channel id (which may reference a single channel or a
|
||||
// "group:<id>" pseudo-channel) against a server's status payload.
|
||||
App.session.resolveChannelById = function(serverData, channelId) {
|
||||
@@ -350,6 +390,28 @@ App.session = App.session || {};
|
||||
const serverKeys = (config.servers || []).map((serverObj) => Object.keys(serverObj)[0]);
|
||||
const existingSession = App.storage.getSession();
|
||||
|
||||
// A merged search answers to no single server, so the check below --
|
||||
// "does this server still list this channel" -- cannot speak for it.
|
||||
// It is valid while the saved search it came from is still there and
|
||||
// at least one member's source is still set up. Rebuilt from that
|
||||
// saved search rather than trusted as stored, so a rename or an edited
|
||||
// member is picked up instead of being frozen into the session.
|
||||
if (existingSession && existingSession.channel && existingSession.channel.isMerged) {
|
||||
const stored = App.savedSearches && typeof App.savedSearches.get === 'function'
|
||||
? App.savedSearches.get(existingSession.channel.savedSearchId)
|
||||
: null;
|
||||
const channel = stored ? App.session.buildMergedChannel(stored) : existingSession.channel;
|
||||
const members = (channel && channel.members) || [];
|
||||
if (members.some((member) => serverKeys.includes(member.server))) {
|
||||
App.storage.setSession({
|
||||
server: members[0].server,
|
||||
channel: channel,
|
||||
options: App.session.hydrateOptions(channel, App.session.serializeOptions(existingSession.options))
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Leave a still-valid session untouched so we don't disturb the user's
|
||||
// current server/channel selection on refresh.
|
||||
if (existingSession && existingSession.channel && serverKeys.includes(existingSession.server)) {
|
||||
|
||||
Reference in New Issue
Block a user