Skip to content

Commit

Permalink
Added persistent appdata path
Browse files Browse the repository at this point in the history
  • Loading branch information
7eventy7 committed Nov 12, 2024
1 parent 05c2294 commit 741fea4
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 20 deletions.
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Dockerfile
# Use Python 3.12 slim image
FROM python:3.12-slim

Expand All @@ -13,8 +14,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy source code
COPY src/ .
# Copy application
COPY main.py .

# Run the script
CMD ["python", "main.py"]
5 changes: 3 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# docker-compose.yml
version: '3.8'

services:
trackly:
build: .
volumes:
- ${MUSIC_PATH:-./music}:/music:ro # Read-only mount of music directory
- ./config:/config # Persistent storage for config
environment:
- MUSIC_PATH=/music
- UPDATE_INTERVAL=0 * * * * # Default to every hour
- UPDATE_INTERVAL=00:00 # Default to midnight
- DISCORD_WEBHOOK= # Set this to your Discord webhook URL
- DISCOGS_TOKEN= # Set this to your Discogs API token
restart: unless-stopped
Expand Down
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ python-dotenv==1.0.0
python3-discogs-client>=2.0.0
requests==2.31.0
schedule==1.2.1
watchdog==3.0.0
python-crontab==3.0.0
watchdog==3.0.0
108 changes: 94 additions & 14 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,21 @@
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from dotenv import load_dotenv
import logging

# Set up logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)

CONFIG_PATH = "/config/artists.json"

class MusicFolderHandler(FileSystemEventHandler):
def __init__(self, artists_file):
self.artists_file = artists_file
def __init__(self):
self.artists_file = CONFIG_PATH
self.last_update = time.time()
# Prevent multiple updates within 5 seconds
self.update_cooldown = 5
Expand All @@ -22,34 +33,56 @@ def on_any_event(self, event):

if event.is_directory:
self.last_update = time.time()
logger.info(f"Directory change detected: {event.src_path}")
update_artist_list()

def load_config():
"""Load environment variables"""
update_interval = os.getenv('UPDATE_INTERVAL')
discord_webhook = os.getenv('DISCORD_WEBHOOK')
discogs_token = os.getenv('DISCOGS_TOKEN')

if not all([update_interval, discord_webhook]):
logger.info("Loading configuration...")
if not all([update_interval, discord_webhook, discogs_token]):
missing_vars = [var for var, val in {
'UPDATE_INTERVAL': update_interval,
'DISCORD_WEBHOOK': discord_webhook,
'DISCOGS_TOKEN': discogs_token
}.items() if not val]
logger.error(f"Missing required environment variables: {', '.join(missing_vars)}")
raise ValueError("Missing required environment variables")

logger.info(f"Configuration loaded successfully. Update interval set to: {update_interval}")
# Use the mounted path directly
music_path = "/music"

return music_path, update_interval, discord_webhook

def update_artist_list():
"""Update the JSON list of artists from the music directory"""
logger.info("Updating artist list...")
music_path = "/music"
artists = [d for d in os.listdir(music_path)
if os.path.isdir(os.path.join(music_path, d))]

with open('artists.json', 'w') as f:
json.dump({'artists': artists, 'last_updated': datetime.now().isoformat()}, f, indent=2)
try:
artists = [d for d in os.listdir(music_path)
if os.path.isdir(os.path.join(music_path, d))]

logger.info(f"Found {len(artists)} artists in music directory")

with open(CONFIG_PATH, 'w') as f:
json.dump({'artists': artists, 'last_updated': datetime.now().isoformat()}, f, indent=2)

logger.info("Artist list updated successfully")
logger.debug(f"Artists found: {', '.join(artists)}")
except Exception as e:
logger.error(f"Error updating artist list: {str(e)}")
raise

def send_discord_notification(release_info):
"""Send a Discord webhook notification about new releases"""
webhook_url = os.getenv('DISCORD_WEBHOOK')

logger.info(f"Sending Discord notification for {release_info['artist']} - {release_info['title']}")

embed = {
"title": f"New {release_info['type']} Release!",
"description": f"Artist: {release_info['artist']}\n"
Expand All @@ -60,38 +93,59 @@ def send_discord_notification(release_info):
}

payload = {"embeds": [embed]}
requests.post(webhook_url, json=payload)
try:
response = requests.post(webhook_url, json=payload)
response.raise_for_status()
logger.info("Discord notification sent successfully")
except requests.exceptions.RequestException as e:
logger.error(f"Failed to send Discord notification: {str(e)}")

def check_new_releases():
"""Check for new releases from tracked artists"""
logger.info("Starting new release check...")
try:
with open('artists.json', 'r') as f:
with open(CONFIG_PATH, 'r') as f:
data = json.load(f)
artists = data['artists']
last_check = datetime.fromisoformat(data.get('last_updated', '2024-01-01T00:00:00'))
logger.info(f"Loaded {len(artists)} artists from config. Last check: {last_check}")
except FileNotFoundError:
print("No artists.json found. Running initial scan...")
logger.warning("No artists.json found. Running initial scan...")
update_artist_list()
return
except json.JSONDecodeError:
logger.error("Error reading artists.json - file may be corrupted")
return
except Exception as e:
logger.error(f"Unexpected error reading config: {str(e)}")
return

# Initialize Discogs client
logger.info("Initializing Discogs client...")
discogs = discogs_client.Client('TracklyBot/1.0', user_token=os.getenv('DISCOGS_TOKEN'))

for artist in artists:
logger.info(f"Checking releases for artist: {artist}")
try:
# Search for the artist on Discogs
results = discogs.search(artist, type='artist')
if not results:
logger.warning(f"No Discogs results found for artist: {artist}")
continue

artist_id = results[0].id
logger.info(f"Found Discogs ID for {artist}: {artist_id}")
artist_releases = discogs.artist(artist_id).releases

# Check recent releases
releases_checked = 0
for release in artist_releases:
try:
release_date = datetime.strptime(release.release_date, '%Y-%m-%d')
releases_checked += 1

if release_date > last_check:
logger.info(f"Found new release for {artist}: {release.title}")
# Determine release type
release_type = 'album'
if release.formats[0].get('quantity', '1') == '1':
Expand All @@ -110,37 +164,63 @@ def check_new_releases():

send_discord_notification(release_info)
except (AttributeError, ValueError) as e:
print(f"Error processing release for {artist}: {e}")
logger.error(f"Error processing release for {artist}: {e}")
continue

logger.info(f"Checked {releases_checked} releases for {artist}")

except Exception as e:
print(f"Error checking releases for {artist}: {e}")
logger.error(f"Error checking releases for {artist}: {e}")
continue

logger.info("Completed release check for all artists")

def main():
"""Main function to run the artist tracker"""
logger.info("Starting Trackly...")

# Load environment variables
music_path, update_interval, _ = load_config()

# Make sure config directory exists
try:
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
logger.info(f"Config directory ensured at {os.path.dirname(CONFIG_PATH)}")
except Exception as e:
logger.error(f"Failed to create config directory: {str(e)}")
raise

# Initial scan of music directory
logger.info("Performing initial music directory scan...")
update_artist_list()

# Set up file system monitoring
event_handler = MusicFolderHandler('artists.json')
logger.info("Setting up file system monitoring...")
event_handler = MusicFolderHandler()
observer = Observer()
observer.schedule(event_handler, music_path, recursive=False)
observer.start()
logger.info(f"File system monitoring active for: {music_path}")

# Schedule regular checks based on update interval
logger.info(f"Scheduling daily checks at {update_interval}")
schedule.every().day.at(update_interval).do(check_new_releases)

# Run initial check
logger.info("Running initial release check...")
check_new_releases()

logger.info("Trackly startup complete")

try:
while True:
schedule.run_pending()
time.sleep(60) # Check every minute for scheduled tasks
except KeyboardInterrupt:
logger.info("Shutting down Trackly...")
observer.stop()
observer.join()
logger.info("Shutdown complete")

if __name__ == "__main__":
main()

0 comments on commit 741fea4

Please sign in to comment.