mirror of
https://github.com/standardebooks/web.git
synced 2025-07-18 20:36:38 -04:00
Flesh out OPDS generation backend to be more robust and to support generic Atom feeds
This commit is contained in:
parent
35188195f1
commit
f6df03cfca
23 changed files with 1549 additions and 267 deletions
|
@ -1,87 +1,45 @@
|
|||
<?
|
||||
use function Safe\file_get_contents;
|
||||
use function Safe\file_put_contents;
|
||||
use function Safe\preg_replace;
|
||||
use function Safe\rename;
|
||||
use function Safe\tempnam;
|
||||
use function Safe\unlink;
|
||||
|
||||
class OpdsFeed{
|
||||
public $Id;
|
||||
public $Url;
|
||||
public $Title;
|
||||
public $ParentUrl;
|
||||
class OpdsFeed extends AtomFeed{
|
||||
public $Updated = null;
|
||||
public $Parent = null; // OpdsNavigationFeed class
|
||||
|
||||
public function __construct(string $url, string $title, ?string $parentUrl){
|
||||
$this->Url = $url;
|
||||
$this->Id = SITE_URL . $url;
|
||||
$this->Title = $title;
|
||||
$this->ParentUrl = $parentUrl;
|
||||
public function __construct(string $url, string $title, string $path, array $entries, ?OpdsNavigationFeed $parent){
|
||||
parent::__construct($url, $title, $path, $entries);
|
||||
$this->Parent = $parent;
|
||||
$this->Stylesheet = '/opds/style';
|
||||
}
|
||||
|
||||
protected function Sha1Entries(string $xmlString): string{
|
||||
try{
|
||||
$xml = new SimpleXMLElement(str_replace('xmlns=', 'ns=', $xmlString));
|
||||
$xml->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
|
||||
$xml->registerXPathNamespace('schema', 'http://schema.org/');
|
||||
protected function SaveUpdatedTimestamp(string $entryId, DateTime $updatedTimestamp): void{
|
||||
// Only save the updated timestamp for the given entry ID in this file
|
||||
|
||||
// Remove any <updated> elements, we don't want to compare against those.
|
||||
foreach($xml->xpath('//updated') ?: [] as $element){
|
||||
unset($element[0]);
|
||||
foreach($this->Entries as $entry){
|
||||
if($entry->Id == $entryId){
|
||||
$entry->Updated = $updatedTimestamp;
|
||||
}
|
||||
|
||||
$output = '';
|
||||
foreach($xml->xpath('/feed/entry') ?: [] as $entry){
|
||||
$output .= $entry->asXml();
|
||||
}
|
||||
|
||||
return sha1(preg_replace('/\s/ius', '', $output));
|
||||
}
|
||||
catch(Exception $ex){
|
||||
// Invalid XML
|
||||
return '';
|
||||
|
||||
$this->XmlString = null;
|
||||
file_put_contents($this->Path, $this->GetXmlString());
|
||||
|
||||
// Do we have any parents of our own to update?
|
||||
if($this->Parent !== null){
|
||||
$this->Parent->SaveUpdatedTimestamp($this->Id, $updatedTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
protected function SaveIfChanged(string $path, string $feed, string $updatedTimestamp): void{
|
||||
$tempFilename = tempnam('/tmp/', 'se-opds-');
|
||||
file_put_contents($tempFilename, $feed);
|
||||
exec('se clean ' . escapeshellarg($tempFilename) . ' 2>&1', $output); // Capture the result in case there's an error, otherwise it prints to stdout
|
||||
$feed = file_get_contents($tempFilename);
|
||||
|
||||
protected function SaveIfChanged(): void{
|
||||
// Did we actually update the feed? If so, write to file and update the index
|
||||
if(!is_file($path) || ($this->Sha1Entries($feed) != $this->Sha1Entries(file_get_contents($path)))){
|
||||
if($this->HasChanged($this->Path)){
|
||||
// Files don't match, save the file and update the parent navigation feed with the last updated timestamp
|
||||
$parentFilepath = WEB_ROOT . str_replace(SITE_URL, '', $this->ParentUrl);
|
||||
if(!is_file($parentFilepath)){
|
||||
$parentFilepath .= '/index.xml';
|
||||
}
|
||||
$xml = new SimpleXMLElement(str_replace('xmlns=', 'ns=', file_get_contents($parentFilepath)));
|
||||
|
||||
$feedEntries = $xml->xpath('/feed/entry[id="' . $this->Id . '"]');
|
||||
if(!$feedEntries){
|
||||
$feedEntries = [];
|
||||
if($this->Parent !== null){
|
||||
$this->Parent->SaveUpdatedTimestamp($this->Id, $this->Updated);
|
||||
}
|
||||
|
||||
if(sizeof($feedEntries) > 0){
|
||||
$feedEntries[0]->{'updated'} = $updatedTimestamp;
|
||||
}
|
||||
|
||||
$xmlString = $xml->asXml();
|
||||
if($xmlString === false){
|
||||
$xmlString = '';
|
||||
}
|
||||
|
||||
file_put_contents($parentFilepath, str_replace(" ns=", " xmlns=", $xmlString));
|
||||
|
||||
// If we include this stylsheet declaration in the OPDS template, `se clean` will remove it and also
|
||||
// add a bunch of empty namespaces in the output. So, add it programatically here instead.
|
||||
file_put_contents($tempFilename, str_replace("?>", "?>\n<?xml-stylesheet href=\"/opds/style\" type=\"text/xsl\"?>", file_get_contents($tempFilename)));
|
||||
|
||||
rename($tempFilename, $path);
|
||||
}
|
||||
else{
|
||||
unlink($tempFilename);
|
||||
// Save our own file
|
||||
parent::Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue