From 4eab95793e6bf23e6d7f6870bc3e3ffb1205352e Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Fri, 5 Jun 2026 09:08:26 -0500 Subject: [PATCH] refactor: rename useSDLogos to autoApplySDLogos for clarity Update the variable name in the EPG settings to better reflect its purpose. Adjust related logic in the UI to ensure consistent handling of the auto-apply feature for SD logos. --- apps/epg/tasks.py | 4 +- frontend/src/components/forms/EPG.jsx | 352 +++++++++++++------ frontend/src/components/tables/EPGsTable.jsx | 3 +- 3 files changed, 241 insertions(+), 118 deletions(-) diff --git a/apps/epg/tasks.py b/apps/epg/tasks.py index fe1b1e7c..8d2bb96e 100644 --- a/apps/epg/tasks.py +++ b/apps/epg/tasks.py @@ -3137,8 +3137,8 @@ def fetch_schedules_direct(source, stations_only=False, force=False): # ------------------------------------------------------------------------- # Step 9: Apply SD station logos to matched channels if enabled # ------------------------------------------------------------------------- - use_sd_logos = (source.custom_properties or {}).get('use_sd_logos', False) - if use_sd_logos: + auto_apply_sd_logos = (source.custom_properties or {}).get('auto_apply_sd_logos', False) + if auto_apply_sd_logos: try: from apps.channels.models import Channel as ChannelModel, Logo diff --git a/frontend/src/components/forms/EPG.jsx b/frontend/src/components/forms/EPG.jsx index b94fadcd..409307e9 100644 --- a/frontend/src/components/forms/EPG.jsx +++ b/frontend/src/components/forms/EPG.jsx @@ -27,6 +27,7 @@ import ScheduleInput from './ScheduleInput'; import { addEPG, updateEPG } from '../../utils/forms/DummyEpgUtils.js'; import { showNotification } from '../../utils/notificationUtils.js'; import API from '../../api.js'; +import useEPGsStore from '../../store/epgs'; // Countries are fetched dynamically from the SD API on component mount. // Fallback list used if the API call fails. @@ -49,34 +50,52 @@ const SD_COUNTRIES_FALLBACK = [ // ESPN HD logo previews — packaged as static URLs so no API call is needed. // These are publicly accessible S3 URLs that don't require authentication. -const SD_LOGO_PREVIEW_BASE = 'https://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s32645'; +const SD_LOGO_PREVIEW_BASE = + 'https://schedulesdirect-api20141201-logos.s3.dualstack.us-east-1.amazonaws.com/stationLogos/s32645'; const SD_LOGO_STYLES = [ - { value: 'dark', label: 'Dark', url: `${SD_LOGO_PREVIEW_BASE}_dark_360w_270h.png` }, - { value: 'white', label: 'White', url: `${SD_LOGO_PREVIEW_BASE}_white_360w_270h.png` }, - { value: 'gray', label: 'Gray', url: `${SD_LOGO_PREVIEW_BASE}_gray_360w_270h.png` }, - { value: 'light', label: 'Light', url: `${SD_LOGO_PREVIEW_BASE}_light_360w_270h.png` }, + { + value: 'dark', + label: 'Dark', + url: `${SD_LOGO_PREVIEW_BASE}_dark_360w_270h.png`, + }, + { + value: 'white', + label: 'White', + url: `${SD_LOGO_PREVIEW_BASE}_white_360w_270h.png`, + }, + { + value: 'gray', + label: 'Gray', + url: `${SD_LOGO_PREVIEW_BASE}_gray_360w_270h.png`, + }, + { + value: 'light', + label: 'Light', + url: `${SD_LOGO_PREVIEW_BASE}_light_360w_270h.png`, + }, ]; // ─── SD Settings: Logo toggle + style selector + Poster toggle ────────────── const SDSettings = ({ sourceId, customProperties }) => { - const cp = customProperties || {}; - const [useSDLogos, setUseSDLogos] = useState(cp.use_sd_logos || false); - const [logoStyle, setLogoStyle] = useState(cp.logo_style || 'dark'); - const [fetchPosters, setFetchPosters] = useState(cp.fetch_posters || false); + const storeCustomProps = useEPGsStore((s) => + sourceId ? s.epgs[sourceId]?.custom_properties : null + ); + const resolvedCp = storeCustomProps || customProperties || {}; + + const [autoApplySDLogos, setAutoApplySDLogos] = useState( + !!resolvedCp.auto_apply_sd_logos + ); + const [logoStyle, setLogoStyle] = useState(resolvedCp.logo_style || 'dark'); + const [fetchPosters, setFetchPosters] = useState(!!resolvedCp.fetch_posters); const [saving, setSaving] = useState(false); - // Sync from parent when customProperties changes + // Sync from store (preferred) or parent props when the form opens or settings save useEffect(() => { - const newCp = customProperties || {}; - setUseSDLogos(newCp.use_sd_logos || false); + const newCp = storeCustomProps || customProperties || {}; + setAutoApplySDLogos(!!newCp.auto_apply_sd_logos); setLogoStyle(newCp.logo_style || 'dark'); - setFetchPosters(newCp.fetch_posters || false); - - // Persist the default logo_style if not already set - if (sourceId && !newCp.logo_style) { - API.updateSDSettings(sourceId, { logo_style: 'dark' }); - } - }, [customProperties, sourceId]); + setFetchPosters(!!newCp.fetch_posters); + }, [storeCustomProps, customProperties]); const saveSetting = async (key, value) => { setSaving(true); @@ -87,9 +106,9 @@ const SDSettings = ({ sourceId, customProperties }) => { } }; - const handleLogoToggle = (checked) => { - setUseSDLogos(checked); - saveSetting('use_sd_logos', checked); + const handleAutoApplyToggle = (checked) => { + setAutoApplySDLogos(checked); + saveSetting('auto_apply_sd_logos', checked); }; const handleLogoChange = (style) => { @@ -102,25 +121,14 @@ const SDSettings = ({ sourceId, customProperties }) => { saveSetting('fetch_posters', checked); }; - const logosDisabled = false; - return ( - handleLogoToggle(e.currentTarget.checked)} - disabled={saving} - size="sm" - mb="sm" - /> - - + Station Logo Style - Choose which logo variant to use for SD stations. + Choose which logo variant to store in EPG data. Available for Set Logo + from EPG even when auto-apply is disabled. {SD_LOGO_STYLES.map((style) => ( @@ -128,16 +136,17 @@ const SDSettings = ({ sourceId, customProperties }) => { key={style.value} onClick={() => handleLogoChange(style.value)} style={{ - border: !logosDisabled && logoStyle === style.value - ? '2px solid var(--mantine-color-blue-5)' - : '2px solid var(--mantine-color-default-border)', + border: + logoStyle === style.value + ? '2px solid var(--mantine-color-blue-5)' + : '2px solid var(--mantine-color-default-border)', borderRadius: 'var(--mantine-radius-sm)', padding: 3, - opacity: logosDisabled ? 0.3 : (saving ? 0.6 : 1), - cursor: logosDisabled ? 'not-allowed' : (saving ? 'wait' : 'pointer'), + opacity: saving ? 0.6 : 1, + cursor: saving ? 'wait' : 'pointer', flex: 1, textAlign: 'center', - pointerEvents: logosDisabled ? 'none' : 'auto', + pointerEvents: saving ? 'none' : 'auto', }} > { height: 50, objectFit: 'contain', display: 'block', - backgroundColor: style.value === 'white' || style.value === 'light' - ? '#333' - : 'transparent', + backgroundColor: + style.value === 'white' || style.value === 'light' + ? '#333' + : 'transparent', borderRadius: 2, - filter: logosDisabled ? 'grayscale(100%)' : 'none', }} /> - + {style.label} ))} + handleAutoApplyToggle(e.currentTarget.checked)} + disabled={saving} + size="sm" + mb="sm" + /> + { const [addingLineup, setAddingLineup] = useState(null); const [removingLineup, setRemovingLineup] = useState(null); const maxLineups = 4; - const SD_DOCS_URL = 'https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#tasks-your-client-must-perform'; + const SD_DOCS_URL = + 'https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#tasks-your-client-must-perform'; const fetchActiveLineups = useCallback(async () => { setLoadingLineups(true); @@ -219,12 +243,12 @@ const SDLineupManager = ({ sourceId }) => { fetchActiveLineups(); // Fetch country list from SD API per their recommendation to not hardcode fetch('https://json.schedulesdirect.org/20141201/available/countries') - .then(r => r.json()) - .then(data => { + .then((r) => r.json()) + .then((data) => { const all = Object.values(data).flat(); const mapped = all - .filter(c => c.shortName && c.fullName) - .map(c => ({ value: c.shortName, label: c.fullName })) + .filter((c) => c.shortName && c.fullName) + .map((c) => ({ value: c.shortName, label: c.fullName })) .sort((a, b) => a.label.localeCompare(b.label)); if (mapped.length > 0) setCountries(mapped); }) @@ -237,7 +261,11 @@ const SDLineupManager = ({ sourceId }) => { setSearching(true); setSearchResults([]); try { - const data = await API.searchSDLineups(sourceId, country, postalCode.trim()); + const data = await API.searchSDLineups( + sourceId, + country, + postalCode.trim() + ); if (data) { setSearchResults(data.lineups || []); } @@ -253,7 +281,10 @@ const SDLineupManager = ({ sourceId }) => { if (!result) return; // Update changesRemaining from response - if (result.changes_remaining !== undefined && result.changes_remaining !== null) { + if ( + result.changes_remaining !== undefined && + result.changes_remaining !== null + ) { setChangesRemaining(result.changes_remaining); } @@ -264,22 +295,38 @@ const SDLineupManager = ({ sourceId }) => { } if (result.error === 'duplicate_lineup') { - showNotification({ title: 'Already added', message: result.message, color: 'yellow' }); + showNotification({ + title: 'Already added', + message: result.message, + color: 'yellow', + }); return; } if (result.error === 'max_lineups_reached') { - showNotification({ title: 'Maximum lineups reached', message: result.message, color: 'orange' }); + showNotification({ + title: 'Maximum lineups reached', + message: result.message, + color: 'orange', + }); return; } if (result.error) { - showNotification({ title: 'Unable to add lineup', message: result.message, color: 'red' }); + showNotification({ + title: 'Unable to add lineup', + message: result.message, + color: 'red', + }); return; } if (result.code === 0) { - showNotification({ title: 'Lineup added', message: lineup.name, color: 'green' }); + showNotification({ + title: 'Lineup added', + message: lineup.name, + color: 'green', + }); await fetchActiveLineups(); } } finally { @@ -292,8 +339,15 @@ const SDLineupManager = ({ sourceId }) => { try { const result = await API.deleteSDLineup(sourceId, lineup.lineup); if (result && result.code === 0) { - showNotification({ title: 'Lineup removed', message: lineup.name, color: 'blue' }); - if (result.changes_remaining !== undefined && result.changes_remaining !== null) { + showNotification({ + title: 'Lineup removed', + message: lineup.name, + color: 'blue', + }); + if ( + result.changes_remaining !== undefined && + result.changes_remaining !== null + ) { setChangesRemaining(result.changes_remaining); } await fetchActiveLineups(); @@ -344,7 +398,9 @@ const SDLineupManager = ({ sourceId }) => { }} > - {lineup.name} + + {lineup.name} + {lineup.transport} · {lineup.location} · {lineup.lineup} @@ -370,33 +426,67 @@ const SDLineupManager = ({ sourceId }) => { {atMax && ( - }> + } + > Maximum of {maxLineups} lineups reached. Remove one to add another. )} {changesRemaining === 0 && ( - }> - You have reached your daily Schedules Direct lineup addition limit. SD allows 6 adds per 24-hour period.{' '} + } + > + You have reached your daily Schedules Direct lineup addition limit. SD + allows 6 adds per 24-hour period.{' '} {changesResetAt && ( - Limit resets at {new Date(changesResetAt).toUTCString()}. + + Limit resets at{' '} + {new Date(changesResetAt).toUTCString()}.{' '} + )} - {!changesResetAt && Limit resets 24 hours after the first add of the day. } - Learn more + {!changesResetAt && ( + Limit resets 24 hours after the first add of the day. + )} + + Learn more + )} {changesRemaining === 1 && ( - }> - You have 1 lineup addition remaining today. Use it carefully — Schedules Direct limits adds to 6 per 24-hour period.{' '} - Learn more + } + > + You have 1 lineup addition remaining today. Use it + carefully — Schedules Direct limits adds to 6 per 24-hour period.{' '} + + Learn more + )} {changesRemaining === 2 && ( - }> - You have 2 lineup additions remaining today. Schedules Direct limits adds to 6 per 24-hour period.{' '} - Learn more + } + > + You have 2 lineup additions remaining today. + Schedules Direct limits adds to 6 per 24-hour period.{' '} + + Learn more + )} @@ -430,7 +520,13 @@ const SDLineupManager = ({ sourceId }) => { {searchResults.length > 0 && ( - + {searchResults.map((lineup) => { @@ -438,12 +534,20 @@ const SDLineupManager = ({ sourceId }) => { return ( - {lineup.name} - {lineup.transport} · {lineup.location} + + {lineup.name} + + + {lineup.transport} · {lineup.location} + - + {isActive ? ( - Active + + Active + ) : ( diff --git a/frontend/src/components/tables/EPGsTable.jsx b/frontend/src/components/tables/EPGsTable.jsx index 8b464ee2..c1dc9ef6 100644 --- a/frontend/src/components/tables/EPGsTable.jsx +++ b/frontend/src/components/tables/EPGsTable.jsx @@ -399,7 +399,8 @@ const EPGsTable = () => { const [sorting, setSorting] = useState([]); const editEPG = async (epg = null) => { - setEPG(epg); + const freshEpg = epg?.id ? (epgs[epg.id] || epg) : epg; + setEPG(freshEpg); // Open the appropriate modal based on source type if (epg?.source_type === 'dummy') { setDummyEpgModalOpen(true);