mirror of
https://codeberg.org/Cyberpro123/AO3-DL.git
synced 2026-07-10 00:12:07 +00:00
515 lines
17 KiB
PHP
515 lines
17 KiB
PHP
<?php
|
|
# This file is part of AOMO, "Archive Of My Own", a collection of Python and PHP
|
|
# scripts designed act as an improved local backup system
|
|
# for works published on https://archiveofourown.org
|
|
#
|
|
# Copyright (c) 2025 Cyberpro123, except where otherwise noted.
|
|
#
|
|
# This project is available under the GNU General Public License (GPL) v2.0,
|
|
# available at https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
|
|
# or in the 'LICENSE.md' file that should be distributed alongside this one.
|
|
#
|
|
# Report issues to https://codeberg.org/Cyberpro123/AOMO
|
|
# Contact author at cyberpro123@posteo.com
|
|
|
|
|
|
################################################################
|
|
# Setup
|
|
################################################################
|
|
|
|
include ".aomoCommon.php";
|
|
|
|
define("CHAPTER", (int) get($_GET, "ch", get($_ENV, "AOMO_DEFAULT_CHAPTER", 1)));
|
|
define("IMAGE_PREFIX", (string) get($_ENV, "AOMO_IMG_PREFIX", "images/"));
|
|
define("IMAGE_PREFIX_MARKER", mb_chr(59907, "UTF-8"));
|
|
|
|
if (USER_ID) {
|
|
if (get($_POST, "progress_work_id", 0)) {
|
|
sqlFetch(array(
|
|
array("INSERT INTO progress_tracker (id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING", array($_POST['progress_work_id'], USER_ID)),
|
|
array("UPDATE progress_tracker SET chapter = ?, timestamp = ? WHERE id = ? AND user_id = ?", array($_POST['progress_chapter_no'], time(), $_POST['progress_work_id'], USER_ID))
|
|
));
|
|
}
|
|
}
|
|
|
|
|
|
$sqlArray = array(
|
|
"SELECT * FROM works WHERE id = ".ID,
|
|
"SELECT title, body FROM text WHERE id = ".ID." AND chapter = 0",
|
|
);
|
|
$dbReturn = sqlFetch($sqlArray);
|
|
$rowWorks = $dbReturn[0];
|
|
if ($rowWorks) {
|
|
$title = $dbReturn[1][0]['title'];
|
|
$workskin = $dbReturn[1][0]['body'];
|
|
$rowWorks = $rowWorks[0];
|
|
$chaptersCount = $rowWorks['chapters_count'];
|
|
} else {
|
|
$title = ID;
|
|
$workskin = "";
|
|
$chaptersCount = 0;
|
|
}
|
|
################################################################
|
|
# Functions
|
|
################################################################
|
|
function displayChapter($num, $oneshot=False) {
|
|
$rowTextLocal = sqlFetch(array("SELECT * FROM text WHERE id = ".ID." AND chapter = $num"))[0][0];
|
|
$outStr = "";
|
|
$outStr .= "<div id='chapter-$num' class='chapter'>";
|
|
$outStr .= "<div class='chapter preface group'>";
|
|
if (!$oneshot) {
|
|
$outStr .= "<h3 class='title'>Chapter $num: ".$rowTextLocal["title"]."</h3>";
|
|
}
|
|
if ($rowTextLocal['summary']) {
|
|
$outStr .= "<div id='summary' class='summary module'>
|
|
<h3 class='heading'>Summary:</h3>
|
|
<blockquote class='userstuff'>";
|
|
$outStr .= str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['summary']);
|
|
$outStr .= "</blockquote></div>"; # summary
|
|
}
|
|
if ($rowTextLocal['notes_start']) {
|
|
$outStr .= "<div id='notes' class='notes module'>
|
|
<h3 class='heading'>Notes:</h3>
|
|
<blockquote class='userstuff'>";
|
|
$outStr .= str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['notes_start']);
|
|
$outStr .= "</blockquote></div>"; # notes
|
|
}
|
|
$outStr .= "</div>"; # chapter preface group
|
|
|
|
$outStr .= "<div class='userstuff module' role='article'>".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['body'])."</div>";
|
|
|
|
if ($rowTextLocal['notes_end']) {
|
|
$outStr .= "
|
|
<div class='chapter preface group'>
|
|
<div id='chapter_".$num."_endnotes' class='end notes module'>
|
|
<h3 class='heading'>Notes:</h3>
|
|
<blockquote class='userstuff'>
|
|
".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowTextLocal['notes_end'])."
|
|
</blockquote>
|
|
</div>
|
|
</div>
|
|
";
|
|
}
|
|
|
|
$outStr .= "</div>"; # chapter-num
|
|
return $outStr;
|
|
}
|
|
|
|
function chapterSelector($chaptersCount) {
|
|
#<label for='ch'>Chapter Select:</label>
|
|
$outStr = "";
|
|
$outStr .= "<form method='get' class='chapterSelector'><p>
|
|
<input type='hidden' name='id' value='".ID."'>
|
|
<select name='ch'>
|
|
<option value='0'>View All Chapters</option>";
|
|
for ($i=1; $i <= $chaptersCount; $i++) {
|
|
$outStr .= "<option value='$i'>Chapter $i</option>";
|
|
}
|
|
$outStr .= "</select><input type='submit' value='Go'></p></form>";
|
|
return $outStr;
|
|
}
|
|
function getChapterNPButtons($chaptersCount) {
|
|
$outStr = "";
|
|
if (CHAPTER > 1) {
|
|
$outStr .= "<li class='chapter previous'><a href='work.php?id=".ID."&ch=".(CHAPTER - 1)."'>← Previous Chapter</a></li>";
|
|
}
|
|
if (CHAPTER != $chaptersCount and CHAPTER != 0) {
|
|
$outStr .= "<li class='chapter next'><a href='work.php?id=".ID."&ch=".(CHAPTER + 1)."'>Next Chapter →</a></li>";
|
|
}
|
|
return $outStr;
|
|
}
|
|
|
|
|
|
|
|
function getTagsBlurb($rowWorks) {
|
|
$sqlArray = array();
|
|
for ($i=1; $i < 9; $i++) {
|
|
$sqlArray[] = "SELECT tag FROM tags WHERE id=".ID." AND category = $i ORDER BY sequence";
|
|
}
|
|
if (USER_ID) {
|
|
$sqlArray[] = "SELECT chapter FROM progress_tracker WHERE id=".ID." AND user_id=".USER_ID;
|
|
}
|
|
$dbReturn = sqlFetch($sqlArray);
|
|
|
|
$rowsTags = array();
|
|
for ($i=1; $i < 9; $i++) {
|
|
$rowsTags[$i] = harvestTags($dbReturn[$i - 1], "tag");
|
|
}
|
|
if (USER_ID) {
|
|
if ($dbReturn[8]) {
|
|
$progressNo = $dbReturn[8][0]['chapter'];
|
|
} else {
|
|
$progressNo = 0;
|
|
}
|
|
}
|
|
unset($dbReturn);
|
|
$outStr = "";
|
|
$outStr .= "<div class='wrapper'><dl class='works meta group'>";
|
|
$outStr .= "<dt class='rating tags'>Rating:</dt><dd class='rating tags'><ul class='commas'><li>".$rowsTags[6][0]."</li></ul></dd>";
|
|
if ($rowsTags[7]) {
|
|
$outStr .= "<dt class='warning tags'>Archive Warning";
|
|
if (count($rowsTags[7]) > 1) {
|
|
$outStr .= "s";
|
|
}
|
|
$outStr .= ":</dt><dd class='warning tags'><ul class='commas'>";
|
|
foreach ($rowsTags[7] as $value) {
|
|
$outStr .= "<li>$value</li>";
|
|
}
|
|
$outStr .= "</ul></dd>";
|
|
}
|
|
if ($rowsTags[8]) {
|
|
$outStr .= "<dt class='category tags'>Categor";
|
|
if (count($rowsTags[8]) > 1) {
|
|
$outStr .= "ies";
|
|
} else {
|
|
$outStr .= "y";
|
|
}
|
|
$outStr .= "</dt><dd class='category tags'><ul class='commas'>";
|
|
foreach ($rowsTags[8] as $value) {
|
|
$outStr .= "<li>$value</li>";
|
|
}
|
|
$outStr .= "</ul></dd>";
|
|
}
|
|
foreach (array(
|
|
array(1, "Fandom", "fandom"),
|
|
array(2, "Relationship", "relationship"),
|
|
array(3, "Character", "character"),
|
|
array(4, "Additional Tag", "freeform")
|
|
) as $value) {
|
|
if ($rowsTags[$value[0]]) {
|
|
$outStr .= "<dt class='$value[2] tags'>$value[1]";
|
|
if (count($rowsTags[$value[0]]) > 1) {
|
|
$outStr .= "s";
|
|
}
|
|
$outStr .= ":</dt><dd class='$value[2] tags'><ul class='commas'>";
|
|
foreach ($rowsTags[$value[0]] as $value2) {
|
|
$outStr .= "<li><a class='tag'>$value2</a></li>";
|
|
}
|
|
$outStr .= "</ul></dd>";
|
|
}
|
|
}
|
|
$outStr .= "<dt class='language'>Language:</dt><dd class='language'>".$rowsTags[5][0]."</dd>";
|
|
$rowsSeries = sqlFetch(array("SELECT series_id, pos FROM series_works WHERE id = ".ID))[0];
|
|
if ($rowsSeries) {
|
|
$outStr .= "<dt class='series'>Series:</dt><dd class='series'><ul class='commas'>";
|
|
foreach ($rowsSeries as $value) {
|
|
$outStr .= "<li>".getSeriesBlurb($value['series_id'], $value['pos'])."</li>";
|
|
}
|
|
$outStr .= "</ul></dd>";
|
|
}
|
|
$outStr .= "<dt class='stats'>Stats:</dt><dd class='stats'><dl class='stats'>";
|
|
$outStr .= "<dt class='published'>Published:</dt><dd class='published'>".date("Y-m-d", $rowWorks["date_published"])."</dd>";
|
|
if ($rowWorks["chapters_expected"] != 1) {
|
|
$outStr .= "<dt class='status'>";
|
|
if ($rowWorks["chapters_expected"] == $rowWorks["chapters_count"]) {
|
|
$outStr .= "Completed";
|
|
} else {
|
|
$outStr .= "Updated";
|
|
}
|
|
$outStr .= ":</dt><dd class='status'>".date("Y-m-d", $rowWorks["date_last_updated"])."</dd>";
|
|
}
|
|
$outStr .= "<dt class='words'>Words:</dt><dd class='words'>".$rowWorks['word_count_ao3']."<span title='This word count generated by AO3, not AOMO'>*</span></dt>";
|
|
$outStr .= "<dt class='chapters'>Chapters:</dt><dd class='chapters'>".$rowWorks["chapters_count"]."/";
|
|
if ($rowWorks["chapters_expected"] == 0) {
|
|
$outStr .= "?";
|
|
} else {
|
|
$outStr .= $rowWorks["chapters_expected"];
|
|
}
|
|
$outStr .= "</dd>";
|
|
if (USER_ID) {
|
|
$outStr .= "<dt class='progress'>Progress</dt><dd class='progress'>$progressNo/";
|
|
$outStr .= $rowWorks['chapters_count'];
|
|
$outStr .= "</dd>";
|
|
}
|
|
$outStr .= "</dl></dd>";
|
|
$outStr .= "</dl></div>"; # end header
|
|
return $outStr;
|
|
}
|
|
|
|
|
|
function getTopNavButtons($title, $chaptersCount) {
|
|
$outStr = "";
|
|
$outStr .= "<ul class='work navigation actions'>";
|
|
$outStr .= "<li><a href='https://archiveofourown.org/works/".ID."'>Link to '$title' on AO3</a></li>";
|
|
if (USER_ID) {
|
|
$outStr .= getMarkProgressButton(0, "Mark as Unread");
|
|
}
|
|
if ($chaptersCount > 1) {
|
|
$outStr .= "<li class='chapter' id='chapter_index_button'><a href='#chapter_index_button'>Chapter Selector</a><ul id='chapter_index' class='expandable secondary'><li>";
|
|
$outStr .= chapterSelector($chaptersCount);
|
|
$outStr .= "</li><li><a href='#outer'>Close</a></li></ul></li>";
|
|
$outStr .= getChapterNPButtons($chaptersCount);
|
|
}
|
|
$outStr .= "</ul>";
|
|
return $outStr;
|
|
}
|
|
|
|
|
|
function getWorkByAuthor($id, $title, $authorRows) {
|
|
$outStr = "";
|
|
$outStr .= linkToWork($id, $title);
|
|
$outStr .= " by ";
|
|
$firstTag = true;
|
|
foreach ($authorRows as $value) {
|
|
if ($firstTag) {
|
|
$firstTag = false;
|
|
} else {
|
|
$outStr .= ", ";
|
|
}
|
|
$outStr .= $value['tag'];
|
|
}
|
|
return $outStr;
|
|
}
|
|
|
|
function getInspirationBlurb($id, $relation) {
|
|
$outStr = "<li>";
|
|
if ($id) {
|
|
$dbReturn = sqlFetch(array(
|
|
array("SELECT title FROM text WHERE id = ? AND chapter = 0", array($id)),
|
|
array("SELECT tag FROM tags WHERE id = ? AND category = 5 ORDER BY sequence", array($id)),
|
|
array("SELECT tag FROM tags WHERE id = ? AND category = 9 ORDER BY sequence", array($id)),
|
|
));
|
|
if ($dbReturn[0]) {
|
|
$title = $dbReturn[0][0]['title'];
|
|
switch ($relation) {
|
|
case 0:
|
|
$outStr .= "Inspired by ";
|
|
case 1:
|
|
$outStr .= getWorkByAuthor($id, $title, $dbReturn[2]);
|
|
break;
|
|
case 2:
|
|
$outStr .= "A translation of ";
|
|
$outStr .= getWorkByAuthor($id, $title, $dbReturn[2]);
|
|
break;
|
|
case 3:
|
|
$outStr .= "Translation into ".$dbReturn[1][0]['tag']." available: ";
|
|
$outStr .= getWorkByAuthor($id, $title, $dbReturn[2]);
|
|
break;
|
|
}
|
|
} else {
|
|
$title = "Undownloaded Work $id";
|
|
switch ($relation) {
|
|
case 0:
|
|
$outStr .= "Inspired by ";
|
|
case 1:
|
|
$outStr .= "<a href='https://archiveofourown.org/works/$id'>$title</a>";
|
|
break;
|
|
case 2:
|
|
$outStr .= "A translation of ";
|
|
$outStr .= "<a href='https://archiveofourown.org/works/$id'>$title</a>";
|
|
break;
|
|
case 3:
|
|
$outStr .= "Translation into unknown language available: ";
|
|
$outStr .= "<a href='https://archiveofourown.org/works/$id'>$title</a>";
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
switch ($relation) {
|
|
case 0:
|
|
$outStr .= "Inspired by ";
|
|
case 1:
|
|
$outStr .= "[Unknown Work]";
|
|
break;
|
|
case 2:
|
|
$outStr .= "A translation of [Unknown Work]";
|
|
break;
|
|
case 3:
|
|
$outStr .= "Translation available: [Unknown Work]";
|
|
break;
|
|
}
|
|
}
|
|
$outStr .= "</li>";
|
|
return $outStr;
|
|
}
|
|
|
|
|
|
function getTitlePrefaceGroup($title) {
|
|
$dbReturn = sqlFetch(array(
|
|
"SELECT summary, notes_start FROM text WHERE id = ".ID." AND chapter = 0",
|
|
"SELECT tag FROM tags WHERE id = ".ID." AND category = 9 ORDER BY sequence",
|
|
array("SELECT id FROM inspirations WHERE translation = 1 AND parent_id = ?", array(ID)),
|
|
array("SELECT parent_id FROM inspirations WHERE translation = 1 AND id = ?", array(ID)),
|
|
array("SELECT parent_id FROM inspirations WHERE translation = 0 AND id = ?", array(ID)),
|
|
));
|
|
$rowText = $dbReturn[0][0];
|
|
$authorStr = "";
|
|
foreach ($dbReturn[1] as $value) {
|
|
if ($authorStr) {
|
|
$authorStr .= ", ";
|
|
}
|
|
$authorStr .= $value['tag'];
|
|
}
|
|
$translations = $dbReturn[2];
|
|
$translation_of = $dbReturn[3];
|
|
$inspired_by = $dbReturn[4];
|
|
$associations = ($translations or $translation_of or $inspired_by);
|
|
$outStr = "";
|
|
$outStr .= "<div class='preface group'>
|
|
<h2 class='title heading'>$title</h2>
|
|
<h3 class='byline heading'>$authorStr</h3>
|
|
";
|
|
if (CHAPTER < 2) {
|
|
if ($rowText['summary']) {
|
|
$outStr .= "<div class='summary module'>
|
|
<h3 class='heading'>Summary:</h3>
|
|
<blockquote class='userstuff'>
|
|
".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowText['summary'])."</blockquote></div>";
|
|
}
|
|
if ($rowText['notes_start'] or $associations) {
|
|
$outStr .= "<div class='notes module'><h3 class='heading'>Notes:</h3>";
|
|
if ($associations) {
|
|
$outStr .= "<ul class='associations'>";
|
|
foreach ($translation_of as $value) {
|
|
$outStr .= getInspirationBlurb($value['parent_id'], 2);
|
|
}
|
|
foreach ($translations as $value) {
|
|
$outStr .= getInspirationBlurb($value['id'], 3);
|
|
}
|
|
foreach ($inspired_by as $value) {
|
|
$outStr .= getInspirationBlurb($value['parent_id'], 0);
|
|
}
|
|
$outStr .= "</ul>";
|
|
}
|
|
if ($rowText['notes_start']) {
|
|
$outStr .= "<blockquote class='userstuff'>
|
|
".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $rowText['notes_start'])."</blockquote>";
|
|
}
|
|
$outStr .= "</div>";
|
|
}
|
|
}
|
|
$outStr .= "</div>"; # preface group
|
|
return $outStr;
|
|
}
|
|
|
|
function getEndnotesAfterwordGroup() {
|
|
$dbReturn = sqlFetch(array(
|
|
array("SELECT notes_end FROM text WHERE id = ? AND chapter = 0", array(ID)),
|
|
array("SELECT id FROM inspirations WHERE translation = 0 AND parent_id = ?", array(ID)),
|
|
));
|
|
if ($dbReturn[0]) {
|
|
$endnotes = $dbReturn[0][0]['notes_end'];
|
|
} else {
|
|
$endnotes = "";
|
|
}
|
|
$inspirations = $dbReturn[1];
|
|
$outStr = "";
|
|
if ($endnotes or $inspirations) {
|
|
$outStr .= "<div class='afterword preface group'>";
|
|
if ($endnotes) {
|
|
$outStr .= "<div id='work_endnotes' class='end notes module'>
|
|
<h3 class='heading'>Notes:</h3>
|
|
<blockquote class='userstuff'>".str_replace(IMAGE_PREFIX_MARKER, IMAGE_PREFIX, $endnotes)."</blockquote>
|
|
</div>";
|
|
}
|
|
if ($inspirations) {
|
|
$outStr .= "<div id='children' class='children module'><h3 class='heading'>Works inspired by this one:</h3><ul>";
|
|
foreach ($inspirations as $value) {
|
|
$outStr .= getInspirationBlurb($value['id'], 1);
|
|
}
|
|
$outStr .= "</ul></div>";
|
|
}
|
|
$outStr .= "</div>";
|
|
}
|
|
return $outStr;
|
|
}
|
|
|
|
function getMarkProgressButton($chapter, $prompt="Mark as Read") {
|
|
$outStr = "";
|
|
$outStr .= "<li><form method='post'>";
|
|
$outStr .= "<input type='hidden' name='progress_work_id' value='".ID."'>";
|
|
$outStr .= "<input type='hidden' name='progress_chapter_no' value='$chapter'>";
|
|
$outStr .= "<input type='submit' value='$prompt'>";
|
|
$outStr .= "</form></li>";
|
|
return $outStr;
|
|
}
|
|
|
|
|
|
################################################################
|
|
# Error Handling
|
|
################################################################
|
|
if ($chaptersCount == 0) {
|
|
# Requested work not in database
|
|
$titleStr = "Invalid Work ID - AOMO";
|
|
http_response_code(404);
|
|
$error = 1;
|
|
} elseif ($chaptersCount < CHAPTER or CHAPTER < 0) {
|
|
# Requested chapter we don't have
|
|
$titleStr = "Invalid Chapter ID - AOMO";
|
|
http_response_code(404);
|
|
$error = 2;
|
|
} else {
|
|
# Normal Operation
|
|
$titleStr = "$title - ".ID." - AOMO";
|
|
$error = 0;
|
|
}
|
|
|
|
################################################################
|
|
# Main
|
|
################################################################
|
|
|
|
echo getSiteMainLead($titleStr, $workskin);
|
|
|
|
|
|
################################################################
|
|
# HTML Body
|
|
################################################################
|
|
|
|
if ($error == 1) {
|
|
# Requested work not in database
|
|
echo "
|
|
<div id='errorPage'>
|
|
<h1>Error 404</h1>
|
|
<h2>Work ID ".ID." not in database</h2>
|
|
<p><a href='https://archiveofourown.org/works/".ID."'>View on AO3</a></p>
|
|
</div>
|
|
";
|
|
} elseif ($error == 2) {
|
|
# Requested chapter we don't have
|
|
echo "
|
|
<div id='errorPage'>
|
|
<h1>Error 404</h1>
|
|
<h2>Work <span class='workTitle'>'$title'</span> doesn't have a chapter ".CHAPTER."</h2>";
|
|
#if ($chaptersCount == 1) {
|
|
# # Oneshot
|
|
# echo "<li><a href='work.php?id=$id&ch=1'>Go to chapter 1</a></li>";
|
|
#} else {
|
|
echo chapterSelector($chaptersCount);
|
|
#}
|
|
echo "
|
|
<p><a href='https://archiveofourown.org/works/".ID."'>View on AO3</a></p>
|
|
</div>
|
|
";
|
|
} else {
|
|
# Normal Operation
|
|
echo getTopNavButtons($title, $chaptersCount);
|
|
echo "<h3 class='landmark heading'>Work Header</h3>";
|
|
echo getTagsBlurb($rowWorks);
|
|
echo "<div id='workskin'>";
|
|
echo getTitlePrefaceGroup($title);
|
|
echo "<div id='chapters' role='article'>";
|
|
|
|
$oneshot = $chaptersCount == 1;
|
|
if (CHAPTER == 0) {
|
|
for ($i = 1; $i <= $chaptersCount; $i++) {
|
|
echo displayChapter($i, $oneshot);
|
|
}
|
|
} else {
|
|
echo displayChapter(CHAPTER, $oneshot);
|
|
}
|
|
echo "</div>";
|
|
|
|
echo getEndnotesAfterwordGroup();
|
|
echo "<ul class='actions'>";
|
|
if (USER_ID) {
|
|
if (CHAPTER == 0) {
|
|
echo getMarkProgressButton($chapters_count);
|
|
} else {
|
|
echo getMarkProgressButton(CHAPTER);
|
|
}
|
|
}
|
|
echo getChapterNPButtons($chaptersCount);
|
|
echo "</ul>";
|
|
}
|
|
echo getSiteMainEnd();
|
|
?>
|