From 57b88f03c17e9b5eab8137f9692843b4b297851f Mon Sep 17 00:00:00 2001 From: Git Date: Thu, 21 Feb 2013 08:00:30 +0000 Subject: [PATCH] Empty commit --- classes/class_bencode.php | 409 +++++++++++++++++++++++++++++ classes/class_misc.php | 11 + classes/class_mysql.php | 15 +- classes/class_torrentsdl.php | 211 +++++++++++++++ classes/class_zip.php | 14 +- classes/script_start.php | 38 +-- sections/artist/artistcomments.sql | 14 - sections/artist/download.php | 146 +++++----- sections/collages/download.php | 126 ++++----- sections/torrents/download.php | 72 ++--- sections/torrents/redownload.php | 99 +++---- 11 files changed, 840 insertions(+), 315 deletions(-) create mode 100644 classes/class_bencode.php create mode 100644 classes/class_torrentsdl.php delete mode 100644 sections/artist/artistcomments.sql diff --git a/classes/class_bencode.php b/classes/class_bencode.php new file mode 100644 index 00000000..aa81fee9 --- /dev/null +++ b/classes/class_bencode.php @@ -0,0 +1,409 @@ +Num = $Val; + } + + public static function make($Val) { + return PHP_INT_SIZE === 4 ? new Int64($Val) : (int)$Val; + } + + public static function get($Val) { + return PHP_INT_SIZE === 4 ? $Val->Num : $Val; + } + + public static function is_int($Val) { + return is_int($Val) || (is_object($Val) && get_class($Val) === 'Int64'); + } +} + +/** + * The encode class is simple and straightforward. The only thing to + * note is that empty dictionaries are represented by boolean trues + */ +class BEnc { + private $DefaultKeys = array( // Get rid of everything except these keys to save some space + 'created by', 'creation date', 'encoding', 'info'); + private $Data; + public $Enc; + + /** + * Encode an arbitrary array (usually one that's just been decoded) + * + * @param array $Arg the thing to encode + * @param mixed $Keys string or array with keys in the input array to encode or true to encode everything + * @return bencoded string representing the content of the input array + */ + public function encode($Arg = false, $Keys = false) { + if ($Arg === false) { + $Data =& $this->Dec; + } else { + $Data =& $Arg; + } + if ($Keys === true) { + $this->Data = $Data; + } else if ($Keys === false) { + $this->Data = array_intersect_key($Data, array_flip($this->DefaultKeys)); + } else if (is_array($Keys)) { + $this->Data = array_intersect_key($Data, array_flip($Keys)); + } else { + $this->Data = isset($Data[$Keys]) ? $Data[$Keys] : false; + } + if (!$this->Data) { + return false; + } + $this->Enc = $this->_benc(); + return $this->Enc; + } + + /** + * Internal encoding function that does the actual job + * + * @return bencoded string + */ + private function _benc() { + if (!is_array($this->Data)) { + if (Int64::is_int($this->Data)) { // Integer + return 'i'.Int64::get($this->Data).'e'; + } + if ($this->Data === true) { // Empty dictionary + return 'de'; + } + return strlen($this->Data).':'.$this->Data; // String + } + if (empty($this->Data) || Int64::is_int(key($this->Data))) { + $IsDict = false; + } else { + $IsDict = true; + ksort($this->Data); // Dictionaries must be sorted + } + $Ret = $IsDict ? 'd' : 'l'; + foreach ($this->Data as $Key => $Value) { + if ($IsDict) { + $Ret .= strlen($Key).':'.$Key; + } + $this->Data = $Value; + $Ret .= $this->_benc(); + } + return $Ret.'e'; + } +} + +/** + * The decode class is simple and straightforward. The only thing to + * note is that empty dictionaries are represented by boolean trues + */ +class BEncDec extends BEnc { + private $Data; + private $Length; + private $Pos = 0; + public $Dec = array(); + public $ExitOnError = true; + const SnipLength = 40; + + /** + * Decode prepararations + * + * @param string $Arg bencoded string or path to bencoded file to decode + * @param bool $IsPath needs to be true if $Arg is a path + * @return decoded data with a suitable structure + */ + function __construct($Arg = false, $IsPath = false) { + if ($Arg === false) { + if (empty($this->Enc)) { + return false; + } + } else { + if ($IsPath === true) { + return $this->bdec_file($Arg); + } + $this->Data = $Arg; + } + return $this->decode(); + } + + /** + * Decodes a bencoded file + * + * @param $Path path to bencoded file to decode + * @return decoded data with a suitable structure + */ + public function bdec_file($Path = false) { + if (empty($Path)) { + return false; + } + if (!$this->Data = @file_get_contents($Path, FILE_BINARY)) { + return $this->error("Error: file '$Path' could not be opened.\n"); + } + return $this->decode(); + } + + /** + * Decodes a string with bencoded data + * + * @param mixed $Arg bencoded data or false to decode the content of $this->Data + * @return decoded data with a suitable structure + */ + public function decode($Arg = false) { + if ($Arg !== false) { + $this->Data = $Arg; + } else if (!$this->Data) { + $this->Data = $this->Enc; + } + if (!$this->Data) { + return false; + } + $this->Length = strlen($this->Data); + $this->Pos = 0; + $this->Dec = $this->_bdec(); + if ($this->Pos < $this->Length) { + // Not really necessary, but if the torrent is invalid, it's better to warn than to silently truncate it + return $this->error(); + } + return $this->Dec; + } + + /** + * Internal decoding function that does the actual job + * + * @return decoded data with a suitable structure + */ + private function _bdec() { + switch ($this->Data[$this->Pos]) { + + case 'i': + $this->Pos++; + $Value = substr($this->Data, $this->Pos, strpos($this->Data, 'e', $this->Pos) - $this->Pos); + if (!ctype_digit($Value) && !($Value[0] == '-' && ctype_digit(substr($Value, 1)))) { + return $this->error(); + } + $this->Pos += strlen($Value) + 1; + return Int64::make($Value); + + case 'l': + $Value = array(); + $this->Pos++; + while ($this->Data[$this->Pos] != 'e') { + if ($this->Pos >= $this->Length) { + return $this->error(); + } + $Value[] = $this->_bdec(); + } + $this->Pos++; + return $Value; + + case 'd': + $Value = array(); + $this->Pos++; + while ($this->Data[$this->Pos] != 'e') { + $Length = substr($this->Data, $this->Pos, strpos($this->Data, ':', $this->Pos) - $this->Pos); + if (!ctype_digit($Length)) { + return $this->error(); + } + $this->Pos += strlen($Length) + $Length + 1; + $Key = substr($this->Data, $this->Pos - $Length, $Length); + if ($this->Pos >= $this->Length) { + return $this->error(); + } + $Value[$Key] = $this->_bdec(); + } + $this->Pos++; + // Use boolean true to keep track of empty dictionaries + return empty($Value) ? true : $Value; + + default: + $Length = substr($this->Data, $this->Pos, strpos($this->Data, ':', $this->Pos) - $this->Pos); + if (!ctype_digit($Length)) { + return $this->error(); // Even if the string is likely to be decoded correctly without this check, it's malformed + } + $this->Pos += strlen($Length) + $Length + 1; + return substr($this->Data, $this->Pos - $Length, $Length); + } + } + + /** + * Convert everything to the correct data types and optionally escape strings + * + * @param bool $Escape whether to escape the textual data + * @param mixed $Data decoded data or false to use the $Dec property + * @return decoded data with more useful data types + */ + public function dump($Escape = true, $Data = false) { + if ($Data === false) { + $Data = $this->Dec; + } + if (Int64::is_int($Data)) { + return Int64::get($Data); + } + if (is_bool($Data)) { + return array(); + } + if (is_array($Data)) { + $Output = array(); + foreach ($Data as $Key => $Val) { + $Output[$Key] = $this->dump($Escape, $Val); + } + return $Output; + } + return $Escape ? htmlentities($Data) : $Data; + } + + /** + * Display an error and halt the operation unless the $ExitOnError property is false + * + * @param string $ErrMsg the error message to display + */ + private function error($ErrMsg = false) { + static $ErrorPos; + if ($this->Pos === $ErrorPos) { + // The recursive nature of the class requires this to avoid duplicate error messages + return false; + } + if ($ErrMsg === false) { + printf("Malformed string. Invalid character at pos 0x%X: %s\n", + $this->Pos, str_replace(array("\r","\n"), array('',' '), htmlentities(substr($this->Data, $this->Pos, self::SnipLength)))); + } else { + echo $ErrMsg; + } + if ($this->ExitOnError) { + exit(); + } + $ErrorPos = $this->Pos; + return false; + } +} + +/** + * Torrent class that contains some convenient functions related to torrent meta data + */ +class BEncTorrent extends BEncDec { + private $PathKey = 'path'; + public $Files = array(); + public $Size = 0; + + /** + * Create a list of the files in the torrent and their sizes as well as the total torrent size + * + * @return array with a list of files and file sizes + */ + public function file_list() { + if (empty($this->Dec)) { + return false; + } + $InfoDict =& $this->Dec['info']; + if (!isset($InfoDict['files'])) { + // Single-file torrent + $this->Files = array( + $this->Size = (Int64::is_int($InfoDict['length']) + ? Int64::get($InfoDict['length']) + : $InfoDict['length']), + $this->Files = (isset($InfoDict['name.utf-8']) + ? $InfoDict['name.utf-8'] + : $InfoDict['name']) + ); + } else { + if (isset($InfoDict['path.utf-8']['files'][0])) { + $this->PathKey = 'path.utf-8'; + } + foreach ($InfoDict['files'] as $File) { + $TmpPath = array(); + foreach ($File[$this->PathKey] as $SubPath) { + $TmpPath[] = $SubPath; + } + $CurSize = (Int64::is_int($File['length']) + ? Int64::get($File['length']) + : $File['length']); + $this->Files[] = array($CurSize, implode('/', $TmpPath)); + $this->Size += $CurSize; + } + } + uasort($this->Files, function($a, $b) { + return strnatcasecmp($a[1], $b[1]); + }); + return array($this->Size, $this->Files); + } + + /** + * Find out the name of the torrent + * + * @return string torrent name + */ + public function get_name() { + if (empty($this->Dec)) { + return false; + } + if (isset($this->Dec['info']['name.utf-8'])) { + return $this->Dec['info']['name.utf-8']; + } + return $this->Dec['info']['name']; + } + + /** + * Find out the total size of the torrent + * + * @return string torrent size + */ + public function get_size() { + if (empty($this->Files)) { + if (empty($this->Dec)) { + return false; + } + $FileList = $this->file_list(); + } + return $FileList[0]; + } + + /** + * Checks if the "private" flag is present in the torrent + * + * @return true if the "private" flag is set + */ + public function is_private() { + if (empty($this->Dec)) { + return false; + } + return isset($this->Dec['info']['private']) && Int64::get($this->Dec['info']['private']) == 1; + } + /** + * Add the "private" flag to the torrent + * + * @return true if a change was required + */ + public function make_private() { + if (empty($this->Dec)) { + return false; + } + if ($this->is_private()) { + return false; + } + $this->Dec['info']['private'] = Int64::make(1); + ksort($this->Dec['info']); + return true; + } + + /** + * Calculate the torrent's info hash + * + * @return info hash in hexadecimal form + */ + public function info_hash() { + if (empty($this->Dec) || !isset($this->Dec['info'])) { + return false; + } + return sha1($this->encode(false, 'info')); + } + + /** + * Add the announce URL to a torrent + */ + public static function add_announce_url($Data, $Url) { + return 'd8:announce'.strlen($Url).':'.$Url . substr($Data, 1); + } +} +?> diff --git a/classes/class_misc.php b/classes/class_misc.php index 08e23b16..7a78bc3d 100644 --- a/classes/class_misc.php +++ b/classes/class_misc.php @@ -400,5 +400,16 @@ public static function display_array($Array, $Escape = array()) { } return $Array; } + + /** + * Check for a : in the beginning of a torrent meta data string + * to see if it's stored in the old base64-encoded format + * + * @param string $Torrent the torrent data + * @return true if the torrent is stored in binary format + */ + public static function is_new_torrent(&$Data) { + return strpos(substr($Data, 0, 10), ':') !== false; + } } ?> diff --git a/classes/class_mysql.php b/classes/class_mysql.php index cd522de2..bda757d0 100644 --- a/classes/class_mysql.php +++ b/classes/class_mysql.php @@ -315,6 +315,19 @@ function to_array($Key = false, $Type = MYSQLI_BOTH, $Escape = true) { return $Return; } + // Loops through the result set, collecting the $ValField column into an array with $KeyField as keys + function to_pair($KeyField, $ValField, $Escape = true) { + $Return = array(); + while ($Row = mysqli_fetch_array($this->QueryID)) { + if ($Escape !== false) { + $Row = Misc::display_array($Row[$ValField], $Escape); + } + $Return[$Row[$KeyField]] = $Row[$ValField]; + } + mysqli_data_seek($this->QueryID, 0); + return $Return; + } + // Loops through the result set, collecting the $Key column into an array function collect($Key, $Escape = true) { $Return = array(); @@ -336,7 +349,7 @@ function get_query_id() { function beginning() { mysqli_data_seek($this->QueryID, 0); + $this->Row = 0; } - } ?> diff --git a/classes/class_torrentsdl.php b/classes/class_torrentsdl.php new file mode 100644 index 00000000..1948e9ca --- /dev/null +++ b/classes/class_torrentsdl.php @@ -0,0 +1,211 @@ +QueryResult = $QueryResult; + $this->Title = $Title; + $this->Zip = new Zip(Misc::file_string($Title)); + } + + /** + * Store the results from a DB query in smaller chunks to save memory + * + * @param string $Key the key to use in the result hash map + * @return array with results and torrent group IDs or false if there are no results left + */ + public function get_downloads($Key) { + global $DB; + $GroupIDs = $Downloads = array(); + $OldQuery = $DB->get_query_id(); + $DB->set_query_id($this->QueryResult); + if (!isset($this->IDBoundaries)) { + if ($Key == 'TorrentID') { + $this->IDBoundaries = false; + } else { + $this->IDBoundaries = $DB->to_pair($Key, 'TorrentID', false); + } + } + $Found = 0; + while ($Download = $DB->next_record(MYSQLI_ASSOC, false)) { + if (!$this->IDBoundaries || $Download['TorrentID'] == $this->IDBoundaries[$Download[$Key]]) { + $Found++; + $Downloads[$Download[$Key]] = $Download; + $GroupIDs[$Download['TorrentID']] = $Download['GroupID']; + if ($Found >= self::ChunkSize) { + break; + } + } + } + $this->NumFound += $Found; + $DB->set_query_id($OldQuery); + if (empty($Downloads)) { + return false; + } + return array($Downloads, $GroupIDs); + } + + /** + * Add a file to the zip archive + * + * @param string $Content file content + * @param array $FileInfo file info stored as an array with at least the keys + * Artist, Name, Year, Media, Format, Encoding and TorrentID + * @param string $FolderName folder name + */ + public function add_file($Content, $FileInfo, $FolderName = '') { + $FileName = self::construct_file_name($FileInfo['Artist'], $FileInfo['Name'], $FileInfo['Year'], $FileInfo['Media'], $FileInfo['Format'], $FileInfo['Encoding'], $FileInfo['TorrentID']); + $this->Size += $FileInfo['Size']; + $this->NumAdded++; + $this->Zip->add_file($Content, ($FolderName ? "$FolderName/" : "") . $FileName); + usleep(25000); // We don't want to send much faster than the client can receive + } + + /** + * Add a file to the list of files that could not be downloaded + * + * @param array $FileInfo file info stored as an array with at least the keys Artist, Name and Year + */ + public function fail_file($FileInfo) { + $this->FailedFiles[] = $FileInfo['Artist'] . $FileInfo['Name'] . " $FileInfo[Year]"; + } + + /** + * Add a file to the list of files that did not match the user's format or quality requirements + * + * @param array $FileInfo file info stored as an array with at least the keys Artist, Name and Year + */ + public function skip_file($FileInfo) { + $this->SkippedFiles[] = $FileInfo['Artist'] . $FileInfo['Name'] . " $FileInfo[Year]"; + } + + /** + * Add a summary to the archive and include a list of files that could not be added. Close the zip archive + * + * @param int $Analyzed number of files that were analyzed (e.g. number of groups in a collage) + * @param int $Skips number of files that did not match any of the user's criteria + */ + public function finalize($FilterStats = true) { + $this->Zip->add_file($this->summary($FilterStats), "Summary.txt"); + if (!empty($this->FailedFiles)) { + $this->Zip->add_file($this->errors(), "Errors.txt"); + } + $this->Zip->close_stream(); + } + + /** + * Produce a summary text over the collector results + * + * @param bool $FilterStats whether to include filter stats in the report + * @return summary text + */ + public function summary($FilterStats) { + global $LoggedUser, $ScriptStartTime; + $Time = number_format(1000 * (microtime(true) - $ScriptStartTime), 2)." ms"; + $Used = Format::get_size(memory_get_usage(true)); + $Date = date("M d Y, H:i"); + $NumSkipped = count($this->SkippedFiles); + return "Collector Download Summary for $this->Title - ".SITE_NAME."\r\n" + . "\r\n" + . "User: $LoggedUser[Username]\r\n" + . "Passkey: $LoggedUser[torrent_pass]\r\n" + . "\r\n" + . "Time: $Time\r\n" + . "Used: $Used\r\n" + . "Date: $Date\r\n" + . "\r\n" + . ($FilterStats !== false + ? "Torrent groups analyzed: $this->NumFound\r\n" + . "Torrent groups filtered: $NumSkipped\r\n" + : "") + . "Torrents downloaded: $this->NumAdded\r\n" + . "\r\n" + . "Total size of torrents (ratio hit): ".Format::get_size($this->Size)."\r\n" + . ($NumSkipped + ? "\r\n" + . "Albums unavailable within your criteria (consider making a request for your desired format):\r\n" + . implode("\r\n", $this->SkippedFiles) . "\r\n" + : ""); + } + + /** + * Compile a list of files that could not be added to the archive + * + * @return list of files + */ + public function errors() { + return "A server error occurred. Please try again at a later time.\r\n" + . "\r\n" + . "The following torrents could not be downloaded:\r\n" + . implode("\r\n", $this->FailedFiles) . "\r\n"; + } + + /** + * Combine a bunch of torrent info into a standardized file name + * + * @params most input variables are mostly self-explanatory + * @param int $TorrentID if given, append "-TorrentID" to torrent name + * @param bool $TxtExtension whether to use .txt or .torrent as file extension + * @return file name with at most 180 characters that is valid on most systems + */ + public static function construct_file_name($Artist, $Album, $Year, $Media, $Format, $Encoding, $TorrentID = false, $TxtExtension = false) { + $TorrentName = Misc::file_string($Album); + if ($Year > 0) { + $TorrentName .= " - $Year"; + } + $TorrentInfo = array(); + if ($Media != '') { + $TorrentInfo[] = $Media; + } + if ($Format != '') { + $TorrentInfo[] = $Format; + } + if ($Encoding != '') { + $TorrentInfo[] = $Encoding; + } + if (!empty($TorrentInfo)) { + $TorrentInfo = " (" . Misc::file_string(implode(" - ", $TorrentInfo)) . ")"; + } else { + $TorrentInfo = ""; + } + + if (!$TorrentName) { + $TorrentName = "No Name"; + } else if (strlen($TorrentName . $TorrentInfo) <= 197) { + $TorrentName = Misc::file_string($Artist) . $TorrentName; + } + + // Leave some room to the user in case the file system limits the path length + $MaxLength = $TxtExtension ? 196 : 192; + if ($TorrentID) { + $MaxLength -= 8; + } + $TorrentName = Format::cut_string($TorrentName . $TorrentInfo, $MaxLength, true, false); + if ($TorrentID) { + $TorrentName .= "-$TorrentID"; + } + if ($TxtExtension) { + return "$TorrentName.txt"; + } + return "$TorrentName.torrent"; + } +} diff --git a/classes/class_zip.php b/classes/class_zip.php index a561f960..b3c1656c 100644 --- a/classes/class_zip.php +++ b/classes/class_zip.php @@ -19,7 +19,7 @@ * First, construct the archive: -$Zip = new ZIP('FileName'); +$Zip = new Zip('FileName'); Adds the headers so that add_file can stream and we don't need to create a massive buffer. open_stream(); was integrated into the constructor to conform with Object-Oriented Standards. @@ -59,7 +59,7 @@ //------------- Detailed example -------------// require('classes/class_zip.php'); -$Zip = new ZIP('FileName'); +$Zip = new Zip('FileName'); $Name = 'Ubuntu-8.10'; $Zip->add_file($TorrentData, 'Torrents/'.Misc::file_string($Name).'.torrent'); $Zip->add_file(file_get_contents('zip.php'), 'zip.php'); @@ -90,7 +90,7 @@ function dostime($TimeStamp = 0) { } */ -class ZIP { +class Zip { public $ArchiveSize = 0; //Total size public $ArchiveFiles = 0; // Total files private $Structure = ''; // Structure saved to memory @@ -99,7 +99,7 @@ class ZIP { public function __construct ($ArchiveName='Archive') { header("Content-type: application/octet-stream"); //Stream download - header("Content-disposition: attachment; filename=\"".urlencode($ArchiveName).".zip\""); //Name the archive + header("Content-disposition: attachment; filename=\"$ArchiveName.zip\""); //Name the archive - Should not be urlencoded } public static function unlimit () { @@ -112,7 +112,7 @@ public function add_file ($FileData, $ArchivePath, $TimeStamp = 0) { /* File header */ $this->Data = "\x50\x4b\x03\x04"; // PK signature $this->Data .= "\x14\x00"; // Version requirements - $this->Data .= "\x00\x00"; // Bit flag + $this->Data .= "\x00\x08"; // Bit flag - 0x8 = UTF-8 file names $this->Data .= "\x08\x00"; // Compression //$this->Data .= dostime($TimeStamp); //Last modified $this->Data .= "\x00\x00\x00\x00"; @@ -147,9 +147,9 @@ public function add_file ($FileData, $ArchivePath, $TimeStamp = 0) { /* Central Directory Structure */ $CDS = "\x50\x4b\x01\x02"; // CDS signature - $CDS .="\x00\x00"; // Constructor version + $CDS .="\x14\x00"; // Constructor version $CDS .="\x14\x00"; // Version requirements - $CDS .="\x00\x00"; // Bit flag + $CDS .="\x00\x08"; // Bit flag - 0x8 = UTF-8 file names $CDS .="\x08\x00"; // Compression $CDS .="\x00\x00\x00\x00"; // Last modified $CDS .= pack("V",$CRC32); // CRC-32 diff --git a/classes/script_start.php b/classes/script_start.php index aeabf271..154c71eb 100644 --- a/classes/script_start.php +++ b/classes/script_start.php @@ -90,6 +90,18 @@ case 'Format': $FileName = 'class_format'; break; + case 'LastFM': + $FileName = 'class_lastfm'; + break; + case 'MASS_USER_BOOKMARKS_EDITOR': + $FileName = 'class_mass_user_bookmarks_editor'; + break; + case 'MASS_USER_TORRENTS_EDITOR': + $FileName = 'class_mass_user_torrents_editor'; + break; + case 'MASS_USER_TORRENTS_TABLE_VIEW': + $FileName = 'class_mass_user_torrents_table_view'; + break; case 'Misc': $FileName = 'class_misc'; break; @@ -104,8 +116,8 @@ case 'SphinxQL_Result': $FileName = 'class_sphinxql'; break; - case 'Tracker': - $FileName = 'class_tracker'; + case 'TEXTAREA_PREVIEW': + $FileName = 'class_textarea_preview'; break; case 'Tools': $FileName = 'class_tools'; @@ -113,29 +125,23 @@ case 'Torrents': $FileName = 'class_torrents'; break; + case 'TorrentsDL': + $FileName = 'class_torrentsdl'; + break; + case 'Tracker': + $FileName = 'class_tracker'; + break; case 'Users': $FileName = 'class_users'; break; case 'View': $FileName = 'class_view'; break; - case 'MASS_USER_TORRENTS_EDITOR': - $FileName = 'class_mass_user_torrents_editor'; - break; - case 'MASS_USER_BOOKMARKS_EDITOR': - $FileName = 'class_mass_user_bookmarks_editor'; - break; - case 'MASS_USER_TORRENTS_TABLE_VIEW': - $FileName = 'class_mass_user_torrents_table_view'; - break; - case 'TEXTAREA_PREVIEW': - $FileName = 'class_textarea_preview'; - break; case 'Votes': $FileName = 'class_votes'; break; - case 'LastFM': - $FileName = 'class_lastfm'; + case 'Zip': + $FileName = 'class_zip'; break; default: die("Couldn't import class " . $ClassName); diff --git a/sections/artist/artistcomments.sql b/sections/artist/artistcomments.sql deleted file mode 100644 index 390aacb4..00000000 --- a/sections/artist/artistcomments.sql +++ /dev/null @@ -1,14 +0,0 @@ -USE gazelle; - -CREATE TABLE `artist_comments` ( - `ID` int(10) NOT NULL AUTO_INCREMENT, - `ArtistID` int(10) NOT NULL, - `AuthorID` int(10) NOT NULL, - `AddedTime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `Body` mediumtext COLLATE utf8_bin, - `EditedUserID` int(10) DEFAULT NULL, - `EditedTime` datetime DEFAULT NULL, - PRIMARY KEY (`ID`), - KEY `TopicID` (`ArtistID`), - KEY `AuthorID` (`AuthorID`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; diff --git a/sections/artist/download.php b/sections/artist/download.php index 274af3a3..77bd2152 100644 --- a/sections/artist/download.php +++ b/sections/artist/download.php @@ -46,34 +46,43 @@ ORDER BY t.GroupID ASC, Rank DESC, t.Seeders ASC */ -if( +if ( !isset($_REQUEST['artistid']) || !isset($_REQUEST['preference']) || !is_number($_REQUEST['preference']) || !is_number($_REQUEST['artistid']) || $_REQUEST['preference'] > 2 || count($_REQUEST['list']) == 0 -) { error(0); } +) { + error(0); +} -if(!check_perms('zip_downloader')){ error(403); } +if (!check_perms('zip_downloader')) { + error(403); +} -$Preferences = array('RemasterTitle DESC','Seeders ASC','Size ASC'); +require(SERVER_ROOT.'/classes/class_bencode.php'); +require(SERVER_ROOT.'/classes/class_torrent.php'); + +$Preferences = array('RemasterTitle DESC', 'Seeders ASC', 'Size ASC'); $ArtistID = $_REQUEST['artistid']; $Preference = $Preferences[$_REQUEST['preference']]; $DB->query("SELECT Name FROM artists_group WHERE ArtistID='$ArtistID'"); -list($ArtistName) = $DB->next_record(MYSQLI_NUM,false); +list($ArtistName) = $DB->next_record(MYSQLI_NUM, false); $DB->query("SELECT GroupID, Importance FROM torrents_artists WHERE ArtistID='$ArtistID'"); -if($DB->record_count() == 0) { error(404); } -$Releases = $DB->to_array('GroupID',MYSQLI_ASSOC,false); +if ($DB->record_count() == 0) { + error(404); +} +$Releases = $DB->to_array('GroupID', MYSQLI_ASSOC, false); $GroupIDs = array_keys($Releases); $SQL = "SELECT CASE "; foreach ($_REQUEST['list'] as $Priority => $Selection) { - if(!is_number($Priority)) { + if (!is_number($Priority)) { continue; } $SQL .= "WHEN "; @@ -110,96 +119,67 @@ } $SQL .= "ELSE 100 END AS Rank, t.GroupID, -t.ID, +t.ID AS TorrentID, t.Media, t.Format, t.Encoding, tg.ReleaseType, -IF(t.RemasterYear=0,tg.Year,t.RemasterYear), +IF(t.RemasterYear=0,tg.Year,t.RemasterYear) AS Year, tg.Name, t.Size FROM torrents AS t JOIN torrents_group AS tg ON tg.ID=t.GroupID AND tg.CategoryID='1' AND tg.ID IN (".implode(',',$GroupIDs).") ORDER BY t.GroupID ASC, Rank DESC, t.$Preference"; -$DB->query($SQL); -$Downloads = $DB->to_array('1',MYSQLI_NUM,false); -$Artists = Artists::get_artists($GroupIDs, false); -$Skips = array(); -$TotalSize = 0; -if(count($Downloads)) { - foreach($Downloads as $Download) { - $TorrentIDs[] = $Download[2]; - } - $DB->query("SELECT TorrentID, file FROM torrents_files WHERE TorrentID IN (".implode(',', $TorrentIDs).")"); - $Torrents = $DB->to_array('TorrentID',MYSQLI_ASSOC,false); -} +$DownloadsQ = $DB->query($SQL); +$Collector = new TorrentsDL($DownloadsQ, $ArtistName); -require(SERVER_ROOT.'/classes/class_torrent.php'); -require(SERVER_ROOT.'/classes/class_zip.php'); -$Zip = new ZIP(Misc::file_string($ArtistName)); -foreach($Downloads as $Download) { - list($Rank, $GroupID, $TorrentID, $Media, $Format, $Encoding, $ReleaseType, $Year, $Album, $Size) = $Download; - $Artist = Artists::display_artists($Artists[$GroupID],false,true,false); - if ($Rank == 100) { - $Skips[] = $Artist.$Album.' '.$Year; +while (list($Downloads, $GroupIDs) = $Collector->get_downloads('GroupID')) { + $Debug->log_var($Downloads, '$Downloads'); + $Debug->log_var($GroupIDs, '$GroupIDs'); + $Artists = Artists::get_artists($GroupIDs); + $TorrentFilesQ = $DB->query("SELECT TorrentID, File FROM torrents_files WHERE TorrentID IN (".implode(',', array_keys($GroupIDs)).")", false); + if (is_int($TorrentFilesQ)) { + // Query failed. Let's not create a broken zip archive + foreach ($GroupIDs as $GroupID) { + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$GroupID], false, true, false); + $Collector->fail_file($Download); + } continue; } - if($Releases[$GroupID]['Importance'] == 1) { - $ReleaseTypeName = $ReleaseTypes[$ReleaseType]; - } elseif($Releases[$GroupID]['Importance'] == 2) { - $ReleaseTypeName = "Guest Appearance"; - } elseif($Releases[$GroupID]['Importance'] == 3) { - $ReleaseTypeName = "Remixed By"; + while (list($TorrentID, $TorrentFile) = $DB->next_record(MYSQLI_NUM, false)) { + $GroupID = $GroupIDs[$TorrentID]; + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); + if ($Download['Rank'] == 100) { + $Collector->skip_file($Download); + continue; + } + if ($Releases[$GroupID]['Importance'] == 1) { + $ReleaseTypeName = $ReleaseTypes[$Download['ReleaseType']]; + } else if ($Releases[$GroupID]['Importance'] == 2) { + $ReleaseTypeName = "Guest Appearance"; + } else if ($Releases[$GroupID]['Importance'] == 3) { + $ReleaseTypeName = "Remixed By"; + } + if (Misc::is_new_torrent($TorrentFile)) { + $TorEnc = BEncTorrent::add_announce_url($TorrentFile, ANNOUNCE_URL."/$LoggedUser[torrent_pass]/announce"); + } else { + $Contents = unserialize(base64_decode($TorrentFile)); + $Tor = new TORRENT($Contents, true); + $Tor->set_announce_url(ANNOUNCE_URL."/$LoggedUser[torrent_pass]/announce"); + unset($Tor->Val['announce-list']); + $TorEnc = $Tor->enc(); + } + $Collector->add_file($TorEnc, $Download, $ReleaseTypeName); + unset($Download); } - $TotalSize += $Size; - $Contents = unserialize(base64_decode($Torrents[$TorrentID]['file'])); - $Tor = new TORRENT($Contents, true); - $Tor->set_announce_url(ANNOUNCE_URL.'/'.$LoggedUser['torrent_pass'].'/announce'); - unset($Tor->Val['announce-list']); - - // We need this section for long file names :/ - $TorrentName=''; - $TorrentInfo=''; - $TorrentName = Misc::file_string($Artist.$Album); - if ($Year > 0) { $TorrentName.=' - '.Misc::file_string($Year); } - if ($Media != '') { $TorrentInfo .= Misc::file_string($Media); } - if ($Format != '') { - if ($TorrentInfo!='') { $TorrentInfo .= ' - '; } - $TorrentInfo .= Misc::file_string($Format); - } - if ($Encoding!='') { - if ($TorrentInfo != '') { $TorrentInfo.=' - '; } - $TorrentInfo .= Misc::file_string($Encoding); - } - if ($TorrentInfo != '') { $TorrentInfo = " ($TorrentInfo)"; } - if (strlen($TorrentName) + strlen($TorrentInfo) + 3 > 200) { - $TorrentName = Misc::file_string($Album).(($Year>0)?(' - '.Misc::file_string($Year)):''); - } - $FileName = Format::cut_string($TorrentName.$TorrentInfo, 180, true, false); - - $Zip->add_file($Tor->enc(), $ReleaseTypeName.'/'.$FileName.'.torrent'); } -$Analyzed = count($Downloads); -$Skipped = count($Skips); -$Downloaded = $Analyzed - $Skipped; -$Time = number_format(((microtime(true)-$ScriptStartTime)*1000),5).' ms'; -$Used = Format::get_size(memory_get_usage(true)); -$Date = date('M d Y, H:i'); -$Zip->add_file('Collector Download Summary - '.SITE_NAME."\r\n\r\nUser:\t\t$LoggedUser[Username]\r\nPasskey:\t$LoggedUser[torrent_pass]\r\n\r\nTime:\t\t$Time\r\nUsed:\t\t$Used\r\nDate:\t\t$Date\r\n\r\nTorrents Analyzed:\t\t$Analyzed\r\nTorrents Filtered:\t\t$Skipped\r\nTorrents Downloaded:\t$Downloaded\r\n\r\nTotal Size of Torrents (Ratio Hit): ".Format::get_size($TotalSize)."\r\n\r\nAlbums Unavailable within your criteria (consider making a request for your desired format):\r\n".implode("\r\n",$Skips), 'Summary.txt'); -$Settings = array(implode(':',$_REQUEST['list']),$_REQUEST['preference']); -$Zip->close_stream(); - -$Settings = array(implode(':',$_REQUEST['list']),$_REQUEST['preference']); -if(!isset($LoggedUser['Collector']) || $LoggedUser['Collector'] != $Settings) { - $DB->query("SELECT SiteOptions FROM users_info WHERE UserID='".$LoggedUser['ID']."'"); - list($Options) = $DB->next_record(MYSQLI_NUM,false); - $Options = unserialize($Options); - $Options['Collector'] = $Settings; - $DB->query("UPDATE users_info SET SiteOptions='".db_string(serialize($Options))."' WHERE UserID='$LoggedUser[ID]'"); - $Cache->begin_transaction('user_info_heavy_'.$LoggedUser['ID']); - $Cache->insert('Collector',$Settings); - $Cache->commit_transaction(0); +$Collector->finalize(); +$Settings = array(implode(':', $_REQUEST['list']), $_REQUEST['preference']); +if (!isset($LoggedUser['Collector']) || $LoggedUser['Collector'] != $Settings) { + Users::update_site_options($LoggedUser['ID'], array('Collector' => $Settings)); } define('IE_WORKAROUND_NO_CACHE_HEADERS', 1); diff --git a/sections/collages/download.php b/sections/collages/download.php index 780894d3..49384a49 100644 --- a/sections/collages/download.php +++ b/sections/collages/download.php @@ -46,29 +46,36 @@ ORDER BY t.GroupID ASC, Rank DESC, t.Seeders ASC */ -if( +if ( !isset($_REQUEST['collageid']) || !isset($_REQUEST['preference']) || !is_number($_REQUEST['preference']) || !is_number($_REQUEST['collageid']) || $_REQUEST['preference'] > 2 || count($_REQUEST['list']) == 0 -) { error(0); } +) { + error(0); +} -if(!check_perms('zip_downloader')){ error(403); } +if (!check_perms('zip_downloader')) { + error(403); +} -$Preferences = array('RemasterTitle DESC','Seeders ASC','Size ASC'); +require(SERVER_ROOT.'/classes/class_bencode.php'); +require(SERVER_ROOT.'/classes/class_torrent.php'); + +$Preferences = array('RemasterTitle DESC', 'Seeders ASC', 'Size ASC'); $CollageID = $_REQUEST['collageid']; $Preference = $Preferences[$_REQUEST['preference']]; $DB->query("SELECT Name FROM collages WHERE ID='$CollageID'"); -list($CollageName) = $DB->next_record(MYSQLI_NUM,false); +list($CollageName) = $DB->next_record(MYSQLI_NUM, false); $SQL = "SELECT CASE "; foreach ($_REQUEST['list'] as $Priority => $Selection) { - if(!is_number($Priority)) { + if (!is_number($Priority)) { continue; } $SQL .= "WHEN "; @@ -105,11 +112,11 @@ } $SQL .= "ELSE 100 END AS Rank, t.GroupID, -t.ID, +t.ID AS TorrentID, t.Media, t.Format, t.Encoding, -IF(t.RemasterYear=0,tg.Year,t.RemasterYear), +IF(t.RemasterYear=0,tg.Year,t.RemasterYear) AS Year, tg.Name, t.Size FROM torrents AS t @@ -117,79 +124,46 @@ INNER JOIN torrents_group AS tg ON tg.ID=t.GroupID AND tg.CategoryID='1' ORDER BY t.GroupID ASC, Rank DESC, t.$Preference"; -$DB->query($SQL); -$Downloads = $DB->to_array('1',MYSQLI_NUM,false); -$Artists = Artists::get_artists($DB->collect('GroupID'), false); -$Skips = array(); -$TotalSize = 0; +$DownloadsQ = $DB->query($SQL); +$Collector = new TorrentsDL($DownloadsQ, $CollageName); -if(count($Downloads)) { - foreach($Downloads as $Download) { - $TorrentIDs[] = $Download[2]; - } - $DB->query("SELECT TorrentID, file FROM torrents_files WHERE TorrentID IN (".implode(',', $TorrentIDs).")"); - $Torrents = $DB->to_array('TorrentID',MYSQLI_ASSOC,false); -} - -require(SERVER_ROOT.'/classes/class_torrent.php'); -require(SERVER_ROOT.'/classes/class_zip.php'); -$Zip = new ZIP(Misc::file_string($CollageName)); -foreach($Downloads as $Download) { - list($Rank, $GroupID, $TorrentID, $Media, $Format, $Encoding, $Year, $Album, $Size) = $Download; - $Artist = Artists::display_artists($Artists[$GroupID],false,true,false); - if ($Rank == 100) { - $Skips[] = $Artist.$Album.' '.$Year; +while (list($Downloads, $GroupIDs) = $Collector->get_downloads('GroupID')) { + $Artists = Artists::get_artists($GroupIDs); + $TorrentFilesQ = $DB->query("SELECT TorrentID, File FROM torrents_files WHERE TorrentID IN (".implode(',', array_keys($GroupIDs)).")", false); + if (is_int($TorrentFilesQ)) { + // Query failed. Let's not create a broken zip archive + foreach ($GroupIDs as $GroupID) { + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$GroupID], false, true, false); + $Collector->fail_file($Download); + } continue; } - $TotalSize += $Size; - $Contents = unserialize(base64_decode($Torrents[$TorrentID]['file'])); - $Tor = new TORRENT($Contents, true); - $Tor->set_announce_url(ANNOUNCE_URL.'/'.$LoggedUser['torrent_pass'].'/announce'); - unset($Tor->Val['announce-list']); - - // We need this section for long file names :/ - $TorrentName=''; - $TorrentInfo=''; - $TorrentName = Misc::file_string($Artist.$Album); - if ($Year > 0) { $TorrentName.=' - '.Misc::file_string($Year); } - if ($Media != '') { $TorrentInfo .= Misc::file_string($Media); } - if ($Format != '') { - if ($TorrentInfo!='') { $TorrentInfo .= ' - '; } - $TorrentInfo .= Misc::file_string($Format); + while (list($TorrentID, $TorrentFile) = $DB->next_record(MYSQLI_NUM, false)) { + $GroupID = $GroupIDs[$TorrentID]; + $Download =& $Downloads[$GroupID]; + $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); + if ($Download['Rank'] == 100) { + $Collector->skip_file($Download); + continue; + } + if (Misc::is_new_torrent($TorrentFile)) { + $TorEnc = BEncTorrent::add_announce_url($TorrentFile, ANNOUNCE_URL."/$LoggedUser[torrent_pass]/announce"); + } else { + $Contents = unserialize(base64_decode($TorrentFile)); + $Tor = new TORRENT($Contents, true); + $Tor->set_announce_url(ANNOUNCE_URL."/$LoggedUser[torrent_pass]/announce"); + unset($Tor->Val['announce-list']); + $TorEnc = $Tor->enc(); + } + $Collector->add_file($TorEnc, $Download); + unset($Download); } - if ($Encoding!='') { - if ($TorrentInfo != '') { $TorrentInfo.=' - '; } - $TorrentInfo .= Misc::file_string($Encoding); - } - if ($TorrentInfo != '') { $TorrentInfo = " ($TorrentInfo)"; } - if (strlen($TorrentName) + strlen($TorrentInfo) + 3 > 200) { - $TorrentName = Misc::file_string($Album).(($Year>0)?(' - '.Misc::file_string($Year)):''); - } - $FileName = $TorrentName.$TorrentInfo; - $FileName = Format::cut_string($FileName, 192, true, false); - - $Zip->add_file($Tor->enc(), $FileName.'.torrent'); } -$Analyzed = count($Downloads); -$Skipped = count($Skips); -$Downloaded = $Analyzed - $Skipped; -$Time = number_format(((microtime(true)-$ScriptStartTime)*1000),5).' ms'; -$Used = Format::get_size(memory_get_usage(true)); -$Date = date('M d Y, H:i'); -$Zip->add_file('Collector Download Summary - '.SITE_NAME."\r\n\r\nUser:\t\t$LoggedUser[Username]\r\nPasskey:\t$LoggedUser[torrent_pass]\r\n\r\nTime:\t\t$Time\r\nUsed:\t\t$Used\r\nDate:\t\t$Date\r\n\r\nTorrents Analyzed:\t\t$Analyzed\r\nTorrents Filtered:\t\t$Skipped\r\nTorrents Downloaded:\t$Downloaded\r\n\r\nTotal Size of Torrents (Ratio Hit): ".Format::get_size($TotalSize)."\r\n\r\nAlbums Unavailable within your criteria (consider making a request for your desired format):\r\n".implode("\r\n",$Skips), 'Summary.txt'); -$Settings = array(implode(':',$_REQUEST['list']),$_REQUEST['preference']); -$Zip->close_stream(); - -$Settings = array(implode(':',$_REQUEST['list']),$_REQUEST['preference']); -if(!isset($LoggedUser['Collector']) || $LoggedUser['Collector'] != $Settings) { - $DB->query("SELECT SiteOptions FROM users_info WHERE UserID='$LoggedUser[ID]'"); - list($Options) = $DB->next_record(MYSQLI_NUM,false); - $Options = unserialize($Options); - $Options['Collector'] = $Settings; - $DB->query("UPDATE users_info SET SiteOptions='".db_string(serialize($Options))."' WHERE UserID='$LoggedUser[ID]'"); - $Cache->begin_transaction('user_info_heavy_'.$LoggedUser['ID']); - $Cache->insert('Collector',$Settings); - $Cache->commit_transaction(0); +$Collector->finalize(); +$Settings = array(implode(':', $_REQUEST['list']), $_REQUEST['preference']); +if (!isset($LoggedUser['Collector']) || $LoggedUser['Collector'] != $Settings) { + Users::update_site_options($LoggedUser['ID'], array('Collector' => $Settings)); } define('IE_WORKAROUND_NO_CACHE_HEADERS', 1); diff --git a/sections/torrents/download.php b/sections/torrents/download.php index 438069f4..2d5f81e4 100644 --- a/sections/torrents/download.php +++ b/sections/torrents/download.php @@ -24,7 +24,6 @@ $TorrentPass = $_REQUEST['torrent_pass']; $AuthKey = $_REQUEST['authkey']; } -require(SERVER_ROOT.'/classes/class_torrent.php'); $TorrentID = $_REQUEST['id']; @@ -147,58 +146,25 @@ $DB->query("SELECT File FROM torrents_files WHERE TorrentID='$TorrentID'"); -list($Contents) = $DB->next_record(MYSQLI_NUM, array(0)); -$Contents = unserialize(base64_decode($Contents)); -$Tor = new TORRENT($Contents, true); // New TORRENT object -// Set torrent announce URL -$Tor->set_announce_url(ANNOUNCE_URL.'/'.$TorrentPass.'/announce'); -// Remove multiple trackers from torrent -unset($Tor->Val['announce-list']); -// Remove web seeds (put here for old torrents not caught by previous commit -unset($Tor->Val['url-list']); -// Remove libtorrent resume info -unset($Tor->Val['libtorrent_resume']); -// Torrent name takes the format of Artist - Album - YYYY (Media - Format - Encoding) - -$TorrentName=''; -$TorrentInfo=''; - -$TorrentName = $Info['PlainArtists']; - -$TorrentName.=$Name; - -if ($Year>0) { $TorrentName.=' - '.$Year; } - -if ($Media!='') { $TorrentInfo.=$Media; } - -if ($Format!='') { - if ($TorrentInfo!='') { $TorrentInfo.=' - '; } - $TorrentInfo.=$Format; +list($Contents) = $DB->next_record(MYSQLI_NUM, false); +if (Misc::is_new_torrent($Contents)) { + require(SERVER_ROOT.'/classes/class_bencode.php'); + $TorEnc = BEncTorrent::add_announce_url($Contents, ANNOUNCE_URL."/$TorrentPass/announce"); +} else { + require(SERVER_ROOT.'/classes/class_torrent.php'); + $Contents = unserialize(base64_decode($Contents)); + $Tor = new TORRENT($Contents, true); // New TORRENT object + // Set torrent announce URL + $Tor->set_announce_url(ANNOUNCE_URL."/$TorrentPass/announce"); + // Remove multiple trackers from torrent + unset($Tor->Val['announce-list']); + // Remove web seeds (put here for old torrents not caught by previous commit + unset($Tor->Val['url-list']); + // Remove libtorrent resume info + unset($Tor->Val['libtorrent_resume']); + $TorEnc = $Tor->enc(); } - -if ($Encoding!='') { - if ($TorrentInfo!='') { $TorrentInfo.=' - '; } - $TorrentInfo.=$Encoding; -} - -// Let's try to shorten the filename intelligently before chopping it off -if (strlen($TorrentName) + strlen($TorrentInfo) + 3 > 200) { - $TorrentName = $Name . (($Year>0)?(' - '.$Year):''); -} - -if ($TorrentInfo!='') { $TorrentName.=' ('.$TorrentInfo.')'; } - -if(!empty($_GET['mode']) && $_GET['mode'] == 'bbb'){ - $TorrentName = $Artists.' -- '.$Name; -} - -if (!$TorrentName) { $TorrentName="No Name"; } - -$FileName = ($Browser == 'Internet Explorer') ? urlencode(Misc::file_string($TorrentName)) : Misc::file_string($TorrentName); -$MaxLength = $DownloadAlt ? 192 : 196; -$FileName = Format::cut_string($FileName, $MaxLength, true, false); -$FileName = $DownloadAlt ? $FileName.'.txt' : $FileName.'.torrent'; - +$FileName = TorrentsDL::construct_file_name($Info['PlainArtists'], $Name, $Year, $Media, $Format, $Encoding, false, $DownloadAlt); if($DownloadAlt) { header('Content-Type: text/plain; charset=utf-8'); @@ -207,6 +173,6 @@ } header('Content-disposition: attachment; filename="'.$FileName.'"'); -echo $Tor->enc(); +echo $TorEnc; define('IE_WORKAROUND_NO_CACHE_HEADERS', 1); diff --git a/sections/torrents/redownload.php b/sections/torrents/redownload.php index f0f8f606..baabca7a 100644 --- a/sections/torrents/redownload.php +++ b/sections/torrents/redownload.php @@ -12,9 +12,10 @@ $User = Users::user_info($UserID); $Perms = Permissions::get_permissions($User['PermissionID']); $UserClass = $Perms['Class']; +list($UserID, $Username) = array_values($User); +require(SERVER_ROOT.'/classes/class_bencode.php'); require(SERVER_ROOT.'/classes/class_torrent.php'); -require(SERVER_ROOT.'/classes/class_zip.php'); if (empty($_GET['type'])) { error(0); @@ -47,84 +48,52 @@ } } -ZIP::unlimit(); - -$DB->query("SELECT - t.ID, +$DownloadsQ = $DB->query("SELECT + t.ID AS TorrentID, DATE_FORMAT(".$Month.",'%Y - %m') AS Month, t.GroupID, t.Media, t.Format, t.Encoding, - IF(t.RemasterYear=0,tg.Year,t.RemasterYear), + IF(t.RemasterYear=0,tg.Year,t.RemasterYear) AS Year, tg.Name, t.Size - FROM torrents as t + FROM torrents as t JOIN torrents_group AS tg ON t.GroupID=tg.ID ".$SQL." - GROUP BY t.ID"); -$Downloads = $DB->to_array(0, MYSQLI_NUM, false); -$Artists = Artists::get_artists($DB->collect('GroupID')); + GROUP BY TorrentID"); -if (!empty($Downloads)) { - $DB->query("SELECT TorrentID, File FROM torrents_files WHERE TorrentID IN (".implode(',', array_keys($Downloads)).")"); - $TorrentFiles = $DB->to_array(0, MYSQLI_NUM, false); -} -list($UserID, $Username) = array_values(Users::user_info($UserID)); -$Zip = new ZIP($Username.'\'s '.ucfirst($_GET['type'])); -foreach ($Downloads as $Download) { - list($TorrentID, $Month, $GroupID, $Media, $Format, $Encoding, $Year, $Album, $Size) = $Download; - if (!isset($TorrentFiles[$TorrentID])) { +$Collector = new TorrentsDL($DownloadsQ, "$Username's ".ucfirst($_GET['type'])); + +while (list($Downloads, $GroupIDs) = $Collector->get_downloads('TorrentID')) { + $Artists = Artists::get_artists($GroupIDs); + $TorrentIDs = array_keys($GroupIDs); + $TorrentFilesQ = $DB->query("SELECT TorrentID, File FROM torrents_files WHERE TorrentID IN (".implode(',', $TorrentIDs).")", false); + if (is_int($TorrentFilesQ)) { + // Query failed. Let's not create a broken zip archive + foreach ($TorrentIDs as $TorrentID) { + $Download =& $Downloads[$TorrentID]; + $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); + $Collector->fail_file($Download); + } continue; } - $Artist = Artists::display_artists($Artists[$GroupID],false,true,false); - $Contents = unserialize(base64_decode($TorrentFiles[$TorrentID][1])); - $Tor = new TORRENT($Contents, true); - $Tor->set_announce_url(ANNOUNCE_URL.'/'.$LoggedUser['torrent_pass'].'/announce'); - unset($Tor->Val['announce-list']); - - $TorrentName = ''; - $TorrentInfo = ''; - $TorrentName = $Artist; - $TorrentName .= $Album; - - if ($Year > 0) { - $TorrentName .= ' - '.$Year; - } - - if ($Media != '') { - $TorrentInfo .= $Media; - } - - if ($Format != '') { - if ($TorrentInfo != '') { - $TorrentInfo .= ' - '; + while (list($TorrentID, $TorrentFile) = $DB->next_record(MYSQLI_NUM, false)) { + $Download =& $Downloads[$TorrentID]; + $Download['Artist'] = Artists::display_artists($Artists[$Download['GroupID']], false, true, false); + if (Misc::is_new_torrent($TorrentFile)) { + $TorEnc = BEncTorrent::add_announce_url($TorrentFile, ANNOUNCE_URL."/$LoggedUser[torrent_pass]/announce"); + } else { + $Contents = unserialize(base64_decode($TorrentFile)); + $Tor = new TORRENT($Contents, true); + $Tor->set_announce_url(ANNOUNCE_URL."/$LoggedUser[torrent_pass]/announce"); + unset($Tor->Val['announce-list']); + $TorEnc = $Tor->enc(); } - $TorrentInfo .= $Format; + $Collector->add_file($TorEnc, $Download, $Download['Month']); + unset($Download); } - - if ($Encoding!='') { - if ($TorrentInfo != '') { - $TorrentInfo .= ' - '; - } - $TorrentInfo .= $Encoding; - } - - if ($TorrentInfo != '') { - $TorrentName .= ' ('.$TorrentInfo.')'; - } - - if (!$TorrentName) { - $TorrentName = "No Name"; - } - - $FileName = Misc::file_string($TorrentName); - if ($Browser == 'Internet Explorer') { - $FileName = urlencode($FileName); - } - $FileName .= '.torrent'; - $Zip->add_file($Tor->enc(), Misc::file_string($Month).'/'.$FileName); } -$Zip->close_stream(); +$Collector->finalize(false); define('IE_WORKAROUND_NO_CACHE_HEADERS', 1);