mirror of
https://github.com/standardebooks/web.git
synced 2025-07-04 22:00:35 -04:00
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:
parent
1f41eb1e55
commit
03ed3c2257
10 changed files with 99 additions and 36 deletions
|
@ -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/.+$
|
||||
|
|
|
@ -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
|
||||
// *******
|
||||
|
|
7
lib/Enums/EbookDownloadSource.php
Normal file
7
lib/Enums/EbookDownloadSource.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?
|
||||
namespace Enums;
|
||||
|
||||
enum EbookDownloadSource: string{
|
||||
case Feed = 'feed';
|
||||
case DownloadPage = 'download';
|
||||
}
|
|
@ -13,4 +13,27 @@ enum EbookFormatType: string{
|
|||
default => '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();
|
||||
}
|
||||
}
|
||||
|
|
5
lib/Exceptions/InvalidEbookFormatException.php
Normal file
5
lib/Exceptions/InvalidEbookFormatException.php
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?
|
||||
namespace Exceptions;
|
||||
|
||||
class InvalidEbookFormatException extends AppException{
|
||||
}
|
|
@ -29,16 +29,16 @@ use function Safe\filesize;
|
|||
<media:thumbnail url="<?= SITE_URL . $entry->Url ?>/downloads/cover-thumbnail.jpg" height="525" width="350"/>
|
||||
<link href="<?= SITE_URL . $entry->Url ?>" rel="alternate" title="This ebook’s page at Standard Ebooks" type="application/xhtml+xml"/>
|
||||
<? if(file_exists(WEB_ROOT . $entry->EpubUrl)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->EpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="enclosure" title="Recommended compatible epub" type="application/epub+zip" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::Epub, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="enclosure" title="Recommended compatible epub" type="application/epub+zip" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->AdvancedEpubUrl)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->AdvancedEpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="enclosure" title="Advanced epub" type="application/epub+zip" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::AdvancedEpub, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="enclosure" title="Advanced epub" type="application/epub+zip" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->KepubUrl)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->KepubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="enclosure" title="Kobo Kepub epub" type="application/kepub+zip" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::Kepub, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="enclosure" title="Kobo Kepub epub" type="application/kepub+zip" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->Azw3Url)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->Azw3Url ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="enclosure" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::Azw3, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="enclosure" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->TextSinglePageUrl . '.xhtml')){ ?>
|
||||
<link href="<?= SITE_URL . $entry->TextSinglePageUrl ?>" length="<?= filesize(WEB_ROOT . $entry->TextSinglePageUrl . '.xhtml') ?>" rel="enclosure" title="XHTML" type="application/xhtml+xml" />
|
||||
|
|
|
@ -42,16 +42,16 @@ use function Safe\filesize;
|
|||
<link href="<?= SITE_URL . $entry->Url ?>/downloads/cover-thumbnail.jpg" rel="http://opds-spec.org/image/thumbnail" type="image/jpeg"/>
|
||||
<link href="<?= SITE_URL . $entry->Url ?>" rel="alternate" title="This ebook’s page at Standard Ebooks" type="application/xhtml+xml"/>
|
||||
<? if(file_exists(WEB_ROOT . $entry->EpubUrl)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->EpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Recommended compatible epub" type="application/epub+zip" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::Epub, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->EpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Recommended compatible epub" type="application/epub+zip" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->AdvancedEpubUrl)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->AdvancedEpubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Advanced epub" type="application/epub+zip" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::AdvancedEpub, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->AdvancedEpubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Advanced epub" type="application/epub+zip" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->KepubUrl)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->KepubUrl ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Kobo Kepub epub" type="application/kepub+zip" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::Kepub, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->KepubUrl) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Kobo Kepub epub" type="application/kepub+zip" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->Azw3Url)){ ?>
|
||||
<link href="<?= SITE_URL . $entry->Azw3Url ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" />
|
||||
<link href="<?= SITE_URL . $entry->GetDownloadUrl(Enums\EbookFormatType::Azw3, Enums\EbookDownloadSource::Feed) ?>" length="<?= filesize(WEB_ROOT . $entry->Azw3Url) ?>" rel="http://opds-spec.org/acquisition/open-access" title="Amazon Kindle azw3" type="application/x-mobipocket-ebook" />
|
||||
<? } ?>
|
||||
<? if(file_exists(WEB_ROOT . $entry->TextSinglePageUrl . '.xhtml')){ ?>
|
||||
<link href="<?= SITE_URL . $entry->TextSinglePageUrl ?>" length="<?= filesize(WEB_ROOT . $entry->TextSinglePageUrl . '.xhtml') ?>" rel="http://opds-spec.org/acquisition/open-access" title="XHTML" type="application/xhtml+xml" />
|
||||
|
|
|
@ -23,6 +23,6 @@ catch(Safe\Exceptions\FilesystemException){
|
|||
<? } ?>
|
||||
<media:thumbnail url="<?= SITE_URL . $entry->Url ?>/downloads/cover-thumbnail.jpg" height="525" width="350"/>
|
||||
<? if($entry->EpubUrl !== null){ ?>
|
||||
<enclosure url="<?= SITE_URL . Formatter::EscapeXml($entry->EpubUrl) ?>" length="<?= $filesize ?>" type="application/epub+zip" /> <? /* Only one <enclosure> is allowed */ ?>
|
||||
<enclosure url="<?= SITE_URL . Formatter::EscapeXml($entry->GetDownloadUrl(Enums\EbookFormatType::Epub, Enums\EbookDownloadSource::Feed)) ?>" length="<?= $filesize ?>" type="application/epub+zip" /> <? /* Only one <enclosure> is allowed */ ?>
|
||||
<? } ?>
|
||||
</item>
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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 Kindle’s library. Despite what you’ve 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 Kindle’s library. Despite what you’ve 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>
|
||||
<? } ?>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue