diff --git a/core/seasonal_discovery.py b/core/seasonal_discovery.py index 96821fa1..d0bfa029 100644 --- a/core/seasonal_discovery.py +++ b/core/seasonal_discovery.py @@ -176,20 +176,63 @@ class SeasonalDiscoveryService: except Exception as e: logger.error(f"Error creating seasonal database schema: {e}") + def _get_effective_month(self, hemisphere: str = None) -> int: + """Get the effective month, adjusted for hemisphere setting. + + Southern hemisphere offsets by 6 months so seasons align correctly + (e.g., December in southern hemisphere maps to June = summer → winter). + Holiday months (Halloween, Christmas, Valentine's) are NOT offset. + """ + month = datetime.now().month + if hemisphere is None: + hemisphere = self._get_hemisphere() + if hemisphere == 'southern': + # Offset by 6 months for seasonal content + return ((month - 1 + 6) % 12) + 1 + return month + + def _get_hemisphere(self) -> str: + """Get configured hemisphere from database metadata.""" + try: + with self.database._get_connection() as conn: + cursor = conn.cursor() + cursor.execute("SELECT value FROM metadata WHERE key = 'hemisphere'") + row = cursor.fetchone() + if row: + val = row[0] if isinstance(row, tuple) else row['value'] + if val in ('northern', 'southern'): + return val + except Exception: + pass + return 'northern' + def get_current_season(self) -> Optional[str]: """ Detect current season based on current month. + Respects hemisphere setting — southern hemisphere offsets seasonal + months by 6, but holidays stay calendar-fixed. Returns: Season key (e.g., 'halloween', 'christmas') or None """ - current_month = datetime.now().month + real_month = datetime.now().month + hemisphere = self._get_hemisphere() + effective_month = self._get_effective_month(hemisphere) + + # Holidays that are calendar-fixed (not season-dependent) + _HOLIDAY_KEYS = {'halloween', 'christmas', 'valentines'} # Check each season to find active ones active_seasons = [] for season_key, config in SEASONAL_CONFIG.items(): - if current_month in config['active_months']: - active_seasons.append(season_key) + if hemisphere == 'southern' and season_key in _HOLIDAY_KEYS: + # Holidays use real calendar month + if real_month in config['active_months']: + active_seasons.append(season_key) + else: + # Seasons use effective (offset) month + if effective_month in config['active_months']: + active_seasons.append(season_key) if not active_seasons: return None @@ -205,13 +248,21 @@ class SeasonalDiscoveryService: return active_seasons[0] if active_seasons else None def get_all_active_seasons(self) -> List[str]: - """Get all seasons active in current month (for displaying multiple sections)""" - current_month = datetime.now().month + """Get all seasons active in current month (hemisphere-aware)""" + real_month = datetime.now().month + hemisphere = self._get_hemisphere() + effective_month = self._get_effective_month(hemisphere) + + _HOLIDAY_KEYS = {'halloween', 'christmas', 'valentines'} active_seasons = [] for season_key, config in SEASONAL_CONFIG.items(): - if current_month in config['active_months']: - active_seasons.append(season_key) + if hemisphere == 'southern' and season_key in _HOLIDAY_KEYS: + if real_month in config['active_months']: + active_seasons.append(season_key) + else: + if effective_month in config['active_months']: + active_seasons.append(season_key) return active_seasons diff --git a/web_server.py b/web_server.py index ea043a37..22e185cf 100644 --- a/web_server.py +++ b/web_server.py @@ -18124,6 +18124,49 @@ def set_discovery_lookback_period(): print(f"Error setting discovery lookback period: {e}") return jsonify({"error": str(e)}), 500 +@app.route('/api/discovery/hemisphere', methods=['GET']) +def get_hemisphere(): + """Get the hemisphere setting for seasonal content.""" + try: + db = get_database() + with db._get_connection() as conn: + cursor = conn.cursor() + cursor.execute("SELECT value FROM metadata WHERE key = 'hemisphere'") + row = cursor.fetchone() + value = 'northern' + if row: + val = row[0] if isinstance(row, tuple) else row['value'] + if val in ('northern', 'southern'): + value = val + return jsonify({"hemisphere": value}) + except Exception as e: + return jsonify({"error": str(e)}), 500 + + +@app.route('/api/discovery/hemisphere', methods=['POST']) +def set_hemisphere(): + """Set the hemisphere for seasonal content (northern or southern).""" + try: + data = request.get_json() + hemisphere = data.get('hemisphere', '').lower() + if hemisphere not in ('northern', 'southern'): + return jsonify({"error": "Must be 'northern' or 'southern'"}), 400 + + db = get_database() + with db._get_connection() as conn: + cursor = conn.cursor() + cursor.execute(""" + INSERT OR REPLACE INTO metadata (key, value, updated_at) + VALUES ('hemisphere', ?, CURRENT_TIMESTAMP) + """, (hemisphere,)) + conn.commit() + + logger.info("Hemisphere set to: %s", hemisphere) + return jsonify({"success": True, "hemisphere": hemisphere}) + except Exception as e: + return jsonify({"error": str(e)}), 500 + + @app.route('/api/wishlist/tracks', methods=['GET']) def get_wishlist_tracks(): """ diff --git a/webui/index.html b/webui/index.html index 31584740..32fd7526 100644 --- a/webui/index.html +++ b/webui/index.html @@ -4347,6 +4347,19 @@ Once scanned, artists are kept updated with new releases only. + +