Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
5 changes: 2 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Podsumer is a self-hosted podcast aggregator (podcatcher) written in PHP with ze
4. **Constants**: UPPERCASE with underscores (e.g., `VERSION`, `PODSUMER_PATH`)
5. **Database Tables**: Plural snake_case (e.g., `feeds`, `items`, `file_contents`)
6. **Database Columns**: snake_case (e.g., `url_hash`, `last_update`)
7. **Configuration Keys**: snake_case (e.g., `store_media_on_disk`, `state_file`)
7. **Configuration Keys**: snake_case (e.g., `media_dir`, `state_file`)

### File Organization

Expand Down Expand Up @@ -155,7 +155,7 @@ function feed(array $args): void

## Special Considerations

1. **Media Storage**: Can be configured for database or disk storage, but cannot be changed after library is established
1. **Media Storage**: All media files are stored on disk in the configured media_dir
2. **Performance**: Database operations use transactions and optimized PRAGMA settings
3. **Compatibility**: Requires SQLite 3.6.19+ with foreign key support
4. **Memory**: No memory limit set (`memory_limit = -1`)
Expand All @@ -173,7 +173,6 @@ function feed(array $args): void
2. Generate coverage: `composer coverage`
3. View coverage locally: `composer coverage-dev`
4. Ensure no regression in test coverage
5. Test with both database and disk storage modes

## Contribution Behavior

Expand Down
78 changes: 49 additions & 29 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,32 +1,52 @@
FROM php:8.2.12-apache-bookworm
RUN apt update && apt install -y git
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" && \
php composer-setup.php && \
php -r "unlink('composer-setup.php');" && \
mv composer.phar /usr/local/bin/composer
FROM php:8.2-apache

# Install system dependencies
RUN apt-get update && apt-get install -y \
libzip-dev && \
docker-php-ext-configure zip && \
docker-php-ext-install -j$(nproc) zip
libsqlite3-dev \
sqlite3 \
ffmpeg \
cron \
&& rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo pdo_sqlite

# Enable Apache modules
RUN a2enmod rewrite
RUN pecl install pcov && docker-php-ext-enable pcov
RUN mkdir -p /opt/podsumer/
RUN mkdir -p /opt/podsumer/conf
COPY ./apache.conf /etc/apache2/sites-available/000-default.conf
COPY ./apache.conf /etc/apache2/sites-enabled/000-default.conf
COPY ./conf/podsumer.conf /opt/podsumer/conf/podsumer.conf
COPY ./sql /opt/podsumer/sql
COPY ./src /opt/podsumer/src
COPY ./templates /opt/podsumer/templates
COPY ./www /opt/podsumer/www
COPY ./composer.json /opt/podsumer/composer.json
COPY ./composer.lock /opt/podsumer/composer.lock
WORKDIR /opt/podsumer
RUN chown -R www-data:www-data /opt/podsumer
RUN chmod -R 755 /opt/podsumer
RUN chown -R www-data:www-data /etc/apache2/sites-available/000-default.conf
RUN chown -R www-data:www-data /etc/apache2/sites-enabled/000-default.conf
RUN composer dump-autoload
EXPOSE 3094

# Set working directory
WORKDIR /var/www/html

# Copy application files
COPY . .

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Install dependencies
RUN composer install --no-dev --optimize-autoloader

# Set permissions
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html \
&& chmod +x /var/www/html/scripts/refresh_feeds.php

# Create media directory
RUN mkdir -p /opt/media && chown -R www-data:www-data /opt/media

# Configure Apache - fix the path to use the correct apache.conf file
COPY apache.conf /etc/apache2/sites-available/000-default.conf

# Create a script to generate the crontab with the configured interval
RUN echo '#!/bin/bash\n\
REFRESH_INTERVAL=$(php -r "include \"/var/www/html/conf/podsumer.conf\"; echo \$feed_refresh_interval ?? 6;")\n\
echo "0 */${REFRESH_INTERVAL} * * * www-data /usr/local/bin/php /var/www/html/scripts/refresh_feeds.php >> /var/log/cron.log 2>&1" > /etc/cron.d/podsumer-cron\n\
chmod 0644 /etc/cron.d/podsumer-cron\n\
crontab /etc/cron.d/podsumer-cron\n\
service cron start\n\
apache2-foreground' > /usr/local/bin/start.sh \
&& chmod +x /usr/local/bin/start.sh

# Start the container with our custom script
CMD ["/usr/local/bin/start.sh"]

76 changes: 63 additions & 13 deletions conf/podsumer.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,9 @@ per_item_art_download = 33
items_per_page = 10
ssl = false

# File Storage Method
# Media Directory
#
# By default all data is stored in the database. If you would prefer
# to have your audio files and artwork stored on disk set this to
# `true` and specify a path where you would like podsumer to save
# files. If you have a very large libary this should improve the
# performance and size of backups.
#
# NOTE: If this value is changed after a library is already established,
# only _new_ items and feeds will use the new storage backend. Previously
# downloaded files will remain where they are.

store_media_on_disk = true

# Directory where media files audio and images are stored.
# Make sure podsumer has permission to read and write in this directory.
# media_dir cannot be changed once library is established. If changed, some media
# files may no longer be servable.
Expand All @@ -33,3 +22,64 @@ media_dir = /opt/media
playback_interval = 5
playback_rewind = 5

# PodcastIndex API key and secret
#
# These are used to search for podcasts and episodes using the PodcastIndex API.
#
# You can get your own API key and secret by signing up at https://podcastindex.org/
#
# Once you have your API key and secret, you can add them here.
podcastindex_key = "YOUR_PODCAST_INDEX_KEY_HERE"
podcastindex_secret = "YOUR_PODCAST_INDEX_SECRET_HERE"

# Ad Blocking Configuration
#
# Enable automatic ad detection and removal from podcasts.
# This feature uses OpenAI Whisper for transcription and GPT-4o-mini for ad detection.
# Default is off to save on API costs.
#
# Note: This requires an OpenAI API key and will incur costs based on usage.
ad_blocking_enabled = true

# OpenAI API key for transcription and ad detection
openai_api_key = "YOUR_OPENAI_API_KEY_HERE"

# OpenAI model for ad detection
# Options: gpt-4o-mini cheaper, gpt-4o more accurate but expensive
openai_ad_detection_model = "gpt-4o"

# Whether to use ffmpeg to strip ads from audio files
# If false, ads will be skipped in the web player instead
use_ffmpeg_ad_removal = false

# Ad segment merge buffer in seconds
# Ad segments that are within this many seconds of each other will be merged
# This reduces confusion and makes skipping easier by combining closely spaced ads
# Default is 8 seconds - increase for more aggressive merging, decrease for more precision
ad_merge_buffer_seconds = 8

# OpenAI Pricing Configuration
#
# OpenAI pricing per 1000 tokens as of December 2024
# Update these values as OpenAI changes their pricing
# Current pricing can be found at: https://openai.com/api/pricing/
#
# Note: The system will use actual token counts from API responses for accurate cost calculation
# rather than estimating based on character count.
#
# Whisper pricing per minute of audio
openai_whisper_cost_per_minute = 0.006

# GPT-4o-mini pricing per 1000 tokens - Current as of Dec 2024
openai_gpt4o_mini_input_cost_per_1k_tokens = 0.00015
openai_gpt4o_mini_output_cost_per_1k_tokens = 0.0006

# GPT-4o pricing per 1000 tokens - in case you want to use a different model
openai_gpt4o_input_cost_per_1k_tokens = 0.0025
openai_gpt4o_output_cost_per_1k_tokens = 0.01

# Feed Refresh Configuration
#
# How often to refresh feeds in hours
# Default is 6 hours
feed_refresh_interval = 1
3 changes: 3 additions & 0 deletions conf/test.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ ssl = false
media_dir = /opt/media
playback_interval = 5
playback_rewind = 5

podcastindex_key = ""
podcastindex_secret = ""
Loading