From c293a6a9e3c46ff31c103a90f92d9de41554fd9f Mon Sep 17 00:00:00 2001 From: Spine Date: Sat, 4 Mar 2023 11:41:56 +0000 Subject: [PATCH] revamp torrent log uploading --- app/Json/AddLog.php | 80 ++++------------- app/Logfile.php | 10 +-- app/LogfileSummary.php | 37 ++++---- app/Manager/Torrent.php | 38 ++++---- app/Manager/TorrentLog.php | 21 ++++- app/Torrent.php | 29 ++---- app/TorrentAbstract.php | 5 +- cypress.config.js | 5 +- package.json | 1 + sections/ajax/add_log.php | 10 ++- sections/ajax/logchecker.php | 23 ++--- sections/logchecker/take_test.php | 7 +- sections/logchecker/take_upload.php | 39 +++----- sections/torrents/delete_log.php | 1 + sections/torrents/edit_log.php | 2 +- sections/torrents/remove_logs.php | 1 + sections/torrents/takeedit.php | 61 ++++--------- sections/upload/upload_handle.php | 113 +++++++++--------------- templates/torrent/riplog.twig | 4 +- tests/cypress/e2e/1-simple/basic.cy.js | 3 +- tests/cypress/e2e/1-simple/upload.cy.js | 85 ++++++++++++++++-- tests/cypress/support/commands.js | 15 ++++ tests/cypress/support/e2e.js | 10 ++- yarn.lock | 38 ++++++++ 24 files changed, 340 insertions(+), 298 deletions(-) diff --git a/app/Json/AddLog.php b/app/Json/AddLog.php index a9608c4c2..af901039a 100644 --- a/app/Json/AddLog.php +++ b/app/Json/AddLog.php @@ -2,94 +2,44 @@ namespace Gazelle\Json; -use Gazelle\File\RipLog; -use Gazelle\File\RipLogHTML; -use Gazelle\Logfile; -use Gazelle\LogfileSummary; use OrpheusNET\Logchecker\Logchecker; class AddLog extends \Gazelle\Json { public function __construct( - protected \Gazelle\Torrent $torrent, - protected \Gazelle\User $user, - protected array $files, + protected \Gazelle\Torrent $torrent, + protected \Gazelle\User $user, + protected \Gazelle\Manager\TorrentLog $torrentLogManager, + protected \Gazelle\LogfileSummary $logfileSummary, ) {} public function payload(): ?array { - $logfiles = []; if ($this->user->id() !== $this->torrent->uploaderId() && !$this->user->permitted('admin_add_log')) { $this->failure('Not the torrent owner or moderator'); return null; } - $logfileSummary = new LogfileSummary; - $ripFiler = new RipLog; - $htmlFiler = new RipLogHTML; - - $torrentId = $this->torrent->id(); $logSummaries = []; - for ($i = 0, $total = count($this->files['name']); $i < $total; $i++) { - if (!$this->files['size'][$i]) { - continue; - } - $logfile = new Logfile( - $this->files['tmp_name'][$i], - $this->files['name'][$i] - ); - $logfiles[] = $logfile; - $logfileSummary->add($logfile); - - self::$db->prepared_query(' - INSERT INTO torrents_logs - (TorrentID, Score, `Checksum`, FileName, Ripper, RipperVersion, `Language`, ChecksumState, LogcheckerVersion, Details) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - ', $torrentId, $logfile->score(), $logfile->checksumStatus(), $logfile->filename(), $logfile->ripper(), - $logfile->ripperVersion(), $logfile->language(), $logfile->checksumState(), - Logchecker::getLogcheckerVersion(), $logfile->detailsAsString() - ); - $logId = self::$db->inserted_id(); - $ripFiler->put($logfile->filepath(), [$torrentId, $logId]); - $htmlFiler->put($logfile->text(), [$torrentId, $logId]); - + $checkerVersion = Logchecker::getLogcheckerVersion(); + foreach($this->logfileSummary->all() as $logfile) { + $this->torrentLogManager->create($this->torrent, $logfile, $checkerVersion); $logSummaries[] = [ 'score' => $logfile->score(), 'checksum' => $logfile->checksumState(), 'ripper' => $logfile->ripper(), 'ripperVersion' => $logfile->ripperVersion(), 'language' => $logfile->language(), - 'details' => $logfile->detailsAsString() + 'details' => $logfile->detailsAsString(), ]; } - $this->torrent->updateLogScore($logfileSummary); - - [$score, $checksum] = self::$db->row(" - SELECT min(CASE WHEN Adjusted = '1' THEN AdjustedScore ELSE Score END) AS Score, - min(CASE WHEN Adjusted = '1' THEN AdjustedChecksum ELSE Checksum END) AS Checksum - FROM torrents_logs - WHERE TorrentID = ? - GROUP BY TorrentID - ", $torrentId - ); - - self::$db->prepared_query( - 'UPDATE torrents SET LogScore = ?, LogChecksum = ?, HasLogDB = ? WHERE ID = ?', - $score, $checksum, '1', $torrentId - ); - - $groupId = $this->torrent->groupId(); - self::$cache->delete_multi([ - "torrent_group_{$groupId}", - "torrents_details_{$groupId}", - sprintf(\Gazelle\TGroup::CACHE_KEY, $groupId), - sprintf(\Gazelle\TGroup::CACHE_TLIST_KEY, $groupId), - ]); + $this->torrent->modifyLogscore(); + $this->torrent->flush(); return [ - 'torrentId' => $torrentId, - 'score' => $score, - 'checksum' => $checksum, - 'logcheckerVersion' => Logchecker::getLogcheckerVersion(), - 'logSummaries' => $logSummaries + 'torrentId' => $this->torrent->id(), + 'score' => $this->torrent->logScore(), + 'checksum' => $this->torrent->logChecksum(), + 'logcheckerVersion' => $checkerVersion, + 'logSummaries' => $logSummaries, ]; } } diff --git a/app/Logfile.php b/app/Logfile.php index 9bce16b3c..42e86329a 100644 --- a/app/Logfile.php +++ b/app/Logfile.php @@ -22,13 +22,13 @@ class Logfile { $checker = new Logchecker(); $checker->newFile($this->filepath); $checker->parse(); - $this->score = max(0, $checker->getScore()); - $this->details = $checker->getDetails(); + $this->score = max(0, $checker->getScore()); + $this->details = $checker->getDetails(); $this->checksumState = $checker->getChecksumState(); - $this->text = $checker->getLog(); - $this->ripper = $checker->getRipper() ?? ''; + $this->text = $checker->getLog(); + $this->ripper = $checker->getRipper() ?? ''; $this->ripperVersion = $checker->getRipperVersion() ?? ''; - $this->language = $checker->getLanguage(); + $this->language = $checker->getLanguage(); } public function checksum() { return $this->checksumState === Checksum::CHECKSUM_OK; } diff --git a/app/LogfileSummary.php b/app/LogfileSummary.php index 5cf6d905d..12a633d94 100644 --- a/app/LogfileSummary.php +++ b/app/LogfileSummary.php @@ -3,42 +3,39 @@ namespace Gazelle; class LogfileSummary { - /** @var Logfile[] */ - protected $list; - protected $allChecksum; - protected $lowestScore; + protected array $list; + protected bool $allChecksum = true; + protected int $lowestScore = 100; - public function __construct() { + public function __construct(array $fileList = []) { $this->list = []; + for ($n = 0, $end = count($fileList['error']); $n < $end; ++$n) { + if ($fileList['error'][$n] == UPLOAD_ERR_OK) { + $log = new Logfile($fileList['tmp_name'][$n], $fileList['name'][$n]); + $this->allChecksum = $this->allChecksum && $log->checksum(); + $this->lowestScore = min($this->lowestScore, $log->score()); + $this->list[] = $log; + } + } } - public function add(Logfile $log) { - $this->list[] = $log; - $this->allChecksum = is_null($this->allChecksum) - ? $log->checksum() - : $this->allChecksum && $log->checksum(); - $this->lowestScore = is_null($this->lowestScore) - ? $log->score() - : min($this->lowestScore, $log->score()); - } - - public function checksum() { + public function checksum(): bool { return $this->allChecksum; } - public function checksumStatus() { + public function checksumStatus(): string { return $this->allChecksum ? '1' : '0'; } - public function overallScore() { + public function overallScore(): int { return is_null($this->lowestScore) ? 0 : $this->lowestScore; } - public function all() { + public function all(): array { return $this->list; } - public function count() { + public function total(): int { return count($this->list); } } diff --git a/app/Manager/Torrent.php b/app/Manager/Torrent.php index d47ed5f30..7929e6ff3 100644 --- a/app/Manager/Torrent.php +++ b/app/Manager/Torrent.php @@ -99,27 +99,27 @@ class Torrent extends \Gazelle\BaseManager { } public function create( - int $tgroupId, - int $userId, - string $description, - string $media, + int $tgroupId, + int $userId, + string $description, + string $media, ?string $format, ?string $encoding, - string $infohash, - string $filePath, - array $fileList, - int $size, - bool $isScene, - bool $isRemaster, - ?int $remasterYear, - string $remasterTitle, - string $remasterRecordLabel, - string $remasterCatalogueNumber, - int $logScore = 0, - bool $hasChecksum = false, - bool $hasCue = false, - bool $hasLog = false, - bool $hasLogInDB = false, + string $infohash, + string $filePath, + array $fileList, + int $size, + bool $isScene, + bool $isRemaster, + ?int $remasterYear, + string $remasterTitle, + string $remasterRecordLabel, + string $remasterCatalogueNumber, + int $logScore = 0, + bool $hasChecksum = false, + bool $hasCue = false, + bool $hasLog = false, + bool $hasLogInDB = false, ): \Gazelle\Torrent { self::$db->prepared_query(" INSERT INTO torrents ( diff --git a/app/Manager/TorrentLog.php b/app/Manager/TorrentLog.php index 82eff0099..20ae18061 100644 --- a/app/Manager/TorrentLog.php +++ b/app/Manager/TorrentLog.php @@ -3,9 +3,28 @@ namespace Gazelle\Manager; class TorrentLog extends \Gazelle\Base { + public function __construct( + protected \Gazelle\File\RipLog $ripFiler, + protected \Gazelle\File\RipLogHTML $htmlFiler, + ) {} + + public function create(\Gazelle\Torrent $torrent, \Gazelle\Logfile $logfile, string $checkerVersion): \Gazelle\TorrentLog { + self::$db->prepared_query(' + INSERT INTO torrents_logs + (TorrentID, Score, `Checksum`, FileName, Ripper, RipperVersion, `Language`, ChecksumState, Details, LogcheckerVersion) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ', $torrent->id(), $logfile->score(), $logfile->checksumStatus(), $logfile->filename(), + $logfile->ripper(), $logfile->ripperVersion(), $logfile->language(), $logfile->checksumState(), $logfile->detailsAsString(), + $checkerVersion + ); + $logId = self::$db->inserted_id(); + $this->ripFiler->put($logfile->filepath(), [$torrent->id(), $logId]); + $this->htmlFiler->put($logfile->text(), [$torrent->id(), $logId]); + return $this->findById($torrent, $logId); + } public function findById(\Gazelle\Torrent $torrent, int $logId): ?\Gazelle\TorrentLog { - $id = self::$db->scalar(" + $id = (int)self::$db->scalar(" SELECT LogID FROM torrents_logs WHERE TorrentID = ? AND LogID = ? ", $torrent->id(), $logId ); diff --git a/app/Torrent.php b/app/Torrent.php index 0cbf90454..4793448b7 100644 --- a/app/Torrent.php +++ b/app/Torrent.php @@ -220,9 +220,9 @@ class Torrent extends TorrentAbstract { if (!$count) { self::$db->prepared_query(" UPDATE torrents SET - HasLogDB = '0', - LogChecksum = '1', - LogScore = 0 + HasLogDB = '0', + LogChecksum = '0', + LogScore = 0 WHERE ID = ? ", $this->id ); @@ -231,21 +231,23 @@ class Torrent extends TorrentAbstract { UPDATE torrents AS t LEFT JOIN ( SELECT TorrentID, - min(CASE WHEN Adjusted = '1' OR AdjustedScore != Score THEN AdjustedScore ELSE Score END) AS Score, - min(CASE WHEN Adjusted = '1' OR AdjustedChecksum != Checksum THEN AdjustedChecksum ELSE Checksum END) AS Checksum + min(CASE WHEN Adjusted = '1' AND AdjustedScore != Score THEN AdjustedScore ELSE Score END) AS Score, + min(CASE WHEN Adjusted = '1' AND AdjustedChecksum != Checksum THEN AdjustedChecksum ELSE Checksum END) AS Checksum FROM torrents_logs WHERE TorrentID = ? GROUP BY TorrentID ) AS tl ON (t.ID = tl.TorrentID) SET + t.HasLogDB = '1', t.LogScore = tl.Score, t.LogChecksum = tl.Checksum WHERE t.ID = ? ", $this->id, $this->id ); } + $affected = self::$db->affected_rows(); $this->flush(); - return self::$db->affected_rows(); + return $affected; } public function rescoreLog(int $logId, \Gazelle\Logfile $logfile, string $version): int { @@ -265,21 +267,6 @@ class Torrent extends TorrentAbstract { return 0; } - public function updateLogScore(LogfileSummary $summary): int { - self::$db->prepared_query(" - UPDATE torrents SET - HasLogDB = '1', - LogScore = ?, - LogChecksum = ? - WHERE ID = ? - ", $summary->overallScore(), $summary->checksumStatus(), - $this->id - ); - $this->flush(); - self::$cache->delete_value(sprintf(TGroup::CACHE_TLIST_KEY, $this->groupId())); - return self::$db->affected_rows(); - } - /** * Remove all logfiles attached to this upload * diff --git a/app/TorrentAbstract.php b/app/TorrentAbstract.php index e3c376d94..c766cd407 100644 --- a/app/TorrentAbstract.php +++ b/app/TorrentAbstract.php @@ -10,7 +10,10 @@ abstract class TorrentAbstract extends BaseObject { protected User $viewer; public function flush(): TorrentAbstract { - self::$cache->delete_value(sprintf(self::CACHE_KEY, $this->id)); + self::$cache->delete_multi([ + sprintf(self::CACHE_KEY, $this->id), + "torrent_download_{$this->id}", + ]); $this->group()->flush(); return $this; } diff --git a/cypress.config.js b/cypress.config.js index 22a11440a..fcca66244 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -11,6 +11,9 @@ module.exports = defineConfig({ supportFile: 'tests/cypress/support/e2e.{js,jsx,ts,tsx}', specPattern: 'tests/cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', video: false, - screenshotOnRunFailure: false + screenshotOnRunFailure: false, + setupNodeEvents(on, config) { + require('cypress-terminal-report/src/installLogsPrinter')(on); + } }, }); diff --git a/package.json b/package.json index 240f97630..933e5f854 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "browser-sync": "^2.27.5", "browser-sync-webpack-plugin": "^2.2.2", "cypress": "^12.0.2", + "cypress-terminal-report": "^5.0.2", "husky": "^8.0.1", "lint-staged": "^10.5.0", "stylelint": "^13.3.2", diff --git a/sections/ajax/add_log.php b/sections/ajax/add_log.php index 2d4a00abb..3a2a0c0ba 100644 --- a/sections/ajax/add_log.php +++ b/sections/ajax/add_log.php @@ -4,10 +4,18 @@ $torrent = (new Gazelle\Manager\Torrent)->findById((int)($_GET['id'] ?? 0)); if (is_null($torrent)) { json_error('bad parameters'); } +if ($torrent->uploaderId() != $Viewer->id() && !$Viewer->permitted('admin_add_log')) { + json_error('Not your upload.'); +} if (empty($_FILES) || empty($_FILES['logfiles'])) { json_error('no log files uploaded'); } -(new Gazelle\Json\AddLog($torrent, $Viewer, $_FILES['logfiles'])) +(new Gazelle\Json\AddLog( + $torrent, + $Viewer, + new Gazelle\Manager\TorrentLog(new Gazelle\File\RipLog, new Gazelle\File\RipLogHTML), + new Gazelle\LogfileSummary($_FILES['logfiles']), +)) ->setVersion(1) ->emit(); diff --git a/sections/ajax/logchecker.php b/sections/ajax/logchecker.php index d68c91551..ef242d943 100644 --- a/sections/ajax/logchecker.php +++ b/sections/ajax/logchecker.php @@ -1,12 +1,15 @@ $fileTmp, 'name' => $fileTmp]; + $file = [ + 'tmp_name' => $fileTmp, + 'name' => $fileTmp + ]; $isPaste = true; } else { json_error('no log file provided'); @@ -17,13 +20,11 @@ if (isset($fileTmp)) { unlink($fileTmp); } -$response = [ - 'ripper' => $logfile->ripper(), +json_print('success', [ + 'ripper' => $logfile->ripper(), 'ripperVersion' => $logfile->ripperVersion(), - 'language' => $logfile->language(), - 'score' => $logfile->score(), - 'checksum' => $logfile->checksumState(), - 'issues' => $logfile->details(), -]; - -json_print('success', $response); + 'language' => $logfile->language(), + 'score' => $logfile->score(), + 'checksum' => $logfile->checksumState(), + 'issues' => $logfile->details(), +]); diff --git a/sections/logchecker/take_test.php b/sections/logchecker/take_test.php index a1e64ecfc..92682ffda 100644 --- a/sections/logchecker/take_test.php +++ b/sections/logchecker/take_test.php @@ -1,12 +1,15 @@ $fileTmp, 'name' => $fileTmp]; + $file = [ + 'tmp_name' => $fileTmp, + 'name' => $fileTmp + ]; $isPaste = true; } else { error('No log file uploaded or file is empty.'); diff --git a/sections/logchecker/take_upload.php b/sections/logchecker/take_upload.php index 8ddaec275..e294ba611 100644 --- a/sections/logchecker/take_upload.php +++ b/sections/logchecker/take_upload.php @@ -15,37 +15,26 @@ if ($torrent->uploaderId() != $Viewer->id() && !$Viewer->permitted('admin_add_lo error('Not your upload.'); } -// Some browsers will report an empty file when you submit, prune those out -$_FILES['logfiles']['name'] = array_filter($_FILES['logfiles']['name'], fn($Name) => !empty($Name)); -if (count($_FILES['logfiles']['name']) == 0) { - error("No logfiles uploaded.\n"); -} $action = in_array($_POST['from_action'], ['upload', 'update']) ? $_POST['from_action'] : 'upload'; +$logfileSummary = new Gazelle\LogfileSummary($_FILES['logfiles']); -$ripFiler = new Gazelle\File\RipLog; -$ripFiler->remove([$torrent->id(), null]); +if (!$logfileSummary->total()) { + error("No logfiles uploaded."); +} else { + $ripFiler = new Gazelle\File\RipLog; + $htmlFiler = new Gazelle\File\RipLogHTML; -$htmlFiler = new Gazelle\File\RipLogHTML; -$htmlFiler->remove([$torrent->id(), null]); + $torrent->removeLogDb(); + $ripFiler->remove([$torrent->id(), null]); + $htmlFiler->remove([$torrent->id(), null]); + $torrentLogManager = new Gazelle\Manager\TorrentLog($ripFiler, $htmlFiler); -$torrent->removeLogDb(); - -$logfileSummary = new Gazelle\LogfileSummary; -foreach ($_FILES['logfiles']['name'] as $Pos => $File) { - if (!$_FILES['logfiles']['size'][$Pos]) { - break; + $checkerVersion = Logchecker::getLogcheckerVersion(); + foreach($logfileSummary->all() as $logfile) { + $torrentLogManager->create($torrent, $logfile, $checkerVersion); } - $logfile = new Gazelle\Logfile( - $_FILES['logfiles']['tmp_name'][$Pos], - $_FILES['logfiles']['name'][$Pos] - ); - $logfileSummary->add($logfile); - $logId = $torrent->addLogDb($logfile, Logchecker::getLogcheckerVersion()); - - $ripFiler->put($logfile->filepath(), [$torrent->id(), $logId]); - $htmlFiler->put($logfile->text(), [$torrent->id(), $logId]); + $torrent->modifyLogscore(); } -$torrent->updateLogScore($logfileSummary); echo $Twig->render('logchecker/result.twig', [ 'summary' => $logfileSummary->all(), diff --git a/sections/torrents/delete_log.php b/sections/torrents/delete_log.php index 79768def9..0bdbf0584 100644 --- a/sections/torrents/delete_log.php +++ b/sections/torrents/delete_log.php @@ -1,5 +1,6 @@ permitted('users_mod')) { error(403); } diff --git a/sections/torrents/edit_log.php b/sections/torrents/edit_log.php index 8cf95bca6..17ba7b2b2 100644 --- a/sections/torrents/edit_log.php +++ b/sections/torrents/edit_log.php @@ -8,7 +8,7 @@ $torrent = (new Gazelle\Manager\Torrent)->findById((int)($_GET['torrentid'] ?? 0 if (is_null($torrent)) { error(404); } -$tlog = (new Gazelle\Manager\TorrentLog)->findById($torrent, (int)($_GET['logid'] ?? 0)); +$tlog = (new Gazelle\Manager\TorrentLog(new Gazelle\File\RipLog, new Gazelle\File\RipLogHTML))->findById($torrent, (int)($_GET['logid'] ?? 0)); if (is_null($tlog)) { error(404); } diff --git a/sections/torrents/remove_logs.php b/sections/torrents/remove_logs.php index 650c4e72e..7ecaedd75 100644 --- a/sections/torrents/remove_logs.php +++ b/sections/torrents/remove_logs.php @@ -1,5 +1,6 @@ permitted('torrents_delete')) { error(403); } diff --git a/sections/torrents/takeedit.php b/sections/torrents/takeedit.php index 902f0c2f5..25c7f4c0e 100644 --- a/sections/torrents/takeedit.php +++ b/sections/torrents/takeedit.php @@ -47,16 +47,16 @@ if (isset($_POST['album_desc'])) { $Properties['GroupDescription'] = trim($_POST['album_desc']); } if ($Properties['Remastered']) { - $Properties['UnknownRelease'] = isset($_POST['unknown']); - $Properties['RemasterYear'] = isset($_POST['remaster_year']) ? (int)$_POST['remaster_year'] : null; - $Properties['RemasterTitle'] = trim($_POST['remaster_title'] ?? ''); - $Properties['RemasterRecordLabel'] = trim($_POST['remaster_record_label'] ?? ''); + $Properties['UnknownRelease'] = isset($_POST['unknown']); + $Properties['RemasterYear'] = isset($_POST['remaster_year']) ? (int)$_POST['remaster_year'] : null; + $Properties['RemasterTitle'] = trim($_POST['remaster_title'] ?? ''); + $Properties['RemasterRecordLabel'] = trim($_POST['remaster_record_label'] ?? ''); $Properties['RemasterCatalogueNumber'] = trim($_POST['remaster_catalogue_number'] ?? ''); } else { - $Properties['UnknownRelease'] = false; - $Properties['RemasterYear'] = null; - $Properties['RemasterTitle'] = ''; - $Properties['RemasterRecordLabel'] = ''; + $Properties['UnknownRelease'] = false; + $Properties['RemasterYear'] = null; + $Properties['RemasterTitle'] = ''; + $Properties['RemasterRecordLabel'] = ''; $Properties['RemasterCatalogueNumber'] = ''; } @@ -199,42 +199,20 @@ foreach ($current as $key => $value) { } } +$logfileSummary = new Gazelle\LogfileSummary($_FILES['logfiles']); + +//******************************************************************************// +//--------------- Start database stuff -----------------------------------------// + $db->begin_transaction(); // It's all or nothing -// Some browsers will report an empty file when you submit, prune those out -if (isset($_FILES['logfiles'])) { - $_FILES['logfiles']['name'] = array_filter($_FILES['logfiles']['name'], fn($name) => !empty($name)); - - $logfileSummary = new Gazelle\LogfileSummary; - $logfiles = []; - if (count($_FILES['logfiles']['name']) > 0) { - ini_set('upload_max_filesize', 1_000_000); - $ripFiler = new Gazelle\File\RipLog; - $htmlFiler = new Gazelle\File\RipLogHTML; - foreach ($_FILES['logfiles']['name'] as $Pos => $File) { - if (!$_FILES['logfiles']['size'][$Pos]) { - continue; - } - $logfile = new Gazelle\Logfile( - $_FILES['logfiles']['tmp_name'][$Pos], - $_FILES['logfiles']['name'][$Pos] - ); - $logfileSummary->add($logfile); - - $db->prepared_query(' - INSERT INTO torrents_logs - (TorrentID, Score, `Checksum`, FileName, Ripper, RipperVersion, `Language`, ChecksumState, LogcheckerVersion, Details) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - ', $TorrentID, $logfile->score(), $logfile->checksumStatus(), $logfile->filename(), $logfile->ripper(), - $logfile->ripperVersion(), $logfile->language(), $logfile->checksumState(), - Logchecker::getLogcheckerVersion(), $logfile->detailsAsString() - ); - $LogID = $db->inserted_id(); - $ripFiler->put($logfile->filepath(), [$TorrentID, $LogID]); - $htmlFiler->put($logfile->text(), [$TorrentID, $LogID]); - } - $torrent->updateLogScore($logfileSummary); +if ($logfileSummary->total()) { + $torrentLogManager = new Gazelle\Manager\TorrentLog(new Gazelle\File\RipLog, new Gazelle\File\RipLogHTML); + $checkerVersion = Logchecker::getLogcheckerVersion(); + foreach($logfileSummary->all() as $logfile) { + $torrentLogManager->create($torrent, $logfile, $checkerVersion); } + $torrent->modifyLogscore(); } // Update info for the torrent @@ -303,5 +281,4 @@ $changeLog = implode(', ', $change); . $Viewer->username() . " ($changeLog)"); $torrent->flush(); -$Cache->delete_value("torrent_download_$TorrentID"); header("Location: " . $torrent->location()); diff --git a/sections/upload/upload_handle.php b/sections/upload/upload_handle.php index 716e68bb5..b9899a1a9 100644 --- a/sections/upload/upload_handle.php +++ b/sections/upload/upload_handle.php @@ -24,18 +24,18 @@ $Properties['Title'] = isset($_POST['title']) ? trim($_POST['title']) : null; // Remastered is an Enum in the DB $Properties['Remastered'] = !empty($_POST['remaster']) ? '1' : '0'; if ($Properties['Remastered'] || !empty($_POST['unknown'])) { - $Properties['UnknownRelease'] = !empty($_POST['unknown']) ? 1 : 0; - $Properties['RemasterYear'] = isset($_POST['remaster_year']) ? (int)$_POST['remaster_year'] : null; - $_POST['remaster_year'] = $Properties['RemasterYear']; - $Properties['RemasterTitle'] = trim($_POST['remaster_title'] ?? ''); - $Properties['RemasterRecordLabel'] = trim($_POST['remaster_record_label'] ?? ''); + $Properties['UnknownRelease'] = !empty($_POST['unknown']) ? 1 : 0; + $Properties['RemasterYear'] = isset($_POST['remaster_year']) ? (int)$_POST['remaster_year'] : null; + $_POST['remaster_year'] = $Properties['RemasterYear']; + $Properties['RemasterTitle'] = trim($_POST['remaster_title'] ?? ''); + $Properties['RemasterRecordLabel'] = trim($_POST['remaster_record_label'] ?? ''); $Properties['RemasterCatalogueNumber'] = trim($_POST['remaster_catalogue_number'] ?? ''); } if (!$Properties['Remastered'] || $Properties['UnknownRelease']) { - $Properties['UnknownRelease'] = 1; - $Properties['RemasterYear'] = null; - $Properties['RemasterTitle'] = ''; - $Properties['RemasterRecordLabel'] = ''; + $Properties['UnknownRelease'] = 1; + $Properties['RemasterYear'] = null; + $Properties['RemasterTitle'] = ''; + $Properties['RemasterRecordLabel'] = ''; $Properties['RemasterCatalogueNumber'] = ''; } $Properties['Year'] = isset($_POST['year']) ? (int)$_POST['year'] : null; @@ -472,24 +472,6 @@ if ($Err) { } } -//******************************************************************************// -//--------------- Start database stuff -----------------------------------------// - -$logfileSummary = new Gazelle\LogfileSummary; -if ($HasLog == '1' && isset($_FILES['logfiles'])) { - foreach (array_keys($_FILES['logfiles']['error']) as $n) { - if ($_FILES['logfiles']['error'][$n] == UPLOAD_ERR_OK) { - $logfileSummary->add( - new Gazelle\Logfile( - $_FILES['logfiles']['tmp_name'][$n], - $_FILES['logfiles']['name'][$n] - ) - ); - } - } -} -$LogInDB = count($logfileSummary->all()) ? '1' : '0'; - $tgroup = null; $NoRevision = false; if ($isMusicUpload) { @@ -527,7 +509,11 @@ $IsNewGroup = is_null($tgroup); //Needs to be here as it isn't set for add format until now $LogName .= $Properties['Title']; -//----- Start inserts +$logfileSummary = new Gazelle\LogfileSummary($_FILES['logfiles']); +$LogInDB = $logfileSummary->total() ? '1' : '0'; + +//******************************************************************************// +//--------------- Start database stuff -----------------------------------------// $Debug->set_flag('upload: database begin transaction'); $db = Gazelle\DB::DB(); @@ -613,58 +599,45 @@ $extraFile = []; $trackerUpdate = []; foreach ($ExtraTorrentsInsert as $ExtraTorrent) { - $db->prepared_query(" - INSERT INTO torrents - (GroupID, UserID, Media, Format, Encoding, - Remastered, RemasterYear, RemasterTitle, RemasterRecordLabel, RemasterCatalogueNumber, - info_hash, FileCount, FileList, FilePath, Size, Description, - Time, LogScore, HasLog, HasCue, FreeTorrent, FreeLeechType) - VALUES - (?, ?, ?, ?, ?, - ?, ?, ?, ?, ?, - ?, ?, ?, ?, ?, ?, - now(), 0, '0', '0', '0', '0') - ", $GroupID, $Viewer->id(), $Properties['Media'], $ExtraTorrent['Format'], $ExtraTorrent['Encoding'], - $Properties['Remastered'], $Properties['RemasterYear'], $Properties['RemasterTitle'], $Properties['RemasterRecordLabel'], $Properties['RemasterCatalogueNumber'], - $ExtraTorrent['InfoHash'], $ExtraTorrent['NumFiles'], $ExtraTorrent['FileString'], - $ExtraTorrent['FilePath'], $ExtraTorrent['TotalSize'], $ExtraTorrent['TorrentDescription'] + $torrentExtra = $torMan->create( + tgroupId: $GroupID, + userId: $Viewer->id(), + description: $ExtraTorrent['TorrentDescription'], + media: $Properties['Media'], + format: $ExtraTorrent['Format'], + encoding: $ExtraTorrent['Encoding'], + infohash: $ExtraTorrent['InfoHash'], + filePath: $ExtraTorrent['FilePath'], + fileList: $ExtraTorrent['FileString'], + size: $ExtraTorrent['TotalSize'], + isScene: $Properties['Scene'], + isRemaster: $Properties['Remastered'], + remasterYear: $Properties['RemasterYear'], + remasterTitle: $Properties['RemasterTitle'], + remasterRecordLabel: $Properties['RemasterRecordLabel'], + remasterCatalogueNumber: $Properties['RemasterCatalogueNumber'], ); - $ExtraTorrentID = $db->inserted_id(); - $db->prepared_query(' - INSERT INTO torrents_leech_stats (TorrentID) - VALUES (?) - ', $ExtraTorrentID - ); - $torrentExtra = new Gazelle\Torrent($ExtraTorrentID); - $torMan->flushFoldernameCache($ExtraTorrent['FilePath']); - $folderCheck[] = $ExtraTorrent['FilePath']; - $trackerUpdate[$ExtraTorrentID] = rawurlencode($ExtraTorrent['InfoHash']); + $torMan->flushFoldernameCache($torrentExtra->path()); + $folderCheck[] = $torrentExtra->path(); + $trackerUpdate[$torrentExtra->id()] = rawurlencode($torrentExtra->infohash()); $bonusTotal += $bonus->torrentValue($torrentExtra); - $extraFile[$ExtraTorrentID] = [ + $extraFile[$torrentExtra->id()] = [ 'payload' => $ExtraTorrent['TorEnc'], - 'size' => number_format($ExtraTorrent['TotalSize'] / (1024 * 1024), 2) + 'size' => number_format($torrentExtra->size() / (1024 * 1024), 2) ]; } //******************************************************************************// //--------------- Write Files To Disk ------------------------------------------// -$ripFiler = new Gazelle\File\RipLog; -$htmlFiler = new Gazelle\File\RipLogHTML; -foreach($logfileSummary->all() as $logfile) { - $db->prepared_query(' - INSERT INTO torrents_logs - (TorrentID, Score, `Checksum`, FileName, Ripper, RipperVersion, `Language`, ChecksumState, LogcheckerVersion, Details) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - ', $TorrentID, $logfile->score(), $logfile->checksumStatus(), $logfile->filename(), - $logfile->ripper(), $logfile->ripperVersion(), $logfile->language(), $logfile->checksumState(), - Logchecker::getLogcheckerVersion(), $logfile->detailsAsString() - ); - $LogID = $db->inserted_id(); - $ripFiler->put($logfile->filepath(), [$TorrentID, $LogID]); - $htmlFiler->put($logfile->text(), [$TorrentID, $LogID]); +if ($logfileSummary->total()) { + $torrentLogManager = new Gazelle\Manager\TorrentLog(new Gazelle\File\RipLog, new Gazelle\File\RipLogHTML); + $checkerVersion = Logchecker::getLogcheckerVersion(); + foreach($logfileSummary->all() as $logfile) { + $torrentLogManager->create($torrent, $logfile, $checkerVersion); + } } $log = new Gazelle\Log; @@ -675,7 +648,7 @@ $log->torrent($GroupID, $TorrentID, $Viewer->id(), 'uploaded ('.number_format($T foreach ($extraFile as $id => $info) { $torrentFiler->put($info['payload'], $id); $log->torrent($GroupID, $id, $Viewer->id(), "uploaded ({$info['size']} MiB)") - ->general("Torrent $ExtraTorrentID ($LogName) ({$info['size']} MiB) was uploaded by " . $Viewer->username()); + ->general("Torrent $id ($LogName) ({$info['size']} MiB) was uploaded by " . $Viewer->username()); } $db->commit(); // We have a usable upload, any subsequent failures can be repaired ex post facto diff --git a/templates/torrent/riplog.twig b/templates/torrent/riplog.twig index f020c99b8..03190d167 100644 --- a/templates/torrent/riplog.twig +++ b/templates/torrent/riplog.twig @@ -16,7 +16,7 @@ {% if viewer.permitted('users_mod') %}
Edit Log Delete Log + href="torrents.php?action=deletelog&torrentid={{ id }}&logid={{ log.id }}&auth={{ viewer.auth }}">Delete Log
{% endif %} @@ -71,6 +71,6 @@ {% else %} No logs found! {% if viewer.permitted('torrents_delete') %} - Repair DBRepair DB {% endif %} {% endfor %} diff --git a/tests/cypress/e2e/1-simple/basic.cy.js b/tests/cypress/e2e/1-simple/basic.cy.js index 2b241775a..c36353d8e 100644 --- a/tests/cypress/e2e/1-simple/basic.cy.js +++ b/tests/cypress/e2e/1-simple/basic.cy.js @@ -31,7 +31,6 @@ describe('page loads as admin', () => { "/top10.php", "/torrents.php", "/torrents.php?action=advanced&artistname=doesnotexist", - "/upload.php", "/user.php", "/user.php?id=1", "/user.php?action=edit&id=1", @@ -48,7 +47,7 @@ describe('page loads as admin', () => { }) it(`should have a footer: ${url}`, () => { cy.visit(url); - cy.contains(`Site and design © ${date.getFullYear()} Gazelle`); + cy.ensureFooter(); }) }) }) diff --git a/tests/cypress/e2e/1-simple/upload.cy.js b/tests/cypress/e2e/1-simple/upload.cy.js index c40a925f2..badb9a671 100644 --- a/tests/cypress/e2e/1-simple/upload.cy.js +++ b/tests/cypress/e2e/1-simple/upload.cy.js @@ -1,13 +1,11 @@ describe('uploading torrent', () => { - let date = new Date(); - let footer = `Site and design © ${date.getFullYear()} Gazelle`; - beforeEach(() => { cy.loginUser(); }) it('upload music torrent', () => { cy.visit('/upload.php'); + cy.ensureFooter(); cy.get('#file').selectFile('tests/cypress/files/valid_torrent.torrent') cy.get("#categories").select('Music'); cy.get("#releasetype").select('Album'); @@ -41,11 +39,12 @@ describe('uploading torrent', () => { cy.contains('Some Artist'); cy.contains('2022'); cy.contains('Cool Test Label!'); - cy.contains(footer); + cy.ensureFooter(); }) it('upload torrent to existing group', () => { cy.visit('/upload.php'); + cy.ensureFooter(); cy.get('#file').selectFile('tests/cypress/files/valid_torrent_2.torrent') cy.get("#categories").select('Music'); cy.get("#releasetype").select('Album'); @@ -76,6 +75,82 @@ describe('uploading torrent', () => { cy.contains('2022'); cy.contains('Cool Test Label!'); cy.contains('[CD / FLAC / Lossless / Log (100%)]'); - cy.contains(footer); + cy.ensureFooter(); + }) + + it('re-attach log to upload', () => { + // find torrent id + cy.visit('/torrents.php?type=uploaded'); + cy.ensureFooter(); + + // bunch of callbacks have been flattened below + cy.get('.group_info').contains('Log (100%)').first() + .find('a[href*="torrents.php?"][href*="torrentid="]').first() + .invoke('attr', 'href').then((torrent_url) => { + let torrent_id = torrent_url.match(/[&?]torrentid=([0-9]+)/)[1]; + cy.visit(torrent_url); + cy.ensureFooter(); + cy.get(`#torrent_${torrent_id}`).contains('View log').click(); + cy.get(`a[href*="view.php?type=riplog&id=${torrent_id}."]`).first() + .invoke('attr', 'href').then((log_url) => { + + let log_id = log_url.match(/&id=[0-9]+\.([0-9]+)/)[1]; + + // delete existing log + cy.loginAdmin(); + cy.visit('/'); + cy.window() + .then((window) => { + cy.window().should('have.property', 'authkey') + cy.visit('/torrents.php', {qs: { + action: 'deletelog', + torrentid: torrent_id, + logid: log_id, + auth: window.authkey + }}) + }); + + cy.loginUser(); + // verify log was correctly removed + cy.visit(torrent_url); + cy.contains('Log (100%)').should('not.exist'); + cy.ensureFooter(); + // set up api token + cy.visit('/user.php?action=token&do=generate', + {method: 'POST', body: {token_name: 'test_reattach_log'}}); + cy.ensureFooter(); + + // upload log + cy.get('.box2 > .pad > strong').invoke('text').then((api_key) => { + cy.fixture('../files/valid_log_eac.log', 'binary').then( (log_bin) => { + // File in binary format gets converted to blob so it can be sent as Form data + const blob = Cypress.Blob.binaryStringToBlob(log_bin, 'application/octet-stream'); + const formData = new FormData(); + formData.append('logfiles[]', blob, 'eac.log'); + cy.request({ + url: '/ajax.php', + qs: {action: 'add_log', id: torrent_id}, + method: 'POST', + headers: {authorization: `token ${api_key}`, + 'content-type': 'multipart/form-data'}, + body: formData + }).then( (response) => { + // it's an ArrayBuffer but for some reason instanceof ArrayBuffer is false + // and its stringifyed version is "{}" + let body = typeof response.body === 'object' ? + new TextDecoder().decode(response.body) : response.body; + try { + body = JSON.parse(body); + } catch (e) { + cy.logCli("bad response body: " + body); + } + expect(body).to.have.property('status', 'success'); + }); + }) + // verify + cy.visit(torrent_url); + cy.ensureFooter(); + cy.contains('Log (100%)'); + })})}); }) }) diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 624af1eba..7eb145b35 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -24,6 +24,9 @@ // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +const date = new Date(); +const footer = `Site and design © ${date.getFullYear()} Gazelle`; + Cypress.Commands.add('login', (username, password) => { // https://www.cypress.io/blog/2021/08/04/authenticate-faster-in-tests-cy-session-command/ cy.session([username, password], () => { @@ -42,3 +45,15 @@ Cypress.Commands.add('loginAdmin', () => { Cypress.Commands.add('loginUser', () => { cy.login('user', 'password'); }) + +Cypress.Commands.add('ensureFooter', () => { + cy.contains(footer); +}) + +Cypress.Commands.add('logCli', (msg) => { + // somehow cypress-terminal-report doesn't pick up cy.log() + Cypress.log({ + name: "logCli", + message: msg + }); +}) diff --git a/tests/cypress/support/e2e.js b/tests/cypress/support/e2e.js index 00e5ff6d1..8caa4a376 100644 --- a/tests/cypress/support/e2e.js +++ b/tests/cypress/support/e2e.js @@ -13,12 +13,14 @@ // https://on.cypress.io/configuration // *********************************************************** -// Import commands.js using ES2015 syntax: +require('cypress-terminal-report/src/installLogsCollector')({ + xhr: { + printHeaderData: true, + printRequestData: true + } +}); import './commands' -// Alternatively you can use CommonJS syntax: -// require('./commands') - Cypress.on('fail', (err, runnable) => { /** * add DOM html to error message diff --git a/yarn.lock b/yarn.lock index f36409eaa..1020e68c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3098,6 +3098,16 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +cypress-terminal-report@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/cypress-terminal-report/-/cypress-terminal-report-5.0.2.tgz#923db46f06c4b32021c48da0177065ea1af2d999" + integrity sha512-YJ6HODTvxKD0FYQX2p3f1DlBRcJcMJjRh3JWZZrBte4dQdnTuIElb4jPh3zbcqsV4MJ+QwF6XyJoXybZgg6kHg== + dependencies: + chalk "^4.0.0" + fs-extra "^10.1.0" + semver "^7.3.5" + tv4 "^1.3.0" + cypress@^12.0.2: version "12.0.2" resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.0.2.tgz#0437abf9d97ad4a972ab554968d58211b0ce4ae5" @@ -4241,6 +4251,15 @@ fs-extra@3.0.1: jsonfile "^3.0.0" universalify "^0.1.0" +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz" @@ -5852,6 +5871,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-dir@^1.0.0: version "1.3.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz" @@ -8124,6 +8150,13 @@ semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@^7.3.5: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + send@0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" @@ -9247,6 +9280,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tv4@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" + integrity sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"