Oppaitime's version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

torrentsdl.class.php 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <?
  2. /**
  3. * Class for functions related to the features involving torrent downloads
  4. */
  5. class TorrentsDL {
  6. const ChunkSize = 100;
  7. const MaxPathLength = 200;
  8. private $QueryResult;
  9. private $QueryRowNum = 0;
  10. private $Zip;
  11. private $IDBoundaries;
  12. private $FailedFiles = array();
  13. private $NumAdded = 0;
  14. private $NumFound = 0;
  15. private $Size = 0;
  16. private $Title;
  17. private $User;
  18. private $AnnounceURL;
  19. private $AnnounceList;
  20. /**
  21. * Create a Zip object and store the query results
  22. *
  23. * @param mysqli_result $QueryResult results from a query on the collector pages
  24. * @param string $Title name of the collection that will be created
  25. * @param string $AnnounceURL URL to add to the created torrents
  26. */
  27. public function __construct(&$QueryResult, $Title) {
  28. G::$Cache->InternalCache = false; // The internal cache is almost completely useless for this
  29. Zip::unlimit(); // Need more memory and longer timeout
  30. $this->QueryResult = $QueryResult;
  31. $this->Title = $Title;
  32. $this->User = G::$LoggedUser;
  33. $this->AnnounceURL = ANNOUNCE_URLS[0][0]."/".G::$LoggedUser['torrent_pass']."/announce";
  34. function add_passkey($Ann) {
  35. return (is_array($Ann)) ? array_map('add_passkey', $Ann) : $Ann."/".G::$LoggedUser['torrent_pass']."/announce";
  36. }
  37. $this->AnnounceList = (sizeof(ANNOUNCE_URLS) == 1 && sizeof(ANNOUNCE_URLS[0]) == 1) ? array() : array_map('add_passkey', ANNOUNCE_URLS);
  38. $this->Zip = new Zip(Misc::file_string($Title));
  39. }
  40. /**
  41. * Store the results from a DB query in smaller chunks to save memory
  42. *
  43. * @param string $Key the key to use in the result hash map
  44. * @return array with results and torrent group IDs or false if there are no results left
  45. */
  46. public function get_downloads($Key) {
  47. $GroupIDs = $Downloads = array();
  48. $OldQuery = G::$DB->get_query_id();
  49. G::$DB->set_query_id($this->QueryResult);
  50. if (!isset($this->IDBoundaries)) {
  51. if ($Key == 'TorrentID') {
  52. $this->IDBoundaries = false;
  53. } else {
  54. $this->IDBoundaries = G::$DB->to_pair($Key, 'TorrentID', false);
  55. }
  56. }
  57. $Found = 0;
  58. while ($Download = G::$DB->next_record(MYSQLI_ASSOC, false)) {
  59. if (!$this->IDBoundaries || $Download['TorrentID'] == $this->IDBoundaries[$Download[$Key]]) {
  60. $Found++;
  61. $Downloads[$Download[$Key]] = $Download;
  62. $GroupIDs[$Download['TorrentID']] = $Download['GroupID'];
  63. if ($Found >= self::ChunkSize) {
  64. break;
  65. }
  66. }
  67. }
  68. $this->NumFound += $Found;
  69. G::$DB->set_query_id($OldQuery);
  70. if (empty($Downloads)) {
  71. return false;
  72. }
  73. return array($Downloads, $GroupIDs);
  74. }
  75. /**
  76. * Add a file to the zip archive
  77. *
  78. * @param string $TorrentData bencoded torrent without announce url (new format) or TORRENT object (old format)
  79. * @param array $Info file info stored as an array with at least the keys
  80. * Artist, Name, Year, Media, Format, Encoding and TorrentID
  81. * @param string $FolderName folder name
  82. */
  83. public function add_file(&$TorrentData, $Info, $FolderName = '') {
  84. $FolderName = Misc::file_string($FolderName);
  85. $MaxPathLength = $FolderName ? (self::MaxPathLength - strlen($FolderName) - 1) : self::MaxPathLength;
  86. $FileName = self::construct_file_name($Info['Artist'], $Info['Name'], $Info['Year'], $Info['Media'], $Info['Format'], $Info['Encoding'], $Info['TorrentID'], false, $MaxPathLength);
  87. $this->Size += $Info['Size'];
  88. $this->NumAdded++;
  89. $this->Zip->add_file(self::get_file($TorrentData, $this->AnnounceURL, $this->AnnounceList), ($FolderName ? "$FolderName/" : "") . $FileName);
  90. usleep(25000); // We don't want to send much faster than the client can receive
  91. }
  92. /**
  93. * Add a file to the list of files that could not be downloaded
  94. *
  95. * @param array $Info file info stored as an array with at least the keys Artist, Name and Year
  96. */
  97. public function fail_file($Info) {
  98. $this->FailedFiles[] = $Info['Artist'] . $Info['Name'] . " $Info[Year]";
  99. }
  100. /**
  101. * Add a file to the list of files that did not match the user's format or quality requirements
  102. *
  103. * @param array $Info file info stored as an array with at least the keys Artist, Name and Year
  104. */
  105. public function skip_file($Info) {
  106. $this->SkippedFiles[] = $Info['Artist'] . $Info['Name'] . " $Info[Year]";
  107. }
  108. /**
  109. * Add a summary to the archive and include a list of files that could not be added. Close the zip archive
  110. *
  111. * @param bool $FilterStats whether to include filter stats in the report
  112. */
  113. public function finalize($FilterStats = true) {
  114. $this->Zip->add_file($this->summary($FilterStats), "Summary.txt");
  115. if (!empty($this->FailedFiles)) {
  116. $this->Zip->add_file($this->errors(), "Errors.txt");
  117. }
  118. $this->Zip->close_stream();
  119. }
  120. /**
  121. * Produce a summary text over the collector results
  122. *
  123. * @param bool $FilterStats whether to include filter stats in the report
  124. * @return summary text
  125. */
  126. public function summary($FilterStats) {
  127. global $ScriptStartTime;
  128. $Time = number_format(1000 * (microtime(true) - $ScriptStartTime), 2)." ms";
  129. $Used = Format::get_size(memory_get_usage(true));
  130. $Date = date("M d Y, H:i");
  131. $NumSkipped = count($this->SkippedFiles);
  132. return "Collector Download Summary for $this->Title - " . SITE_NAME . "\r\n"
  133. . "\r\n"
  134. . "User: {$this->User[Username]}\r\n"
  135. . "Passkey: {$this->User[torrent_pass]}\r\n"
  136. . "\r\n"
  137. . "Time: $Time\r\n"
  138. . "Used: $Used\r\n"
  139. . "Date: $Date\r\n"
  140. . "\r\n"
  141. . ($FilterStats !== false
  142. ? "Torrent groups analyzed: $this->NumFound\r\n"
  143. . "Torrent groups filtered: $NumSkipped\r\n"
  144. : "")
  145. . "Torrents downloaded: $this->NumAdded\r\n"
  146. . "\r\n"
  147. . "Total size of torrents (ratio hit): ".Format::get_size($this->Size)."\r\n"
  148. . ($NumSkipped
  149. ? "\r\n"
  150. . "Albums unavailable within your criteria (consider making a request for your desired format):\r\n"
  151. . implode("\r\n", $this->SkippedFiles) . "\r\n"
  152. : "");
  153. }
  154. /**
  155. * Compile a list of files that could not be added to the archive
  156. *
  157. * @return list of files
  158. */
  159. public function errors() {
  160. return "A server error occurred. Please try again at a later time.\r\n"
  161. . "\r\n"
  162. . "The following torrents could not be downloaded:\r\n"
  163. . implode("\r\n", $this->FailedFiles) . "\r\n";
  164. }
  165. /**
  166. * Combine a bunch of torrent info into a standardized file name
  167. *
  168. * @params most input variables are self-explanatory
  169. * @param int $TorrentID if given, append "-TorrentID" to torrent name
  170. * @param bool $Txt whether to use .txt or .torrent as file extension
  171. * @param int $MaxLength maximum file name length
  172. * @return file name with at most $MaxLength characters
  173. */
  174. public static function construct_file_name($Artist, $Album, $Year, $Media, $Format, $Encoding, $TorrentID = false, $Txt = false, $MaxLength = self::MaxPathLength) {
  175. $MaxLength -= ($Txt ? 4 : 8);
  176. if ($TorrentID !== false) {
  177. $MaxLength -= (strlen($TorrentID) + 1);
  178. }
  179. $TorrentArtist = Misc::file_string($Artist);
  180. $TorrentName = Misc::file_string($Album);
  181. if ($Year > 0) {
  182. $TorrentName .= " - $Year";
  183. }
  184. $TorrentInfo = array();
  185. if ($Media != '') {
  186. $TorrentInfo[] = $Media;
  187. }
  188. if ($Format != '') {
  189. $TorrentInfo[] = $Format;
  190. }
  191. if ($Encoding != '') {
  192. $TorrentInfo[] = $Encoding;
  193. }
  194. if (!empty($TorrentInfo)) {
  195. $TorrentInfo = ' (' . Misc::file_string(implode(' - ', $TorrentInfo)) . ')';
  196. } else {
  197. $TorrentInfo = '';
  198. }
  199. if (!$TorrentName) {
  200. $TorrentName = 'No Name';
  201. } elseif (mb_strlen($TorrentArtist . $TorrentName . $TorrentInfo, 'UTF-8') <= $MaxLength) {
  202. $TorrentName = $TorrentArtist . $TorrentName;
  203. }
  204. $TorrentName = Format::cut_string($TorrentName . $TorrentInfo, $MaxLength, true, false);
  205. if ($TorrentID !== false) {
  206. $TorrentName .= "-$TorrentID";
  207. }
  208. if ($Txt) {
  209. return "$TorrentName.txt";
  210. }
  211. return "$TorrentName.torrent";
  212. }
  213. /**
  214. * Convert a stored torrent into a binary file that can be loaded in a torrent client
  215. *
  216. * @param mixed $TorrentData bencoded torrent without announce URL (new format) or TORRENT object (old format)
  217. * @return bencoded string
  218. */
  219. public static function get_file(&$TorrentData, $AnnounceURL, $AnnounceList = array()) {
  220. if (Misc::is_new_torrent($TorrentData)) {
  221. $Bencode = BencodeTorrent::add_announce_url($TorrentData, $AnnounceURL);
  222. if (!empty($AnnounceList)) {
  223. $Bencode = BencodeTorrent::add_announce_list($Bencode, $AnnounceList);
  224. }
  225. return $Bencode;
  226. }
  227. $Tor = new TORRENT(unserialize(base64_decode($TorrentData)), true);
  228. $Tor->set_announce_url($AnnounceURL);
  229. unset($Tor->Val['announce-list']);
  230. unset($Tor->Val['url-list']);
  231. unset($Tor->Val['libtorrent_resume']);
  232. return $Tor->enc();
  233. }
  234. }