From 03ed3c22578d92b0fd5bd630f13cefeca4d87c0c Mon Sep 17 00:00:00 2001 From: Mike Colagrosso Date: Thu, 24 Apr 2025 16:20:44 -0600 Subject: [PATCH] Add Ebook::DownloadUrl for web and feed downloads This commit adds a rewrite rule for ebook downloads of the form: ``` /ebooks/some-author/some-book/downloads/some-filename.epub ``` to `www/ebooks/download.php`. That file handles the logic of whether to show a thank you page before beginning the download. Download URLs in RSS, Atom, and OPDS feeds follow the same pattern, but they have a query string parameter `?source=feed` to always skip the thank you page. --- config/apache/rewrites/ebooks.conf | 2 +- lib/Ebook.php | 28 ++++++++++++ lib/Enums/EbookDownloadSource.php | 7 +++ lib/Enums/EbookFormatType.php | 23 ++++++++++ .../InvalidEbookFormatException.php | 5 +++ templates/AtomFeedEntry.php | 8 ++-- templates/OpdsAcquisitionEntry.php | 8 ++-- templates/RssEntry.php | 2 +- www/ebooks/download.php | 44 +++++++++---------- www/ebooks/get.php | 8 ++-- 10 files changed, 99 insertions(+), 36 deletions(-) create mode 100644 lib/Enums/EbookDownloadSource.php create mode 100644 lib/Exceptions/InvalidEbookFormatException.php diff --git a/config/apache/rewrites/ebooks.conf b/config/apache/rewrites/ebooks.conf index 869c2fae..301a932a 100644 --- a/config/apache/rewrites/ebooks.conf +++ b/config/apache/rewrites/ebooks.conf @@ -59,7 +59,7 @@ RewriteRule ^/ebooks/jules-verne/eight-hundred-leagues-on-the-amazon/w-j-gordon( RewriteRule ^/ebooks/jules-verne/twenty-thousand-leagues-under-the-seas/f-p-walter.* - [R=451,L] # Rewrite ebook downloads. -RewriteRule ^/ebooks/(.+?)/download$ /ebooks/download.php?url-path=$1 [QSA] +RewriteRule ^/ebooks/(.+?)/downloads/(.+?\.(?:epub|azw3))$ /ebooks/download.php?url-path=$1&filename=$2 [L,QSA] # Prevent this rule from firing if we're getting a distribution file. RewriteCond %{REQUEST_FILENAME} !^/ebooks/.+?/downloads/.+$ diff --git a/lib/Ebook.php b/lib/Ebook.php index 114f65fc..b7aa4fb3 100644 --- a/lib/Ebook.php +++ b/lib/Ebook.php @@ -1167,6 +1167,34 @@ final class Ebook{ $this->SetIdentifier(); } + public function GetDownloadUrl(Enums\EbookFormatType $format = Enums\EbookFormatType::Epub, ?Enums\EbookDownloadSource $source = null): ?string{ + switch($format){ + case Enums\EbookFormatType::Kepub: + $downloadUrl = $this->KepubUrl; + break; + + case Enums\EbookFormatType::Azw3: + $downloadUrl = $this->Azw3Url; + break; + + case Enums\EbookFormatType::AdvancedEpub: + $downloadUrl = $this->AdvancedEpubUrl; + break; + + case Enums\EbookFormatType::Epub: + default: + $downloadUrl = $this->EpubUrl; + break; + } + + if(isset($source)){ + $downloadUrl .= '?source=' . $source->value; + + } + + return $downloadUrl; + } + // ******* // METHODS // ******* diff --git a/lib/Enums/EbookDownloadSource.php b/lib/Enums/EbookDownloadSource.php new file mode 100644 index 00000000..86953674 --- /dev/null +++ b/lib/Enums/EbookDownloadSource.php @@ -0,0 +1,7 @@ + 'application/epub+zip' }; } + + /** + * @throws \Exceptions\InvalidEbookFormatException + */ + public static function FromFilename(string $filename): self{ + if(str_ends_with($filename, '.azw3')){ + return self::Azw3; + } + + if(str_ends_with($filename, '.kepub.epub')){ + return self::Kepub; + } + + if(str_ends_with($filename, '_advanced.epub')){ + return self::AdvancedEpub; + } + + if(str_ends_with($filename, '.epub')){ + return self::Epub; + } + + throw new \Exceptions\InvalidEbookFormatException(); + } } diff --git a/lib/Exceptions/InvalidEbookFormatException.php b/lib/Exceptions/InvalidEbookFormatException.php new file mode 100644 index 00000000..d378678b --- /dev/null +++ b/lib/Exceptions/InvalidEbookFormatException.php @@ -0,0 +1,5 @@ +Url ?>/downloads/cover-thumbnail.jpg" height="525" width="350"/> EpubUrl)){ ?> - + AdvancedEpubUrl)){ ?> - + KepubUrl)){ ?> - + Azw3Url)){ ?> - + TextSinglePageUrl . '.xhtml')){ ?> diff --git a/templates/OpdsAcquisitionEntry.php b/templates/OpdsAcquisitionEntry.php index ccdbf667..be65f998 100644 --- a/templates/OpdsAcquisitionEntry.php +++ b/templates/OpdsAcquisitionEntry.php @@ -42,16 +42,16 @@ use function Safe\filesize; EpubUrl)){ ?> - + AdvancedEpubUrl)){ ?> - + KepubUrl)){ ?> - + Azw3Url)){ ?> - + TextSinglePageUrl . '.xhtml')){ ?> diff --git a/templates/RssEntry.php b/templates/RssEntry.php index b0e27cbd..7c4a88af 100644 --- a/templates/RssEntry.php +++ b/templates/RssEntry.php @@ -23,6 +23,6 @@ catch(Safe\Exceptions\FilesystemException){ EpubUrl !== null){ ?> - is allowed */ ?> + is allowed */ ?> diff --git a/www/ebooks/download.php b/www/ebooks/download.php index 8a2c59f5..8c48590b 100644 --- a/www/ebooks/download.php +++ b/www/ebooks/download.php @@ -1,12 +1,17 @@ 4 || isset($source); try{ $urlPath = HttpInput::Str(GET, 'url-path') ?? null; @@ -17,28 +22,21 @@ try{ throw new Exceptions\InvalidFileException(); } - $format = Enums\EbookFormatType::tryFrom(HttpInput::Str(GET, 'format') ?? '') ?? Enums\EbookFormatType::Epub; - switch($format){ - case Enums\EbookFormatType::Kepub: - $downloadUrl = $ebook->KepubUrl; - break; - - case Enums\EbookFormatType::Azw3: - $downloadUrl = $ebook->Azw3Url; - break; - - case Enums\EbookFormatType::AdvancedEpub: - $downloadUrl = $ebook->AdvancedEpubUrl; - break; - - case Enums\EbookFormatType::Epub: - default: - $downloadUrl = $ebook->EpubUrl; - break; + $filename = HttpInput::Str(GET, 'filename'); + if(!isset($filename)){ + throw new Exceptions\InvalidFileException(); } - if(!$showThankYouPage){ + try{ + $format = Enums\EbookFormatType::FromFilename($filename); + } + catch(Exceptions\InvalidEbookFormatException){ + throw new Exceptions\InvalidFileException(); + } + + if($skipThankYouPage){ // Download the file directly, without showing the thank you page. + $downloadUrl = $ebook->GetDownloadUrl($format); $downloadPath = WEB_ROOT . $downloadUrl; if(!is_file($downloadPath)){ @@ -54,6 +52,8 @@ try{ exit(); } + $downloadUrl = $ebook->GetDownloadUrl($format, Enums\EbookDownloadSource::DownloadPage); + // Increment local download count, expires in 2 weeks. $downloadCount++; setcookie('download-count', (string)$downloadCount, ['expires' => intval((new DateTimeImmutable('+2 week'))->format(Enums\DateTimeFormat::UnixTimestamp->value)), 'path' => '/', 'domain' => SITE_DOMAIN, 'secure' => true, 'httponly' => false, 'samesite' => 'Lax']); diff --git a/www/ebooks/get.php b/www/ebooks/get.php index 0fd76d12..313e6fbc 100644 --- a/www/ebooks/get.php +++ b/www/ebooks/get.php @@ -200,7 +200,7 @@ catch(Exceptions\EbookNotFoundException){

- Compatible epub All devices and apps except Kindles and Kobos. + Compatible epub All devices and apps except Kindles and Kobos.

@@ -209,7 +209,7 @@ catch(Exceptions\EbookNotFoundException){
  • - azw3 Kindle devices and apps.KindleCoverUrl !== null){ ?> Also download the Kindle cover thumbnail to see the cover in your Kindle’s library. Despite what you’ve been told, Kindle does not natively support epub. You may also be interested in our Kindle FAQ. Also see our Kindle FAQ. + azw3 Kindle devices and apps.KindleCoverUrl !== null){ ?> Also download the Kindle cover thumbnail to see the cover in your Kindle’s library. Despite what you’ve been told, Kindle does not natively support epub. You may also be interested in our Kindle FAQ. Also see our Kindle FAQ.

  • @@ -218,7 +218,7 @@ catch(Exceptions\EbookNotFoundException){
  • - kepub Kobo devices and apps. You may also be interested in our Kobo FAQ. + kepub Kobo devices and apps. You may also be interested in our Kobo FAQ.

  • @@ -227,7 +227,7 @@ catch(Exceptions\EbookNotFoundException){
  • - Advanced epub An advanced format that uses the latest technology not yet fully supported by most ereaders. + Advanced epub An advanced format that uses the latest technology not yet fully supported by most ereaders.