Add GitHub version checker to Stream-Mapparr plugin
Implements automatic version checking that displays update status on the plugin settings page. Features: - Fetches latest release version from GitHub using stdlib (urllib.request) - Displays version status at top of settings page (Update Available / Up to Date) - Caches version check results for 24 hours to minimize API calls - Re-checks automatically when plugin version changes - Fails gracefully if network unavailable or GitHub API errors occur Technical Details: - Uses only Python standard library (no requests dependency) - Version check triggered via fields property (runs when settings opened) - Cache stored in /data/stream_mapparr_version_check.json - Timeout set to 5 seconds for API requests - All errors logged at debug level to avoid cluttering logs
This commit is contained in:
@@ -9,7 +9,9 @@ import csv
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
from datetime import datetime
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from datetime import datetime, timedelta
|
||||
from django.utils import timezone
|
||||
|
||||
# Django model imports
|
||||
@@ -37,8 +39,20 @@ class Plugin:
|
||||
@property
|
||||
def fields(self):
|
||||
"""Dynamically generate settings fields including channel database selection."""
|
||||
# Check for version updates (with caching)
|
||||
version_info = {'message': f"Current version: {self.version}", 'status': 'unknown'}
|
||||
try:
|
||||
version_info = self._check_version_update()
|
||||
except Exception as e:
|
||||
LOGGER.debug(f"[Stream-Mapparr] Error checking version update: {e}")
|
||||
|
||||
# Static fields that are always present
|
||||
static_fields = [
|
||||
{
|
||||
"id": "version_status",
|
||||
"type": "info",
|
||||
"label": version_info['message'],
|
||||
},
|
||||
{
|
||||
"id": "overwrite_streams",
|
||||
"label": "🔄 Overwrite Existing Streams",
|
||||
@@ -240,6 +254,7 @@ class Plugin:
|
||||
|
||||
def __init__(self):
|
||||
self.processed_data_file = "/data/stream_mapparr_processed.json"
|
||||
self.version_check_cache_file = "/data/stream_mapparr_version_check.json"
|
||||
self.loaded_channels = []
|
||||
self.loaded_streams = []
|
||||
self.channel_stream_matches = []
|
||||
@@ -247,6 +262,158 @@ class Plugin:
|
||||
|
||||
LOGGER.info(f"[Stream-Mapparr] {self.name} Plugin v{self.version} initialized")
|
||||
|
||||
def _get_latest_version(self, owner, repo):
|
||||
"""
|
||||
Fetches the latest release tag name from GitHub using only Python's standard library.
|
||||
|
||||
Args:
|
||||
owner (str): GitHub repository owner
|
||||
repo (str): GitHub repository name
|
||||
|
||||
Returns:
|
||||
str: Latest version tag or error message
|
||||
"""
|
||||
url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
|
||||
|
||||
# Add a user-agent to avoid potential 403 Forbidden errors
|
||||
headers = {
|
||||
'User-Agent': 'Dispatcharr-Plugin-Version-Checker'
|
||||
}
|
||||
|
||||
try:
|
||||
# Create a request object with headers
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
|
||||
# Make the request and open the URL with a timeout
|
||||
with urllib.request.urlopen(req, timeout=5) as response:
|
||||
# Read the response and decode it as UTF-8
|
||||
data = response.read().decode('utf-8')
|
||||
|
||||
# Parse the JSON string
|
||||
json_data = json.loads(data)
|
||||
|
||||
# Get the tag name
|
||||
latest_version = json_data.get("tag_name")
|
||||
|
||||
if latest_version:
|
||||
return latest_version
|
||||
else:
|
||||
return None
|
||||
|
||||
except urllib.error.HTTPError as http_err:
|
||||
if http_err.code == 404:
|
||||
LOGGER.debug(f"[Stream-Mapparr] GitHub repo not found or has no releases: {http_err}")
|
||||
return None
|
||||
else:
|
||||
LOGGER.debug(f"[Stream-Mapparr] HTTP error checking version: {http_err.code}")
|
||||
return None
|
||||
except Exception as e:
|
||||
# Catch other errors like timeouts
|
||||
LOGGER.debug(f"[Stream-Mapparr] Error checking version: {str(e)}")
|
||||
return None
|
||||
|
||||
def _check_version_update(self):
|
||||
"""
|
||||
Check if a new version is available on GitHub.
|
||||
Uses caching to limit checks to once per day or when the plugin version changes.
|
||||
|
||||
Returns:
|
||||
dict: Contains 'message' and 'status' keys for display
|
||||
"""
|
||||
current_version = self.version
|
||||
github_owner = "PiratesIRC"
|
||||
github_repo = "Stream-Mapparr"
|
||||
|
||||
# Default response
|
||||
result = {
|
||||
'message': f"Current version: {current_version}",
|
||||
'status': 'unknown'
|
||||
}
|
||||
|
||||
try:
|
||||
# Load cache if it exists
|
||||
cache_data = {}
|
||||
should_check = True
|
||||
|
||||
if os.path.exists(self.version_check_cache_file):
|
||||
try:
|
||||
with open(self.version_check_cache_file, 'r', encoding='utf-8') as f:
|
||||
cache_data = json.load(f)
|
||||
|
||||
# Check if we need to recheck:
|
||||
# 1. Plugin version changed (upgrade/downgrade)
|
||||
# 2. More than 24 hours since last check
|
||||
cached_plugin_version = cache_data.get('plugin_version')
|
||||
last_check_str = cache_data.get('last_check')
|
||||
|
||||
if cached_plugin_version == current_version and last_check_str:
|
||||
# Parse the last check time
|
||||
last_check = datetime.fromisoformat(last_check_str)
|
||||
time_diff = datetime.now() - last_check
|
||||
|
||||
# If less than 24 hours, use cached data
|
||||
if time_diff < timedelta(hours=24):
|
||||
should_check = False
|
||||
latest_version = cache_data.get('latest_version')
|
||||
|
||||
# Compare versions
|
||||
if latest_version and latest_version != current_version:
|
||||
result = {
|
||||
'message': f"🎉 Update available! Current: {current_version} → Latest: {latest_version}",
|
||||
'status': 'update_available'
|
||||
}
|
||||
else:
|
||||
result = {
|
||||
'message': f"✅ You are up to date (v{current_version})",
|
||||
'status': 'up_to_date'
|
||||
}
|
||||
except Exception as e:
|
||||
LOGGER.debug(f"[Stream-Mapparr] Error reading version cache: {e}")
|
||||
should_check = True
|
||||
|
||||
# Perform the check if needed
|
||||
if should_check:
|
||||
latest_version = self._get_latest_version(github_owner, github_repo)
|
||||
|
||||
# Update cache
|
||||
cache_data = {
|
||||
'plugin_version': current_version,
|
||||
'latest_version': latest_version,
|
||||
'last_check': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
try:
|
||||
with open(self.version_check_cache_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(cache_data, f, indent=2)
|
||||
except Exception as e:
|
||||
LOGGER.debug(f"[Stream-Mapparr] Error writing version cache: {e}")
|
||||
|
||||
# Compare versions
|
||||
if latest_version and latest_version != current_version:
|
||||
result = {
|
||||
'message': f"🎉 Update available! Current: {current_version} → Latest: {latest_version}",
|
||||
'status': 'update_available'
|
||||
}
|
||||
elif latest_version:
|
||||
result = {
|
||||
'message': f"✅ You are up to date (v{current_version})",
|
||||
'status': 'up_to_date'
|
||||
}
|
||||
else:
|
||||
result = {
|
||||
'message': f"Current version: {current_version} (unable to check for updates)",
|
||||
'status': 'error'
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
LOGGER.debug(f"[Stream-Mapparr] Error in version check: {e}")
|
||||
result = {
|
||||
'message': f"Current version: {current_version} (update check failed)",
|
||||
'status': 'error'
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def _get_channel_databases(self):
|
||||
"""
|
||||
Scan for channel database files and return metadata for each.
|
||||
|
||||
Reference in New Issue
Block a user