preparing for 1.2.65

- fixed a bug in v8 snapshot parsing
- refactoring the method for blocking podcasts and sections on the main page
- fixed patch discriptions
- fixed css rules
This commit is contained in:
amd64fox
2025-10-16 10:38:05 +03:00
parent ab9ab17055
commit a500bf2100
3 changed files with 343 additions and 108 deletions

View File

@@ -1,82 +1,294 @@
function sectionBlock(e, type) {
const API_PATHFINDER = 'api-partner.spotify.com/pathfinder';
const API_RECOMMENDATIONS = 'api.spotify.com/v1/views/personalized-recommendations';
const body = e?.data?.home;
const BLOCKED_SECTIONS_BY_CATEGORY = {
'Party': [
'0JQ5DAnM3wGh0gz1MXnul1'
],
'Chill': [
'0JQ5DAnM3wGh0gz1MXnukV'
],
'Best of the Year': [
'0JQ5IMCbQBLupUQrQFeCzx'
],
'Best of Artists / Tracks': [
'0JQ5DAnM3wGh0gz1MXnu3C'
],
'Best of songwriters': [
'0JQ5DAnM3wGh0gz1MXnu4w'
],
'Biggest Indie Playlists': [
'0JQ5IMCbQBLhSb02SGYpDM'
],
'Charts': [
'0JQ5DAnM3wGh0gz1MXnu5g'
],
'Dinner': [
'0JQ5DAnM3wGh0gz1MXnu3p'
],
'Featured Charts': [
'0JQ5DAob0KOew1FBAMSmBz'
],
'Focus': [
'0JQ5DAob0JCuWaGLU6ntFY',
'0JQ5DAnM3wGh0gz1MXnulP'
],
'Fresh new music': [
'0JQ5DAnM3wGh0gz1MXnu3s'
],
'Gaming music': [
'0JQ5DAob0LaV9FOMJ9utY5'
],
'Happy': [
'0JQ5DAnM3wGh0gz1MXnu3q'
],
'ICE PHONK': [
'0JQ5IMCbQBLiqrNCH9VvmA'
],
'Mood': [
'0JQ5DAnM3wGh0gz1MXnucG',
'0JQ5DAob0JCuWaGLU6ntFT'
],
'Most Listened 2023': [
'0JQ5IMCbQBLicmNERjnGn5'
],
'Music to game to': [
'0JQ5DAob0Jr9ClCbkV4pZD'
],
'Popular Albums / Artists': [
'0JQ5DAnM3wGh0gz1MXnu3B'
],
'Popular new releases': [
'0JQ5DAnM3wGh0gz1MXnu3D'
],
'Popular radio': [
'0JQ5DAnM3wGh0gz1MXnu4h'
],
'Sad': [
'0JQ5DAnM3wGh0gz1MXnu3u',
'0JQ5DAnM3wGh0gz1MXnul2'
],
'Throwback': [
'0JQ5DAnM3wGh0gz1MXnu3w',
'0JQ5DAnM3wGh0gz1MXnul4'
],
'Throwback Thursday / Spotify Playlists / Good night ': [
'0JQ5DAuChZYPe9iDhh2mJz'
],
'Today`s biggest hits': [
'0JQ5DAnM3wGh0gz1MXnu3M'
],
'Trending now': [
'0JQ5DAnM3wGh0gz1MXnu3E'
],
'Workout': [
'0JQ5DAnM3wGh0gz1MXnu3x',
'0JQ5DAnM3wGh0gz1MXnul6'
],
'Now defrosting': [
'0JQ5IMCbQBLlC31GvtaB6w'
],
'Unknown': [
'0JQ5IMCbQBLqTJyy28YCa9',
'0JQ5DAnM3wGh0gz1MXnu7R'
]
};
const BLOCKED_SECTIONS = {};
for (const [category, ids] of Object.entries(BLOCKED_SECTIONS_BY_CATEGORY)) {
for (const id of ids) {
BLOCKED_SECTIONS[id] = category;
}
}
const BLOCKED_CONTENT_TYPES = new Set(['Podcast', 'Audiobook', 'Episode']);
const createSectionAdapter = (isPersonalizedRecommendations) => {
if (isPersonalizedRecommendations) {
return {
getId: (item) => {
const href = item?.href;
if (!href) return null;
const parts = href.split('/');
let id = parts[parts.length - 1];
if (id.startsWith('section')) {
id = id.substring(7);
}
return id;
},
getTitle: (item) => item?.content?.name || 'Unknown',
getRef: (item) => item?.href,
getSectionId: (item) => item?.id,
getContentItems: (item) => item?.content?.items,
getContentData: (contentItem) => contentItem?.content,
getContentType: (contentItem) => contentItem?.type,
getContentTypeName: (contentItem) => contentItem?.content_type
};
} else {
return {
getId: (item) => {
const uri = item?.uri;
if (!uri) return null;
const parts = uri.split(':');
return parts[parts.length - 1];
},
getTitle: (item) => item?.data?.title?.text || 'Unknown',
getRef: (item) => item?.uri,
getSectionId: (item) => null,
getContentItems: (item) => item?.sectionItems?.items,
getContentData: (contentItem) => contentItem?.content?.data,
getContentType: (contentItem) => null,
getContentTypeName: (contentItem) => null
};
}
};
const processShortcutsSection = (contentItems, adapter, removed) => {
if (!contentItems?.length) return false;
for (let j = contentItems.length - 1; j >= 0; j--) {
const contentItem = contentItems[j];
const contentType = adapter.getContentTypeName(contentItem);
if (contentType !== 'PODCAST_EPISODE' && contentType !== 'AUDIOBOOK') {
continue;
}
removed.push({
type: contentType,
name: contentItem?.name || 'Unknown',
uri: contentItem?.uri || 'N/A'
});
contentItems.splice(j, 1);
}
return true;
};
const isPodcastSection = (contentItems, adapter) => {
if (!contentItems?.length) return false;
return adapter.getContentType(contentItems[0]) === 'show';
};
const removeBlockedContent = (contentItems, adapter, removed) => {
if (!contentItems?.length) return;
for (let j = contentItems.length - 1; j >= 0; j--) {
const contentData = adapter.getContentData(contentItems[j]);
if (!contentData || !BLOCKED_CONTENT_TYPES.has(contentData.__typename)) {
continue;
}
removed.push({
type: contentData.__typename,
name: contentData.name || 'Unknown',
uri: contentData.uri || 'N/A'
});
contentItems.splice(j, 1);
}
};
function sectionBlock(data, type) {
const body = data?.data?.home;
const sections = body?.sectionContainer?.sections?.items;
const items = data?.content?.items || data?.data?.content?.items;
const isPersonalizedRecommendations = !!items && !body;
const targetArray = isPersonalizedRecommendations ? items : sections;
function removeSections() {
const sectionsData = [
{ id: '0JQ5IMCbQBLupUQrQFeCzx', name: 'Best of the Year' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3C', name: 'Best of Artists / Tracks' },
{ id: '0JQ5DAnM3wGh0gz1MXnu4w', name: 'Best of songwriters' },
{ id: '0JQ5IMCbQBLhSb02SGYpDM', name: 'Biggest Indie Playlists' },
{ id: '0JQ5DAnM3wGh0gz1MXnu5g', name: 'Charts' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3p', name: 'Dinner' },
{ id: '0JQ5DAob0KOew1FBAMSmBz', name: 'Featured Charts' },
{ id: '0JQ5DAob0JCuWaGLU6ntFY', name: 'Focus' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3s', name: 'Fresh new music' },
{ id: '0JQ5DAob0LaV9FOMJ9utY5', name: 'Gaming music' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3q', name: 'Happy' },
{ id: '0JQ5IMCbQBLiqrNCH9VvmA', name: 'ICE PHONK' },
{ id: '0JQ5DAnM3wGh0gz1MXnucG', name: 'Mood' },
{ id: '0JQ5DAob0JCuWaGLU6ntFT', name: 'Mood' },
{ id: '0JQ5IMCbQBLicmNERjnGn5', name: 'Most Listened 2023' },
{ id: '0JQ5DAob0Jr9ClCbkV4pZD', name: 'Music to game to' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3B', name: 'Popular Albums / Artists' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3D', name: 'Popular new releases' },
{ id: '0JQ5DAnM3wGh0gz1MXnu4h', name: 'Popular radio' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3u', name: 'Sad' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3w', name: 'Throwback' },
{ id: '0JQ5DAuChZYPe9iDhh2mJz', name: 'Throwback Thursday / Spotify Playlists' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3M', name: 'Today`s biggest hits' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3E', name: 'Trending now' },
{ id: '0JQ5DAnM3wGh0gz1MXnu3x', name: 'Workout' },
{ id: '0JQ5IMCbQBLqTJyy28YCa9', name: '?' },
{ id: '0JQ5IMCbQBLlC31GvtaB6w', name: '?' },
{ id: '0JQ5DAnM3wGh0gz1MXnu7R', name: '?' }
];
const sectionIdsRegex = new RegExp(sectionsData.map(section => section.id).join('|'));
if (!targetArray?.length) return;
for (let i = sections.length - 1; i >= 0; i--) {
const uri = sections[i]?.uri;
if (uri && uri.match(sectionIdsRegex)) {
sections.splice(i, 1);
const adapter = createSectionAdapter(isPersonalizedRecommendations);
const removed = [];
for (let i = targetArray.length - 1; i >= 0; i--) {
const item = targetArray[i];
const sectionId = adapter.getId(item);
if (!sectionId) continue;
if (sectionId in BLOCKED_SECTIONS) {
removed.push({
id: sectionId,
knownAs: BLOCKED_SECTIONS[sectionId],
actualTitle: adapter.getTitle(item),
ref: adapter.getRef(item)
});
targetArray.splice(i, 1);
}
}
if (removed.length > 0) {
console.log(`[SectionBlock] Removed ${removed.length} blocked section(s):`, removed);
}
}
function removePodcasts() {
if (Array.isArray(sections)) {
for (let i = 0; i < sections.length; i++) {
const sectionItems = sections[i]?.sectionItems?.items;
if (!targetArray?.length) return;
if (Array.isArray(sectionItems)) {
for (let j = 0; j < sectionItems.length; j++) {
const contentData = sectionItems[j]?.content?.data;
const adapter = createSectionAdapter(isPersonalizedRecommendations);
const removed = [];
if (contentData && ["Podcast", "Audiobook", "Episode"].includes(contentData.__typename)) {
sectionItems.splice(j, 1);
j--;
}
}
for (let i = targetArray.length - 1; i >= 0; i--) {
const item = targetArray[i];
const contentItems = adapter.getContentItems(item);
if (isPersonalizedRecommendations) {
const sectionId = adapter.getSectionId(item);
if (sectionId === 'shortcuts') {
processShortcutsSection(contentItems, adapter, removed);
continue;
}
if (isPodcastSection(contentItems, adapter)) {
removed.push({
type: 'PodcastSection',
sectionId: sectionId,
sectionName: adapter.getTitle(item),
itemsCount: contentItems.length
});
targetArray.splice(i, 1);
continue;
}
}
removeBlockedContent(contentItems, adapter, removed);
}
if (removed.length > 0) {
console.log(`[SectionBlock] Removed ${removed.length} podcast/audiobook item(s):`, removed);
}
}
function removeCanvasSections() {
if (Array.isArray(sections)) {
for (let i = sections.length - 1; i >= 0; i--) {
if (!sections?.length) return;
const sectionDataTypename = sections[i]?.data?.__typename;
const removed = [];
if (sectionDataTypename === 'HomeFeedBaselineSectionData') {
sections.splice(i, 1);
}
for (let i = sections.length - 1; i >= 0; i--) {
if (sections[i]?.data?.__typename === 'HomeFeedBaselineSectionData') {
removed.push({
uri: sections[i]?.uri || 'N/A',
title: sections[i]?.data?.title?.text || 'Canvas Section'
});
sections.splice(i, 1);
}
}
if (removed.length > 0) {
console.log(`[SectionBlock] Removed ${removed.length} canvas section(s):`, removed);
}
}
if (body?.greeting && sections) {
if ((body?.greeting && sections) || items) {
const actions = {
section: removeSections,
podcast: removePodcasts,
@@ -84,13 +296,57 @@ function sectionBlock(e, type) {
all: () => {
removeSections();
removePodcasts();
removeCanvasSections();
if (!isPersonalizedRecommendations) {
removeCanvasSections();
}
}
};
if (Array.isArray(type)) {
type.forEach(t => actions[t]?.());
} else {
actions[type]?.();
}
}
}
}
const originalFetch = window.fetch;
window.fetch = async function (...args) {
const [url] = args;
const urlString = typeof url === 'string' ? url : url?.url || '';
const isPathfinderUrl = urlString.includes(API_PATHFINDER);
const isPersonalizedRecommendationsUrl = urlString.includes(API_RECOMMENDATIONS);
if (!isPathfinderUrl && !isPersonalizedRecommendationsUrl) {
return originalFetch.apply(this, args);
}
const response = await originalFetch.apply(this, args);
const clonedResponse = response.clone();
try {
const data = await response.json();
const shouldModify = (isPathfinderUrl && data?.data?.home) ||
(isPersonalizedRecommendationsUrl && data?.content);
if (!shouldModify) {
return clonedResponse;
}
sectionBlock(data, '');
return new Response(JSON.stringify(data), {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
} catch (error) {
console.error('Fetch intercept error:', error);
return clonedResponse;
}
};