mirror of
https://github.com/plexguide/Huntarr-Sonarr.git
synced 2025-12-16 20:04:16 -06:00
feat: initialize timezone from environment variable and enhance documentation for timezone settings
This commit is contained in:
@@ -57,9 +57,11 @@
|
||||
<ul>
|
||||
<li><a href="#system-settings">System Settings</a>
|
||||
<ul>
|
||||
<li><a href="#timezone">Timezone</a></li>
|
||||
<li><a href="#check-for-updates">Check for Updates</a></li>
|
||||
<li><a href="#debug-mode">Debug Mode</a></li>
|
||||
<li><a href="#display-resources">Display Resources</a></li>
|
||||
<li><a href="#low-usage-mode">Low Usage Mode</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#notifications">Notifications</a>
|
||||
@@ -92,6 +94,7 @@
|
||||
<li><a href="#cmd-wait-attempts">CMD Wait Attempts</a></li>
|
||||
<li><a href="#max-dl-queue-size">Max DL Queue Size</a></li>
|
||||
<li><a href="#log-refresh-interval">Log Refresh Interval</a></li>
|
||||
<li><a href="#base-url">Base URL</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -101,6 +104,33 @@
|
||||
<h2>System Settings</h2>
|
||||
<p>These settings control basic functionality and appearance of your Huntarr.io instance.</p>
|
||||
|
||||
<h3 id="timezone"><a href="#timezone" class="info-icon"><i class="fas fa-info-circle"></i></a> Timezone</h3>
|
||||
<p>Set your timezone for accurate time display in logs, scheduling, and all time-related features throughout Huntarr.</p>
|
||||
|
||||
<p>This setting controls how timestamps are displayed in the following areas:</p>
|
||||
<ul>
|
||||
<li><strong>Log timestamps:</strong> All log entries will show the correct local time</li>
|
||||
<li><strong>Scheduling displays:</strong> Schedule times will be shown in your local timezone</li>
|
||||
<li><strong>Stateful management:</strong> Reset times and state information will use your timezone</li>
|
||||
<li><strong>Cycle tracking:</strong> Cycle start/end times will be in your local time</li>
|
||||
</ul>
|
||||
|
||||
<p>When running in Docker, Huntarr will automatically detect your timezone from the <code>TZ</code> environment variable if set. You can override this setting through the web interface at any time.</p>
|
||||
|
||||
<p><strong>Supported timezones include:</strong></p>
|
||||
<ul>
|
||||
<li>UTC (Coordinated Universal Time)</li>
|
||||
<li>North American timezones (Eastern, Central, Mountain, Pacific, Hawaii)</li>
|
||||
<li>Canadian timezones (Eastern and Pacific Canada)</li>
|
||||
<li>European timezones (UK, Central Europe, Germany, Netherlands, Italy, Spain)</li>
|
||||
<li>Asian timezones (Japan, China, India)</li>
|
||||
<li>Australian and Pacific timezones (Sydney, Perth, Auckland)</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Docker Example:</strong> Set <code>TZ=Pacific/Honolulu</code> in your docker-compose.yml environment variables for Hawaii time.</p>
|
||||
|
||||
<p>Changes to this setting take effect immediately and will update all logging and display times throughout the application.</p>
|
||||
|
||||
<h3 id="check-for-updates"><a href="#check-for-updates" class="info-icon"><i class="fas fa-info-circle"></i></a> Check for Updates</h3>
|
||||
<p>When enabled, Huntarr will automatically check for new versions and notify you when updates are available.</p>
|
||||
|
||||
@@ -121,6 +151,27 @@
|
||||
<p>The Resources section displays helpful links like documentation, GitHub repository, and community forums. You may want to hide this once you're familiar with Huntarr to optimize screen space.</p>
|
||||
|
||||
<p>New users should keep this enabled as it provides quick access to documentation and support resources. More experienced users might prefer to hide this section to focus on the core functionality of Huntarr.</p>
|
||||
|
||||
<h3 id="low-usage-mode"><a href="#low-usage-mode" class="info-icon"><i class="fas fa-info-circle"></i></a> Low Usage Mode</h3>
|
||||
<p>Reduces CPU and GPU usage by disabling animations and visual effects, making Huntarr more suitable for older devices or systems with limited resources.</p>
|
||||
|
||||
<p>When enabled, this setting will:</p>
|
||||
<ul>
|
||||
<li><strong>Disable animations:</strong> Removes smooth transitions and loading animations</li>
|
||||
<li><strong>Reduce visual effects:</strong> Simplifies the user interface to use fewer system resources</li>
|
||||
<li><strong>Optimize rendering:</strong> Uses more efficient rendering techniques for slower devices</li>
|
||||
<li><strong>Lower CPU usage:</strong> Reduces background processing for UI elements</li>
|
||||
</ul>
|
||||
|
||||
<p>This mode is particularly useful for:</p>
|
||||
<ul>
|
||||
<li>Older computers or single-board computers like Raspberry Pi</li>
|
||||
<li>Systems with limited RAM or processing power</li>
|
||||
<li>Remote access scenarios where bandwidth is limited</li>
|
||||
<li>Users who prefer a more responsive, simplified interface</li>
|
||||
</ul>
|
||||
|
||||
<p>The setting takes effect immediately and doesn't require a restart. You can toggle it on and off as needed depending on your current usage requirements.</p>
|
||||
</section>
|
||||
|
||||
<section id="notifications">
|
||||
@@ -353,6 +404,45 @@
|
||||
<li><strong>30 seconds:</strong> Default, good balance for most users</li>
|
||||
<li><strong>60 seconds:</strong> For systems with limited resources or when logs aren't frequently needed</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="base-url"><a href="#base-url" class="info-icon"><i class="fas fa-info-circle"></i></a> Base URL</h3>
|
||||
<p>Base URL path for reverse proxy configurations (e.g., '/huntarr'). Leave empty for root path deployment.</p>
|
||||
|
||||
<p>This setting is essential when running Huntarr behind a reverse proxy server like Nginx, Apache, or Cloudflare Tunnel where you want to host Huntarr at a subpath rather than the root domain.</p>
|
||||
|
||||
<p><strong>Example configurations:</strong></p>
|
||||
<ul>
|
||||
<li><strong>Root path:</strong> Leave empty to access Huntarr at <code>https://yourdomain.com/</code></li>
|
||||
<li><strong>Subpath:</strong> Set to <code>/huntarr</code> to access at <code>https://yourdomain.com/huntarr/</code></li>
|
||||
<li><strong>Multiple services:</strong> Set to <code>/media/huntarr</code> for <code>https://yourdomain.com/media/huntarr/</code></li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Reverse proxy configuration examples:</strong></p>
|
||||
|
||||
<p><strong>Nginx:</strong></p>
|
||||
<pre><code>location /huntarr {
|
||||
proxy_pass http://huntarr:9705;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}</code></pre>
|
||||
|
||||
<p><strong>Cloudflare Tunnel:</strong></p>
|
||||
<pre><code>ingress:
|
||||
- hostname: yourdomain.com
|
||||
path: /huntarr
|
||||
service: http://huntarr:9705</code></pre>
|
||||
|
||||
<p><strong>Important notes:</strong></p>
|
||||
<ul>
|
||||
<li>Always include the leading slash (e.g., <code>/huntarr</code> not <code>huntarr</code>)</li>
|
||||
<li>Do not include trailing slashes</li>
|
||||
<li>Requires container restart to take effect</li>
|
||||
<li>Ensure your reverse proxy is configured to forward requests to this path</li>
|
||||
</ul>
|
||||
|
||||
<p>Credit to <a href="https://github.com/scr4tchy" target="_blank">scr4tchy</a> for implementing this feature.</p>
|
||||
</section>
|
||||
|
||||
<div class="section-nav">
|
||||
|
||||
@@ -328,7 +328,7 @@ const SettingsForms = {
|
||||
container.innerHTML = instancesHtml + searchSettingsHtml;
|
||||
|
||||
// Add event listeners for the instance management
|
||||
SettingsForms.setupInstanceManagement(container, 'radarr', settings.instances.length);
|
||||
this.setupInstanceManagement(container, 'radarr', settings.instances.length);
|
||||
|
||||
// Set up event listeners for the skip_future_releases checkbox
|
||||
const skipFutureCheckbox = container.querySelector('#radarr_skip_future_releases');
|
||||
@@ -1160,7 +1160,7 @@ const SettingsForms = {
|
||||
<p class="setting-help" style="margin-left: -3ch !important;">Show or hide the Resources section on the home page</p>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label for="low_usage_mode"><a href="#" class="info-icon" title="Learn more about Low Usage Mode" target="_blank" rel="noopener"><i class="fas fa-info-circle"></i></a>Low Usage Mode:</label>
|
||||
<label for="low_usage_mode"><a href="https://plexguide.github.io/Huntarr.io/settings/settings.html#low-usage-mode" class="info-icon" title="Learn more about Low Usage Mode" target="_blank" rel="noopener"><i class="fas fa-info-circle"></i></a>Low Usage Mode:</label>
|
||||
<label class="toggle-switch" style="width:40px; height:20px; display:inline-block; position:relative;">
|
||||
<input type="checkbox" id="low_usage_mode" ${settings.low_usage_mode === true ? 'checked' : ''}>
|
||||
<span class="toggle-slider" style="position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0; background-color:#3d4353; border-radius:20px; transition:0.4s;"></span>
|
||||
@@ -1168,13 +1168,14 @@ const SettingsForms = {
|
||||
<p class="setting-help" style="margin-left: -3ch !important;">Disables animations to reduce CPU/GPU usage on older devices</p>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label for="timezone"><a href="#" class="info-icon" title="Set your timezone for accurate time display" target="_blank" rel="noopener"><i class="fas fa-info-circle"></i></a>Timezone:</label>
|
||||
<label for="timezone"><a href="https://plexguide.github.io/Huntarr.io/settings/settings.html#timezone" class="info-icon" title="Set your timezone for accurate time display" target="_blank" rel="noopener"><i class="fas fa-info-circle"></i></a>Timezone:</label>
|
||||
<select id="timezone" name="timezone" style="width: 300px; padding: 8px 12px; border-radius: 6px; cursor: pointer; border: 1px solid rgba(255, 255, 255, 0.1); background-color: #1f2937; color: #d1d5db;">
|
||||
<option value="UTC" ${settings.timezone === 'UTC' || !settings.timezone ? 'selected' : ''}>UTC (Coordinated Universal Time)</option>
|
||||
<option value="America/New_York" ${settings.timezone === 'America/New_York' ? 'selected' : ''}>Eastern Time (America/New_York)</option>
|
||||
<option value="America/Chicago" ${settings.timezone === 'America/Chicago' ? 'selected' : ''}>Central Time (America/Chicago)</option>
|
||||
<option value="America/Denver" ${settings.timezone === 'America/Denver' ? 'selected' : ''}>Mountain Time (America/Denver)</option>
|
||||
<option value="America/Los_Angeles" ${settings.timezone === 'America/Los_Angeles' ? 'selected' : ''}>Pacific Time (America/Los_Angeles)</option>
|
||||
<option value="Pacific/Honolulu" ${settings.timezone === 'Pacific/Honolulu' ? 'selected' : ''}>Hawaii Time (Pacific/Honolulu)</option>
|
||||
<option value="America/Toronto" ${settings.timezone === 'America/Toronto' ? 'selected' : ''}>Eastern Canada (America/Toronto)</option>
|
||||
<option value="America/Vancouver" ${settings.timezone === 'America/Vancouver' ? 'selected' : ''}>Pacific Canada (America/Vancouver)</option>
|
||||
<option value="Europe/London" ${settings.timezone === 'Europe/London' ? 'selected' : ''}>UK Time (Europe/London)</option>
|
||||
@@ -1215,7 +1216,7 @@ const SettingsForms = {
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label for="stateful_management_hours"><a href="https://plexguide.github.io/Huntarr.io/settings/settings.html#stateful-management" class="info-icon" title="Learn more about state reset intervals" target="_blank" rel="noopener"><i class="fas fa-info-circle"></i></a>State Reset (Hours):</label>
|
||||
<label for="stateful_management_hours"><a href="https://plexguide.github.io/Huntarr.io/settings/settings.html#state-reset-hours" class="info-icon" title="Learn more about state reset intervals" target="_blank" rel="noopener"><i class="fas fa-info-circle"></i></a>State Reset (Hours):</label>
|
||||
<input type="number" id="stateful_management_hours" min="1" value="${settings.stateful_management_hours || 168}" style="width: 50% !important; max-width: 200px !important; box-sizing: border-box !important; margin: 0 !important; padding: 8px 12px !important; border-radius: 4px !important; display: block !important; text-align: left !important;">
|
||||
<p class="setting-help" style="margin-left: -3ch !important;">Hours before resetting processed media state (<span id="stateful_management_days">${((settings.stateful_management_hours || 168) / 24).toFixed(1)} days</span>)</p>
|
||||
<p class="setting-help reset-help" style="margin-left: -3ch !important;">Reset clears all processed media IDs to allow reprocessing</p>
|
||||
|
||||
8
main.py
8
main.py
@@ -116,6 +116,14 @@ try:
|
||||
# Initialize main logger
|
||||
huntarr_logger = setup_main_logger()
|
||||
|
||||
# Initialize timezone from TZ environment variable
|
||||
try:
|
||||
from primary.settings_manager import initialize_timezone_from_env
|
||||
initialize_timezone_from_env()
|
||||
huntarr_logger.info("Timezone initialization completed.")
|
||||
except Exception as e:
|
||||
huntarr_logger.warning(f"Failed to initialize timezone from environment: {e}")
|
||||
|
||||
# Initialize clean logging for frontend consumption
|
||||
setup_clean_logging()
|
||||
huntarr_logger.info("Clean logging system initialized for frontend consumption.")
|
||||
|
||||
@@ -47,15 +47,8 @@ hourly_cap_scheduler_thread = None
|
||||
def _get_user_timezone():
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
general_settings = settings_manager.load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
|
||||
# Import timezone handling
|
||||
try:
|
||||
user_tz = pytz.timezone(timezone_name)
|
||||
return user_tz
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.UTC
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception:
|
||||
return pytz.UTC
|
||||
|
||||
|
||||
@@ -205,19 +205,8 @@ def _save_cycle_data(data: Dict[str, Any]) -> None:
|
||||
def _get_user_timezone():
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
from src.primary import settings_manager
|
||||
general_settings = settings_manager.load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
|
||||
# Import timezone handling
|
||||
import pytz
|
||||
try:
|
||||
user_tz = pytz.timezone(timezone_name)
|
||||
print(f"[CycleTracker] Using user timezone: {timezone_name}")
|
||||
return user_tz
|
||||
except pytz.UnknownTimeZoneError:
|
||||
print(f"[CycleTracker] Unknown timezone '{timezone_name}', falling back to UTC")
|
||||
return pytz.UTC
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception as e:
|
||||
print(f"[CycleTracker] Error getting user timezone: {e}, using UTC")
|
||||
import pytz
|
||||
@@ -346,7 +335,7 @@ def update_sleep_json(app_type: str, next_cycle_time: datetime.datetime, cyclelo
|
||||
# Determine cyclelock value
|
||||
if cyclelock is None:
|
||||
# If not explicitly set, preserve existing value or default to True (cycle starting)
|
||||
existing_cyclelock = sleep_data.get(app_type, {}).get('cyclelock', True)
|
||||
existing_cyclelock = sleep_data.get(app_type, {})
|
||||
cyclelock = existing_cyclelock
|
||||
|
||||
# Update the app's data - store times in user's timezone format
|
||||
|
||||
@@ -45,16 +45,8 @@ scheduler_thread = None
|
||||
def _get_user_timezone():
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
from src.primary import settings_manager
|
||||
general_settings = settings_manager.load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
|
||||
import pytz
|
||||
try:
|
||||
user_tz = pytz.timezone(timezone_name)
|
||||
return user_tz
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.UTC
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception:
|
||||
import pytz
|
||||
return pytz.UTC
|
||||
|
||||
@@ -200,6 +200,15 @@ def save_settings(app_name: str, settings_data: Dict[str, Any]) -> bool:
|
||||
# Clear cache for this app to ensure fresh reads
|
||||
clear_cache(app_name)
|
||||
|
||||
# If general settings were saved, also clear timezone cache
|
||||
if app_name == 'general':
|
||||
try:
|
||||
from src.primary.utils.timezone_utils import clear_timezone_cache
|
||||
clear_timezone_cache()
|
||||
settings_logger.debug("Timezone cache cleared after general settings save")
|
||||
except Exception as e:
|
||||
settings_logger.warning(f"Failed to clear timezone cache: {e}")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
settings_logger.error(f"Error saving settings for {app_name} to {settings_file}: {e}")
|
||||
@@ -286,6 +295,47 @@ def apply_timezone(timezone: str) -> bool:
|
||||
settings_logger.error(f"Error setting timezone: {str(e)}")
|
||||
return False
|
||||
|
||||
def initialize_timezone_from_env():
|
||||
"""Initialize timezone setting from TZ environment variable if not already set."""
|
||||
try:
|
||||
# Get the TZ environment variable
|
||||
tz_env = os.environ.get('TZ')
|
||||
if not tz_env:
|
||||
settings_logger.info("No TZ environment variable found, using default UTC")
|
||||
return
|
||||
|
||||
# Load current general settings
|
||||
general_settings = load_settings("general")
|
||||
current_timezone = general_settings.get("timezone")
|
||||
|
||||
# If timezone is not set in settings, initialize it from TZ environment variable
|
||||
if not current_timezone or current_timezone == "UTC":
|
||||
settings_logger.info(f"Initializing timezone from TZ environment variable: {tz_env}")
|
||||
|
||||
# Validate the timezone
|
||||
try:
|
||||
import pytz
|
||||
pytz.timezone(tz_env) # This will raise an exception if invalid
|
||||
|
||||
# Update the settings
|
||||
general_settings["timezone"] = tz_env
|
||||
save_settings("general", general_settings)
|
||||
|
||||
# Apply the timezone to the system
|
||||
apply_timezone(tz_env)
|
||||
|
||||
settings_logger.info(f"Successfully initialized timezone to {tz_env}")
|
||||
|
||||
except pytz.UnknownTimeZoneError:
|
||||
settings_logger.warning(f"Invalid timezone in TZ environment variable: {tz_env}, keeping UTC")
|
||||
except Exception as e:
|
||||
settings_logger.error(f"Error validating timezone {tz_env}: {e}")
|
||||
else:
|
||||
settings_logger.info(f"Timezone already set in settings: {current_timezone}")
|
||||
|
||||
except Exception as e:
|
||||
settings_logger.error(f"Error initializing timezone from environment: {e}")
|
||||
|
||||
# Add a list of known advanced settings for clarity and documentation
|
||||
ADVANCED_SETTINGS = [
|
||||
"api_timeout",
|
||||
|
||||
@@ -180,11 +180,8 @@ def clear_processed_ids(app_type: str = None) -> None:
|
||||
def _get_user_timezone():
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
import pytz
|
||||
general_settings = settings_manager.load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
user_tz = pytz.timezone(timezone_name)
|
||||
return user_tz
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not get user timezone, defaulting to UTC: {e}")
|
||||
import pytz
|
||||
|
||||
@@ -357,12 +357,8 @@ def get_state_management_summary(app_type: str, instance_name: str) -> Dict[str,
|
||||
def _get_user_timezone():
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
from src.primary.settings_manager import load_settings
|
||||
import pytz
|
||||
general_settings = load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
user_tz = pytz.timezone(timezone_name)
|
||||
return user_tz
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception as e:
|
||||
stateful_logger.warning(f"Could not get user timezone, defaulting to UTC: {e}")
|
||||
import pytz
|
||||
|
||||
@@ -20,15 +20,8 @@ def get_ip_address():
|
||||
def _get_user_timezone():
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
from src.primary import settings_manager
|
||||
general_settings = settings_manager.load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
|
||||
import pytz
|
||||
try:
|
||||
return pytz.timezone(timezone_name)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.UTC
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception:
|
||||
import pytz
|
||||
return pytz.UTC
|
||||
|
||||
@@ -31,14 +31,8 @@ CLEAN_LOG_FILES = {
|
||||
def _get_user_timezone():
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
from src.primary import settings_manager
|
||||
general_settings = settings_manager.load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
|
||||
try:
|
||||
return pytz.timezone(timezone_name)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.UTC
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception:
|
||||
return pytz.UTC
|
||||
|
||||
|
||||
@@ -45,16 +45,10 @@ class LocalTimeFormatter(logging.Formatter):
|
||||
def _get_user_timezone(self):
|
||||
"""Get the user's selected timezone from general settings"""
|
||||
try:
|
||||
from src.primary import settings_manager
|
||||
general_settings = settings_manager.load_settings("general")
|
||||
timezone_name = general_settings.get("timezone", "UTC")
|
||||
|
||||
import pytz
|
||||
try:
|
||||
return pytz.timezone(timezone_name)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.UTC
|
||||
from src.primary.utils.timezone_utils import get_user_timezone
|
||||
return get_user_timezone()
|
||||
except Exception:
|
||||
# Final fallback if timezone_utils can't be imported
|
||||
import pytz
|
||||
return pytz.UTC
|
||||
|
||||
@@ -216,6 +210,33 @@ def update_logging_levels():
|
||||
|
||||
print(f"[Logger] Updated all logger levels to {logging.getLevelName(level)}")
|
||||
|
||||
def refresh_timezone_formatters():
|
||||
"""
|
||||
Force refresh of all logger formatters to use updated timezone settings.
|
||||
This should be called when the timezone setting changes.
|
||||
"""
|
||||
print("[Logger] Refreshing timezone formatters for all loggers")
|
||||
|
||||
# Create new formatter with updated timezone handling
|
||||
log_format = "%(asctime)s - huntarr - %(levelname)s - %(message)s"
|
||||
new_formatter = LocalTimeFormatter(log_format, datefmt="%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# Update main logger handlers
|
||||
if logger:
|
||||
for handler in logger.handlers:
|
||||
handler.setFormatter(new_formatter)
|
||||
|
||||
# Update all app logger handlers
|
||||
for app_name, app_logger in app_loggers.items():
|
||||
app_type = app_name.split('.')[-1] if '.' in app_name else app_name
|
||||
app_format = f"%(asctime)s - huntarr.{app_type} - %(levelname)s - %(message)s"
|
||||
app_formatter = LocalTimeFormatter(app_format, datefmt="%Y-%m-%d %H:%M:%S")
|
||||
|
||||
for handler in app_logger.handlers:
|
||||
handler.setFormatter(app_formatter)
|
||||
|
||||
print("[Logger] Timezone formatters refreshed for all loggers")
|
||||
|
||||
def debug_log(message: str, data: object = None, app_type: Optional[str] = None) -> None:
|
||||
"""
|
||||
Log debug messages with optional data.
|
||||
|
||||
100
src/primary/utils/timezone_utils.py
Normal file
100
src/primary/utils/timezone_utils.py
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Timezone utilities for Huntarr
|
||||
Centralized timezone handling with proper fallbacks
|
||||
"""
|
||||
|
||||
import os
|
||||
import pytz
|
||||
from typing import Union
|
||||
|
||||
# Cache for timezone to avoid repeated settings lookups
|
||||
_timezone_cache = None
|
||||
_cache_timestamp = 0
|
||||
_cache_ttl = 5 # 5 seconds cache TTL
|
||||
|
||||
|
||||
def clear_timezone_cache():
|
||||
"""Clear the timezone cache to force a fresh lookup."""
|
||||
global _timezone_cache, _cache_timestamp
|
||||
_timezone_cache = None
|
||||
_cache_timestamp = 0
|
||||
|
||||
|
||||
def get_user_timezone() -> pytz.BaseTzInfo:
|
||||
"""
|
||||
Get the user's selected timezone with proper fallback handling.
|
||||
|
||||
Fallback order:
|
||||
1. User's timezone setting from general settings
|
||||
2. TZ environment variable
|
||||
3. UTC as final fallback
|
||||
|
||||
Returns:
|
||||
pytz.BaseTzInfo: The timezone object to use
|
||||
"""
|
||||
global _timezone_cache, _cache_timestamp
|
||||
|
||||
# Check cache first
|
||||
import time
|
||||
current_time = time.time()
|
||||
if _timezone_cache and (current_time - _cache_timestamp) < _cache_ttl:
|
||||
return _timezone_cache
|
||||
|
||||
try:
|
||||
# First try to get timezone from user settings
|
||||
try:
|
||||
from src.primary import settings_manager
|
||||
general_settings = settings_manager.load_settings("general", use_cache=False) # Force fresh read
|
||||
timezone_name = general_settings.get("timezone")
|
||||
|
||||
if timezone_name and timezone_name != "UTC":
|
||||
try:
|
||||
tz = pytz.timezone(timezone_name)
|
||||
# Cache the result
|
||||
_timezone_cache = tz
|
||||
_cache_timestamp = current_time
|
||||
return tz
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pass # Fall through to TZ environment variable
|
||||
except Exception:
|
||||
pass # Fall through to TZ environment variable
|
||||
|
||||
# Second try TZ environment variable
|
||||
tz_env = os.environ.get('TZ')
|
||||
if tz_env:
|
||||
try:
|
||||
tz = pytz.timezone(tz_env)
|
||||
# Cache the result
|
||||
_timezone_cache = tz
|
||||
_cache_timestamp = current_time
|
||||
return tz
|
||||
except pytz.UnknownTimeZoneError:
|
||||
pass # Fall through to UTC
|
||||
|
||||
# Final fallback to UTC
|
||||
tz = pytz.UTC
|
||||
_timezone_cache = tz
|
||||
_cache_timestamp = current_time
|
||||
return tz
|
||||
|
||||
except Exception:
|
||||
# If anything goes wrong, always return UTC
|
||||
tz = pytz.UTC
|
||||
_timezone_cache = tz
|
||||
_cache_timestamp = current_time
|
||||
return tz
|
||||
|
||||
|
||||
def get_timezone_name() -> str:
|
||||
"""
|
||||
Get the timezone name as a string.
|
||||
|
||||
Returns:
|
||||
str: The timezone name (e.g., 'Pacific/Honolulu', 'UTC')
|
||||
"""
|
||||
try:
|
||||
timezone = get_user_timezone()
|
||||
return str(timezone)
|
||||
except Exception:
|
||||
return "UTC"
|
||||
@@ -619,6 +619,13 @@ def save_general_settings():
|
||||
timezone_success = settings_manager.apply_timezone(new_timezone)
|
||||
if timezone_success:
|
||||
general_logger.info(f"Successfully applied timezone {new_timezone}")
|
||||
# Refresh all logger formatters to use the new timezone
|
||||
try:
|
||||
from src.primary.utils.logger import refresh_timezone_formatters
|
||||
refresh_timezone_formatters()
|
||||
general_logger.info("Timezone formatters refreshed for all loggers")
|
||||
except Exception as e:
|
||||
general_logger.warning(f"Failed to refresh timezone formatters: {e}")
|
||||
else:
|
||||
general_logger.warning(f"Failed to apply timezone {new_timezone}, but settings saved")
|
||||
except Exception as e:
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.6.2
|
||||
7.6.3
|
||||
|
||||
Reference in New Issue
Block a user