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.
This commit is contained in:
Mike Colagrosso 2025-04-24 16:20:44 -06:00 committed by Alex Cabal
parent 1f41eb1e55
commit 03ed3c2257
10 changed files with 99 additions and 36 deletions

View file

@ -1,12 +1,17 @@
<?
use Safe\DateTimeImmutable;
// If the user is not logged in, or has less than some amount of downloads, show a thank-you page.
$ebook = null;
$downloadCount = HttpInput::Int(COOKIE, 'download-count') ?? 0;
$showThankYouPage = Session::$User === null && $downloadCount < 5;
$downloadUrl = null;
$downloadCount = HttpInput::Int(COOKIE, 'download-count') ?? 0;
$source = Enums\EbookDownloadSource::tryFrom(HttpInput::Str(GET, 'source') ?? '');
// Skip the thank you page if any of these are true:
// * The user is logged in.
// * Their `download-count` cookie is above some amount.
// * The link is from a specific source.
$skipThankYouPage = isset(Session::$User) || $downloadCount > 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']);

View file

@ -200,7 +200,7 @@ catch(Exceptions\EbookNotFoundException){
<meta property="schema:description" content="epub"/>
<meta property="schema:encodingFormat" content="application/epub+zip"/>
<p>
<span><a property="schema:contentUrl" href="<?= $ebook->Url ?>/download?format=<?= Enums\EbookFormatType::Epub->value ?>" class="epub">Compatible epub</a></span> <span></span> <span>All devices and apps except Kindles and Kobos.</span>
<span><a property="schema:contentUrl" href="<?= $ebook->GetDownloadUrl(Enums\EbookFormatType::Epub) ?>" class="epub">Compatible epub</a></span> <span></span> <span>All devices and apps except Kindles and Kobos.</span>
</p>
</li>
<? } ?>
@ -209,7 +209,7 @@ catch(Exceptions\EbookNotFoundException){
<li property="schema:encoding" typeof="schema:MediaObject">
<meta property="schema:encodingFormat" content="application/x-mobipocket-ebook"/>
<p>
<span><a property="schema:contentUrl" href="<?= $ebook->Url ?>/download?format=<?= Enums\EbookFormatType::Azw3->value ?>" class="amazon"><span property="schema:description">azw3</span></a></span> <span></span> <span>Kindle devices and apps.<? if($ebook->KindleCoverUrl !== null){ ?> Also download the <a href="<?= $ebook->KindleCoverUrl ?>">Kindle cover thumbnail</a> to see the cover in your Kindles library. Despite what youve been told, <a href="/help/how-to-use-our-ebooks#kindle-epub">Kindle does not natively support epub.</a> You may also be interested in our <a href="/help/how-to-use-our-ebooks#kindle-faq">Kindle FAQ</a>.<? }else{ ?> Also see our <a href="/how-to-use-our-ebooks#kindle-faq">Kindle FAQ</a>.<? } ?></span>
<span><a property="schema:contentUrl" href="<?= $ebook->GetDownloadUrl(Enums\EbookFormatType::Azw3) ?>" class="amazon"><span property="schema:description">azw3</span></a></span> <span></span> <span>Kindle devices and apps.<? if($ebook->KindleCoverUrl !== null){ ?> Also download the <a href="<?= $ebook->KindleCoverUrl ?>">Kindle cover thumbnail</a> to see the cover in your Kindles library. Despite what youve been told, <a href="/help/how-to-use-our-ebooks#kindle-epub">Kindle does not natively support epub.</a> You may also be interested in our <a href="/help/how-to-use-our-ebooks#kindle-faq">Kindle FAQ</a>.<? }else{ ?> Also see our <a href="/how-to-use-our-ebooks#kindle-faq">Kindle FAQ</a>.<? } ?></span>
</p>
</li>
<? } ?>
@ -218,7 +218,7 @@ catch(Exceptions\EbookNotFoundException){
<li property="schema:encoding" typeof="schema:MediaObject">
<meta property="schema:encodingFormat" content="application/kepub+zip"/>
<p>
<span><a property="schema:contentUrl" href="<?= $ebook->Url ?>/download?format=<?= Enums\EbookFormatType::Kepub->value ?>" class="kobo"><span property="schema:description">kepub</span></a></span> <span></span> <span>Kobo devices and apps. You may also be interested in our <a href="/help/how-to-use-our-ebooks#kobo-faq">Kobo FAQ</a>.</span>
<span><a property="schema:contentUrl" href="<?= $ebook->GetDownloadUrl(Enums\EbookFormatType::Kepub) ?>" class="kobo"><span property="schema:description">kepub</span></a></span> <span></span> <span>Kobo devices and apps. You may also be interested in our <a href="/help/how-to-use-our-ebooks#kobo-faq">Kobo FAQ</a>.</span>
</p>
</li>
<? } ?>
@ -227,7 +227,7 @@ catch(Exceptions\EbookNotFoundException){
<li property="schema:encoding" typeof="schema:MediaObject">
<meta property="schema:encodingFormat" content="application/epub+zip"/>
<p>
<span><a property="schema:contentUrl" href="<?= $ebook->Url ?>/download?format=<?= Enums\EbookFormatType::AdvancedEpub->value ?>" class="epub"><span property="schema:description">Advanced epub</span></a></span> <span></span> <span>An advanced format that uses the latest technology not yet fully supported by most ereaders.</span>
<span><a property="schema:contentUrl" href="<?= $ebook->GetDownloadUrl(Enums\EbookFormatType::AdvancedEpub) ?>" class="epub"><span property="schema:description">Advanced epub</span></a></span> <span></span> <span>An advanced format that uses the latest technology not yet fully supported by most ereaders.</span>
</p>
</li>
<? } ?>