/rescene.php
PHP | 2862 lines | 1930 code | 312 blank | 620 comment | 304 complexity | 33ade3b945d705a1d5ad146e49485ffa MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * PHP Library to read and edit a .srr file. It reads .srs files.
- * Copyright (c) 2011-2017 Gfy
- *
- * rescene.php is free software, you can redistribute it and/or modify
- * it under the terms of GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *
- * You should have received a copy of the the GNU Affero
- * General Public License, along with rescene.php. If not, see
- * http://www.gnu.org/licenses/agpl.html
- *
- * Additional permission under the GNU Affero GPL version 3 section 7:
- *
- * If you modify this Program, or any covered work, by linking or
- * combining it with other code, such other code is not for that reason
- * alone subject to any of the requirements of the GNU Affero GPL
- * version 3.
- */
- /*
- * LGPLv3 with Affero clause (LAGPL)
- * See http://mo.morsi.org/blog/node/270
- * rescene.php written on 2011-07-27
- * Last version: 2017-07-09
- *
- * Features:
- * - process a SRR file which returns:
- * - SRR file size.
- * - Application name of the tool used to create the SRR file.
- * - List of files stored in the SRR.
- * - List of RAR volumes the SRR can reconstruct.
- * - List of files that are archived inside these RARs.
- * - Size of all Recovery Records inside the SRR file.
- * - Comments inside SFV files.
- * - Warnings when something unusual is found with the SRR.
- * - Remove a stored file.
- * - Rename a stored file.
- * - Add a stored file.
- * - Read a stored file.
- * - Extract a stored file.
- * - Calculate a hash of the SRR based on RAR metadata.
- * - Sorting of the stored file names.
- * - process in memory SRR 'file'
- * - compare two SRR files
- * - nfo: ignore line endings
- * - sfv: sort it before comparing and remove comment lines
- * - rar metadata
- * -> quick: by hash
- * -> see what is missing
- * - other files
- * -> quick: by hash
- * - compare SRS files
- * - Output flag added to indicate if the RARs used compression.
- * - Support to read SRS files. (AVI/MKV/MP4/WMV/FLAC/MP3)
- * - Sort stored files inside the SRR.
- * - OpenSubtitles.org hash support.
- * - Extract the SRR meta data of a single RAR set
- *
- * - nfo compare: strip line endings + new line?
- * Indiana.Jones.And.The.Last.Crusade.1989.PAL.DVDR-DNA
- *
- * List of possible features/todo list:
- * - process in memory SRR 'file' + other API functions (very low priority)
- * => can be done using temp files in memory
- * - refactor compare SRR
- * - merge SRRs (Python script exists)
- * - encryption sanity check
- * - add paths before the rar files
- * - detect when SRR is cut/metadata from rars missing
- * => hard to do correctly (SFVs subs exist too)
- * - how to throw errors correctly?
- * - sorting the list of the stored files by hand
- * - "Application name found in the middle of the SRR."
- * causes hashes to be different
- * - http://www.srrdb.com/release/details/Race.To.Witch.Mountain.1080p.BluRay.x264-HD1080 (wrong file size)
- * - http://www.srrdb.com/release/details/NBA.2010.03.02.Pacers.Vs.Lakers.720p.HDTV.x264-BALLS (crc FFFFFFFF)
- * - http://www.srrdb.com/release/details/Dexter.S01E03.720p.Bluray.x264-ORPHEUS (short crc)
- * - When renaming a file and only the capitals will be different, a file with the old name is added.
- * - http://www.srrdb.com/release/details/Scrapland.AlcoholClone.MI-NOGRP (dupe names, so not all files get shown)
- * - Add error when a file is twice in the SFV (twice the meta data too)
- * - Add warning when there is no SFV file and the SRR contains RAR meta data
- *
- */
- // necessary for storing files in large (60MB) SRR files
- ini_set('memory_limit', '512M');
- $BLOCKNAME = array(
- 0x69 => 'SRR VolumeHeader',
- 0x6A => 'SRR Stored File',
- 0x6B => 'SRR OSO Hash',
- 0x6C => 'SRR RAR Padding',
- 0x71 => 'SRR RAR subblock',
- 0x72 => 'RAR Marker',
- 0x73 => 'Archive Header',
- 0x74 => 'File',
- 0x75 => 'Old style - Comment',
- 0x76 => 'Old style - Extra info (authenticity information)',
- 0x77 => 'Old style - Subblock',
- 0x78 => 'Old style - Recovery record',
- 0x79 => 'Old style - Archive authenticity',
- 0x7A => 'New-format subblock',
- 0x7B => 'Archive end'
- );
- class FileType {
- const MKV = 'MKV';
- const AVI = 'AVI';
- const MP4 = 'MP4';
- const WMV = 'WMV';
- const FLAC = 'FLAC';
- const MP3 = 'MP3';
- const STREAM = 'STRM'; // vob and basic m2ts
- const M2TS = 'M2TS';
- const Unknown = '';
- }
- // for suppressing error messages
- $CLI_APP = false;
- // cli progs are cool
- if (!empty($argc) && strstr($argv[0], basename(__FILE__))) {
- $CLI_APP = true;
-
- /* How to use the CLI version in Windows:
- - Download and install PHP. http://windows.php.net/download/
- - Run this script by entering something like
- C:\Program Files (x86)\PHP\php.exe rescene.php
- in the command prompt.
- - [Add 'C:\Program Files (x86)\PHP' to your systems Path environment variable
- to be able to run PHP from anywhere.]
- - To run this script from everywhere, create 'rescene.bat' in a directory that is in your PATH.
- For example: 'C:\Windows\rescene.bat'
- Include the following content:
- "C:\Program Files (x86)\PHP\php.exe" "C:\Windows\rescene.php" %*
- And place the PHP file accordingly.
- Enter 'rescene' anywhere to use it.
- */
- if (!array_key_exists(1, $argv)) {
- echo "The first parameter needs to be a .srr file.\n";
- echo " -s 'file to store' (Save)\n";
- echo " -d 'file to remove' (Delete)\n";
- echo " -r 'file to rename' (Rename)\n";
- echo " -v 'file to get' (View)\n";
- echo " -x 'file to write' (eXtract)\n";
- echo " -p 'file to split' (sPlit)\n";
- echo " -h 'special hash of the SRR file' (Hash)\n";
- echo " -a 'show SRS info (sAmple)\n";
- echo " -l 'show stored SRR languages (Languages)\n";
- echo " -c 'compare two SRR files' (Compare)\n";
- echo " -t 'runs a couple of small tests' (Testing)\n";
- exit(1);
- }
- $srr = $argv[1];
- // to test execution time
- $mtime = microtime();
- $mtime = explode(' ',$mtime);
- $mtime = $mtime[1] + $mtime[0];
- $starttime = $mtime;
- if (array_key_exists(2, $argv)) {
- $switch = $argv[2];
- if (array_key_exists(3, $argv)) {
- $file = $argv[3];
- switch($switch) {
- case '-d': // delete
- if (removeFile($srr, $file)) {
- echo 'File successfully removed.';
- } else {
- echo 'File not found in SRR file.';
- }
- break;
- case '-s': // store
- $path = '';
- if (array_key_exists(4, $argv)) {
- $path = $argv[4];
- }
- if (storeFileCli($srr, $file, $path)) {
- echo 'File successfully stored.';
- } else {
- echo 'Error while storing file.';
- }
- break;
- case '-r': // rename
- if (array_key_exists(4, $argv)) {
- $newName = $argv[4];
- echo 'SRR file: ' . $srr . "\n";
- echo 'Old name: ' . $file . "\n";
- echo 'New name: ' . $newName . "\n";
- if (renameFile($srr, $file, $newName)) {
- echo 'File successfully renamed.';
- } else {
- echo 'Error while renaming file.';
- }
- } else {
- echo 'Please enter a new name.';
- }
- break;
- case '-v': // view
- print_r(getStoredFile($srr, $file));
- break;
- case '-x': // extract
- // strip the path info
- $nopath = basename($file);
- $result = file_put_contents($nopath, getStoredFile($srr, $file));
- if ($result !== FALSE) {
- echo 'File succesfully extracted';
- } else {
- echo 'Something went wrong. Did you provide a correct file name with path?';
- }
- break;
- case '-c': // compare
- print_r(compareSrr($srr, $file));
- break;
- case '-p': // split
- $data = grabSrrSubset($srr, $file);
- file_put_contents('rescene.php_split.srr', $data);
- break;
- default:
- echo 'Unknown parameter. Use -r, -a, -v, -x or -c.';
- }
- } elseif ($switch === '-h') {
- echo 'The calculated content hash for this SRR file is: ';
- $result = processSrr($srr);
- echo calculateHash($srr, $result['rarFiles']);
- } elseif ($switch === '-a') {
- // show SRS info
- $srsData = file_get_contents($srr);
- print_r(processSrsData($srsData));
- } elseif ($switch === '-l') {
- // show vobsub languages
- print_r(getVobsubLanguages($srr));
- } elseif ($switch === '-t') {
- echo 'fileNameCheckTest: ';
- if (fileNameCheckTest()) {
- echo "OK!\n";
- } else {
- echo "NOT OK!\n";
- }
- echo 'createSrrHeaderBlockTest: ';
- if (createSrrHeaderBlockTest()) {
- echo "OK!\n";
- } else {
- echo "NOT OK!\n";
- }
- echo 'getBasenameVolumeTest: ';
- if (getBasenameVolumeTest()) {
- echo "OK!\n";
- } else {
- echo "NOT OK!\n";
- }
-
- //compareSrr($srr, $srr);
- //$data = file_get_contents($srr);
- //print_r(processSrrData($data));
- //add file
- //storeFileCli($srr, 'dbmodel.png');
- //remove file
- //if(removeFile($srr, 'dbmodel.png')) {
- // print_r("successfully removed");
- //}
- //process file
- // if ($result = processSrr($srr)) {
- // print_r($result);
- // echo 'success';
- // } else {
- // echo 'failure';
- // }
-
- // $sf = array_keys($result['storedFiles']);
- // sort($sf);
- // if (sortStoredFiles($srr, $sf)) {
- // echo 'success';
- // } else {
- // echo 'failure';
- // }
- }
- } else {
- $result = processSrr($srr);
- //print_r($result['storedFiles']);
- //print_r(($result['warnings']));
- //print_r(sortStoredFiles($result['storedFiles']));
- print_r($result);
- }
- // end part processing time
- $mtime = microtime();
- $mtime = explode(' ',$mtime);
- $mtime = $mtime[1] + $mtime[0];
- $endtime = $mtime;
- $totaltime = ($endtime - $starttime);
- echo "\nFile processed in {$totaltime} seconds";
- }
- // API functions
- /**
- * Processes a whole SRR file and returns an array with useful details.
- * @param string $file location to the file that needs to be read.
- * @return mixed data array, or false on failure
- */
- function processSrr($file) {
- $result = FALSE;
- if(file_exists($file)) {
- $fh = fopen($file, 'rb');
- if (flock($fh, LOCK_SH)) {
- $result = processSrrHandle($fh);
- flock($fh, LOCK_UN); // release the lock
- }
- fclose($fh); // close the file
- }
- return $result;
- }
- /**
- * Processes a whole SRR file and returns an array with useful details.
- * @param bytes $srrFileData the contents of the SRR file.
- */
- function processSrrData(&$srrFileData) {
- // http://www.php.net/manual/en/wrappers.php.php
- // Set the limit to 5 MiB. After this limit, a temporary file will be used.
- $memoryLimit = 5 * 1024 * 1024;
- $fp = fopen("php://temp/maxmemory:$memoryLimit", 'r+');
- fwrite($fp, $srrFileData);
- rewind($fp);
- $result = processSrrHandle($fp);
- fclose($fp);
- return $result;
- }
- /**
- * Leaves the file handle open!
- * Only used in the 2 functions above.
- */
- function processSrrHandle($fileHandle) {
- // global $BLOCKNAME;
- $fh = $fileHandle;
- $srrSize = getFileSizeHandle($fileHandle);
- // variables to store all resulting data
- $appName = 'No SRR application name found';
- $stored_files = array();
- $rar_files = array();
- $archived_files = array();
- $oso_hashes = array();
- $recovery = NULL;
- $sfv = array();
- $sfv['comments'] = array();
- $sfv['files'] = array();
- $warnings = array();
- $compressed = FALSE; // it's an SRR file for compressed RARs
- $encrypted = FALSE; // encryption is used on one or more files
- // other initializations
- $read = 0; // number of bytes we have read so far
- $last_read = 0; // to prevent looping on encountering bad data
- $current_rar = NULL;
- $customPacker = FALSE; // when not created with WinRAR
- while($read < $srrSize) {
- $add_size = TRUE;
- // to read basic block header
- $block = new Block($fh, $warnings);
- // echo 'Block type: ' . $BLOCKNAME[$block->blockType] . "\n";
- // echo 'Block flags: ' . dechex($block->flags) . "\n";
- // echo 'Header size: ' . $block->hsize . "\n";
- switch($block->blockType) {
- case 0x69: // SRR Header Block
- if ($appName !== 'No SRR application name found') {
- array_push($warnings, 'Application name found in the middle of the SRR.');
- }
- $appName = $block->readSrrAppName();
- break;
- case 0x6B: // SRR OSO Hash (blocks at the end of the file)
- $block->srrOsoHashFileHeader();
- $entry = array();
- $entry['fileName'] = $block->fileName;
- $entry['osoHash'] = $block->osoHash;
- $entry['fileSize'] = $block->fileSize;
- $entry['blockOffset'] = $block->startOffset;
- $entry['blockSize'] = $block->hsize;
- $entry['data'] = $block->data;
- array_push($oso_hashes, $entry);
-
- if (!is_null($current_rar)) {
- $current_rar = NULL; // SRR block detected: start again
- }
- break;
- case 0x6C: // SRR RAR Padding Block
- $current_rar['fileSize'] -= $block->hsize;
- $block->skipBlock();
- break;
- case 0x6A: // SRR Stored File Block
- $block->srrReadStoredFileHeader();
- // store stored file details
- $sf = array();
- $sf['fileName'] = $block->fileName;
- $sf['fileOffset'] = $block->storedFileStartOffset;
- $sf['fileSize'] = $block->addSize;
- $sf['blockOffset'] = $block->startOffset;
- // The same file can be stored multiple times.
- // This can make SRR files unnoticeably large.
- if (array_key_exists($block->fileName, $stored_files)) {
- // message here must start with Duplicate for the check in sorting
- array_push($warnings, "Duplicate file detected! {$sf['fileName']}");
- }
- if (preg_match('/\\\\/', $block->fileName)) {
- array_push($warnings, "Backslash detected! {$sf['fileName']}");
- }
- if ($block->addSize === 0) {
- // an "empty" directory is allowed
- if (strpos($sf['fileName'], '/') === FALSE) {
- array_push($warnings, "Empty file detected! {$sf['fileName']}");
- }
- } elseif (strtolower(substr($sf['fileName'], - 4)) === '.sfv') {
- // we read the sfv file to grab the crc data of the rar files
- $temp = processSfv(fread($fh, $block->addSize));
- $sfv['comments'] = array_merge($sfv['comments'], $temp['comments']);
- $sfv['files'] = array_merge($sfv['files'], $temp['files']);
- $sf['basenameVolume'] = getBasenameVolume($block->fileName, FALSE);
- }
- $block->skipBlock();
- // calculate CRC of the stored file
- $sdata = stream_get_contents($fileHandle, $block->addSize, $block->storedFileStartOffset);
- $sf['fileCrc'] = strtoupper(str_pad(dechex(crc32($sdata)), 8, '0', STR_PAD_LEFT));
- // $sf['fileCrc'] = dechex(crc32(fread($fh, $block->addSize)));
- // $sf['fileCrc'] = hash('crc32b', fread($fh, $block->addSize));
- $stored_files[$block->fileName] = $sf;
- // end file size counting (_should_ not be necessary for Stored File Block)
- // -> 'ReScene Database Cleanup Script 1.0' SRRs were fixed with 'FireScene Cleanup'
- // (stored files weren't before the first SRR Rar file block)
- case 0x71: // SRR Rar File
- if (!is_null($current_rar)) {
- $current_rar = NULL; // SRR block detected: start again
- }
- // end fall through from SRR Stored File block
- if ($block->blockType == 0x6A) {
- break;
- }
- $add_size = FALSE;
- // read the name of the stored rar file
- $block->srrReadRarFileHeader();
- $recovery_data_removed = $block->flags & 0x1;
- // the hashmap key is only the lower case file name without the path
- // to make it possible to add the CRC data from the SFVs
- $key = strtolower(basename($block->rarName));
- if (array_key_exists($key, $rar_files)) {
- $f = $rar_files[$key];
- } else {
- $f = array(); // array that stores the file details
- $f['fileName'] = $block->rarName; // the path is still stored here
- $f['fileSize'] = 0;
- // when the SRR is build without SFV or the SFV is missing some lines
- $f['fileCrc'] = 'UNKNOWN!';
- // useful for actually comparing srr data
- $f['offsetStartSrr'] = $block->startOffset; // where the SRR block begins
- $f['offsetStartRar'] = ftell($fh); // where the actual RAR headers begin
- // initialize, set later when volume header is available
- $f['basenameVolume'] = '';
- }
- $rar_files[$key] = $f;
- // start counting file size
- $current_rar = $f;
- break;
- case 0x74: // RAR Packed File
- $block->rarReadPackedFileHeader();
- if (array_key_exists($block->fileName, $archived_files)) {
- $f = $archived_files[$block->fileName];
- // FLEET, AVS,... (first and last rar have correct size)
- if ($f['fileSizeStart'] !== $block->fileSize) {
- $customPacker = TRUE;
- }
- } else { // new file found in the archives
- $f = array();
- $f['fileName'] = $block->fileName;
- $f['fileTime'] = date("Y-m-d h:i:s", $block->fileTime);
- $f['compressionMethod'] = $block->compressionMethod;
- $f['fileSizeStart'] = $block->fileSize;
- // file size complexity because of crappy custom packers
- if ($block->fileSize !== 0xffffffffffffffff && // 1
- $block->fileSize !== -1 && /* 2) 32 bit php */
- $block->fileSize !== 0xffffffff /* 2) 64 bit php */) {
- // file size normal case
- $f['fileSize'] = $block->fileSize;
- } else {
- $f['fileSize'] = 0;
- // 1) custom RAR packers used: last RAR contains the size
- // Street.Fighter.V-RELOADED or Magic.Flute-HI2U or 0x0007
- if ($block->fileSize == 0xffffffffffffffff) {
- array_push($warnings, "RELOADED/HI2U/0x0007 custom RAR packer detected.");
- }
- // 2) crap group that doesn't store the correct size at all:
- // The.Powerpuff.Girls.2016.S01E08.HDTV.x264-QCF
- if ($block->fileSize == 0xffffffff || $block->fileSize == -1) {
- array_push($warnings, "Crappy QCF RAR packer detected.");
- }
- }
- }
- // check if compression was used
- if ($f['compressionMethod'] != 0x30) { // 0x30: Storing
- $compressed = TRUE;
- }
-
- // file size counting fixes
- // 2) above int was correct? it must match at the end - QCF
- if (($block->fileSize == -1 || $block->fileSize == 0xffffffff) && !$compressed) {
- $f['fileSize'] += $block->addSize;
- }
- // 1) expected the last RAR (first with the proper value)
- if ($block->fileSize !== 0xffffffffffffffff && $f['fileSize'] == 0) {
- $f['fileSize'] = $block->fileSize;
- }
- // CRC of the file is the CRC stored in the last archive that has the file
- // add leading zeros when the CRC isn't 8 characters
- $f['fileCrc'] = strtoupper(str_pad($block->fileCrc, 8, '0', STR_PAD_LEFT));
- $archived_files[$block->fileName] = $f;
-
- // file is encrypted with password
- // The.Sims.4.City.Living.INTERNAL-RELOADED
- if ($block->flags & 0x4) {
- $encrypted = TRUE;
- }
- break;
- case 0x78: // RAR Old Recovery
- if (is_null($recovery)) {
- // first recovery block we see
- $recovery = array();
- $recovery['fileName'] = 'Protect!';
- $recovery['fileSize'] = 0;
- }
- $recovery['fileSize'] += $block->addSize;
- if ($recovery_data_removed) {
- $block->skipHeader();
- } else { // we need to skip the data that is still there
- $block->skipBlock();
- }
- break;
- case 0x7A: // RAR New Subblock: RR, AV, CMT
- $block->rarReadPackedFileHeader();
- if ($block->fileName === 'RR') { // Recovery Record
- if (is_null($recovery)) {
- $recovery = array();
- $recovery['fileName'] = 'Protect+';
- $recovery['fileSize'] = 0;
- }
- $recovery['fileSize'] += $block->addSize;
- if (!$recovery_data_removed) {
- $block->skipBlock();
- }
- break;
- } // other types have no data removed and will be fully skipped
- $block->skipBlock();
- break;
- case 0x73: // RAR Volume Header
- // warnings for ASAP and IMMERSE -> crappy rars
- $ext = strtolower(substr($current_rar['fileName'], - 4));
- if (($block->flags & 0x0100) && $ext !== '.rar' && $ext !== '.001') {
- array_push($warnings, "MHD_FIRSTVOLUME flag set for {$current_rar['fileName']}.");
- }
- $is_new_style_naming = $block->flags & 0x0010 && $block->flags & 0x0001; // new numbering and a volume
- $current_rar['basenameVolume'] = getBasenameVolume($current_rar['fileName'], $is_new_style_naming);
- // encrypted block headers are used: these SRRs don't exist
- if ($block->flags & 0x0080) {
- $encrypted = TRUE;
- }
- case 0x72: // RAR Marker
- case 0x7B: // RAR Archive End
- case 0x75: // Old Comment
- case 0x76: // Old Authenticity
- case 0x77: // Old Subblock
- case 0x79: // Old Authenticity
- // no usefull stuff for us anymore: skip block and possible contents
- $block->skipBlock();
- break;
- default: // Unrecognized RAR/SRR block found!
- $block->skipBlock();
- if (!empty($current_rar['fileName'])) { // Psych.S06E02.HDTV.XviD-P0W4
- // -> P0W4 cleared RAR archive end block: almost all zeros except for the header length field
- array_push($warnings, "Unknown RAR block found in {$current_rar['fileName']}");
- } else { // e.g. a rar file that still has its contents
- array_push($warnings, 'ERROR: Not a SRR file?');
- return FALSE;
- //trigger_error('Not a SRR file.', E_USER_ERROR);
- }
- }
- // calculate size of the rar file + end offset
- if (!is_null($current_rar)) {
- if ($add_size === TRUE) {
- $current_rar['fileSize'] += $block->fullSize;
- }
- // store end offset of the header data of the rar volume
- $current_rar['offsetEnd'] = ftell($fh);
- // keep the results updated
- $rar_files[strtolower(basename($current_rar['fileName']))] = $current_rar;
- }
- // nuber of bytes we have processed
- $read = ftell($fh);
-
- // don't loop when bad data is encountered
- if ($read === $last_read) {
- break;
- }
- $last_read = $read;
- }
- // add sfv CRCs to all the rar files we have found
- foreach ($sfv['files'] as $key => $val) {
- // the capitalization between sfv and the actual file isn't always the same
- $lkey = strtolower($key);
- if (array_key_exists($lkey, $rar_files)) {
- $rar_files[$lkey]['fileCrc'] = strtoupper($val);
- // everything that stays can not be reconstructed (subs from .sfv files)
- unset($sfv['files'][$key]); // remove data from $sfv
- }
- }
-
- if ($customPacker) {
- array_push($warnings, 'Custom RAR packer detected.');
- }
- // return all info in a multi dimensional array
- return array(
- 'srrSize' => $srrSize,
- 'appName' => $appName,
- 'storedFiles' => $stored_files,
- 'rarFiles' => $rar_files,
- 'archivedFiles' => $archived_files,
- 'osoHashes' => $oso_hashes,
- // Recovery Records across all archives in the SRR data
- // the name is based on the first encountered recovery block
- // Protect! -> old style RAR recovery (before RAR 3.0)
- // Protect+ -> new style RAR recovery
- 'recovery' => $recovery,
- 'sfv' => $sfv, // comments and files that aren't covered by the SRR
- 'warnings' => $warnings, // when something unusual is found
- 'compressed' => $compressed,
- 'encrypted' => $encrypted
- );
- }
- /**
- * Same as the getStoredFileData() function, but based on the file name.
- * @param $srrfile The name of the SRR file to read.
- * @param $filename The file we want the contents from, including the path.
- * @return The bytes of the file or FALSE on failure.
- */
- function getStoredFile($srrfile, $filename) {
- $result = FALSE;
- $fh = fopen($srrfile, 'rb');
-
- if (flock($fh, LOCK_SH)) {
- $srr = processSrrHandle($fh);
-
- foreach($srr['storedFiles'] as $key => $value) {
- if($key === $filename) {
- $result = stream_get_contents($fh, $value['fileSize'], $value['fileOffset']);
- break;
- }
- }
- flock($fh, LOCK_UN); // release the lock
- }
-
- fclose($fh); // close the file
- return $result;
- }
- /**
- * Removes a file stored in the SRR file.
- * @param string $srrfile Path of the SRR file.
- * @param string $filename Path and name of the file to remove.
- * @return TRUE on success, FALSE otherwise
- */
- function removeFile($srrfile, $filename) {
- $result = FALSE;
- $fh = fopen($srrfile, 'c+b');
-
- if (flock($fh, LOCK_EX)) {
- $srr = processSrrHandle($fh);
-
- foreach ($srr['storedFiles'] as $key => $value) {
- if ($value['fileName'] === $filename) {
- // how much to remove? read the block starting from the offset
- fseek($fh, $value['blockOffset'], SEEK_SET);
- $warnings_stub = array();
- $block = new Block($fh, $warnings_stub);
- fseek($fh, $value['blockOffset'] + $block->fullSize, SEEK_SET);
- $after = fread($fh, $srr['srrSize']); // srrSize: the (max) amount to read
- ftruncate($fh, $value['blockOffset']);
- fseek($fh, 0, SEEK_END); // Upon success, returns 0; otherwise, returns -1.
- fwrite($fh, $after);
- $result = TRUE;
- break;
- }
- }
-
- flock($fh, LOCK_UN); // release the lock
- }
-
- fclose($fh); // close the file
- return $result;
- }
- /**
- * Adds a file to the saved files inside a SRR file.
- * @param string $srr The path of the SRR file.
- * @param string $file The file to store.
- * @param bytes $path The path that must be prefixed for the file name.
- * @return TRUE on success, FALSE otherwise.
- */
- function storeFileCli($srr, $file, $path='') {
- // the path must have the path separator included
- if ($path != '' && substr($path, -1) !== '/') {
- return FALSE;
- }
- $fileContents = file_get_contents($file);
- return storeFile($srr, $path . basename($file), $fileContents);
- }
- /**
- * Adds a file to the saved files inside a SRR file.
- * @param string $srrFile The path of the SRR file.
- * @param string $filePath The path and name that will be stored.
- * @param bytes $fdata The bytes of the file to store in the SRR file.
- * @return TRUE when storing succeeds.
- */
- function storeFile($srrFile, $filePath, $fdata) {
- // check for illegal windows characters
- // the path separator must be /
- // twice (//) may not be possible
- if (fileNameCheck($filePath)) {
- return FALSE;
- }
- $fh = fopen($srrFile, 'c+b');
-
- if (flock($fh, LOCK_EX)) {
- $srr = processSrrHandle($fh);
-
- // don't let the same file get added twice
- foreach($srr['storedFiles'] as $key => $value) {
- if($key === $filePath) {
- flock($fh, LOCK_UN);
- fclose($fh);
- return FALSE;
- }
- }
-
- $offset = newFileOffset($fh);
- if ($offset < 0) {
- // broken/empty .srr file due to bugs :(
- flock($fh, LOCK_UN);
- fclose($fh);
- return FALSE;
- }
-
- $after = fread($fh, $srr['srrSize']);
- $header = createStoredFileHeader($filePath, strlen($fdata));
- ftruncate($fh, $offset);
- fseek($fh, 0, SEEK_END); // Upon success, returns 0; otherwise, returns -1.
- fwrite($fh, $header);
- fwrite($fh, $fdata);
- fwrite($fh, $after);
-
- flock($fh, LOCK_UN); // release the lock
- }
-
- fclose($fh); // close the file
- return TRUE;
- }
- function addOsoHash($srrFile, $oso_hash_data) {
- $fh = fopen($srrFile, 'c+b');
-
- if (flock($fh, LOCK_EX)) {
- $result = processSrrHandle($fh);
-
- // the hash must not already exist
- foreach($result['osoHashes'] as $value) {
- if ($value['data'] == $oso_hash_data) {
- flock($fh, LOCK_UN);
- fclose($fh);
- return FALSE;
- }
- }
-
- fseek($fh, 0, SEEK_END); // is not necessary
- fwrite($fh, $oso_hash_data);
-
- flock($fh, LOCK_UN); // release the lock
- }
-
- fclose($fh); // close the file
- return TRUE;
- }
- // /**
- // * Adds a new OSO hash to the end of the SRR file.
- // *
- // * @param string $srr
- // * @param int $fileSize
- // * @param string $osoHash
- // * @param string $fileName
- // */
- // function addOsoHash($srr, $fileSize, $osoHash, $fileName) {
- // // check for illegal windows characters; no paths
- // if (fileNameCheck($fileName) || strstr($fileName, '/')) {
- // return FALSE;
- // }
- // if ($fileSize < 0 || !preg_match('/[a-f0-9]{16}/i', $osoHash) || strlen($fileName) < 1) {
- // return FALSE;
- // }
-
- // // the hash must not already exist
- // $result = processSrr($srr);
- // foreach($result['osoHashes'] as $value) {
- // if ($value['fileName'] == $fileName &&
- // $value['fileSize'] == $fileSize &&
- // $value['osoHash'] == $osoHash) {
- // return FALSE;
- // }
- // }
-
- // $fh = fopen($srr, 'rb');
- // $before = fread($fh, getFileSizeHandle($fh));
- // fclose($fh);
-
- // // 2 byte CRC, 1 byte block type, 2 bytes for the flag 0x0000
- // $header = pack('H*' , '6B6B6B0000');
-
- // $osoBlockHeader = encode_int($fileSize); // broken on 32 bit!!
- // // OSO hash stored as little endian
- // $reversed = '';
- // for($i=strlen($osoHash);$i>=0;$i-=2) {
- // $reversed .= substr($osoHash, $i, 2);
- // }
- // $osoBlockHeader .= pack('H*' , $reversed);
- // $osoBlockHeader .= pack('v', strlen($fileName));
- // $osoBlockHeader .= $fileName;
- // $headerSize = pack('v', 5 + 2 + 8 + 8 + 2 + strlen($fileName));
- // print_r(unpack('H*', $header . $headerSize . $osoBlockHeader));
- // //file_put_contents($srr, $before . $header . $headerSize . $osoBlockHeader, LOCK_EX);
- // return TRUE;
- // }
- // function encode_int($in, $pad_to_bits=64, $little_endian=true) {
- // $in = decbin($in);
- // $in = str_pad($in, $pad_to_bits, '0', STR_PAD_LEFT);
- // $out = '';
- // for ($i = 0, $len = strlen($in); $i < $len; $i += 8) {
- // $out .= chr(bindec(substr($in,$i,8)));
- // }
- // if($little_endian) $out = strrev($out);
- // return $out;
- // }
- /**
- * Renames a stored file.
- * @param string $srrFile The path of the SRR file.
- * @param string $oldName The path and file name of a stored file.
- * @param string $newName The new path and file name of a stored file.
- * @return TRUE on success, FALSE otherwise.
- */
- function renameFile($srrFile, $oldName, $newName) {
- if (fileNameCheck($newName)) {
- if ($CLI_APP) {
- print_r("The new file name is illegal. Use only forward slashes for paths.\n");
- }
- return FALSE;
- }
-
- $result = FALSE;
- $fh = fopen($srrFile, 'c+b');
-
- if (flock($fh, LOCK_EX)) {
- $srr = processSrrHandle($fh);
-
- // prevent renaming to a file that already exists
- foreach ($srr['storedFiles'] as $key => $value) {
- if ($key === $newName) {
- flock($fh, LOCK_UN);
- fclose($fh);
- return FALSE;
- }
- }
-
- // rename the first file
- foreach ($srr['storedFiles'] as $key => $value) {
- if ($value['fileName'] === $oldName) {
- fseek($fh, $value['blockOffset'], SEEK_SET);
- $warnings_stub = array();
- $block = new Block($fh, $warnings_stub);
- $block->srrReadStoredFileHeader();
- fseek($fh, $value['blockOffset'] + $block->hsize, SEEK_SET);
- $after = fread($fh, $srr['srrSize']); // srrSize: the (max) amount to read
- ftruncate($fh, $value['blockOffset']);
- fseek($fh, 0, SEEK_END); // Upon success, returns 0; otherwise, returns -1.
- $changedHeader = createStoredFileHeader($newName, $block->addSize);
- fwrite($fh, $changedHeader);
- fwrite($fh, $after);
- $result = TRUE;
- break;
- }
- }
-
- flock($fh, LOCK_UN); // release the lock
- }
-
- fclose($fh); // close the file
- return $result;
- }
- /**
- * Calculate hash to identify SRRs that cover the same RAR volumes.
- * The result can be wrong when the provided $rarFiles array is outdated.
- * @param string $srr The SRR file.
- * @param array $rarFiles The resulting array from processSrr().
- * @return Sha1 hash of the srr file
- */
- function calculateHash($srrfile, $rarFiles, $algorithm='sha1') {
- // do the calculation only on the sorted RAR volumes
- // this way it still yields the same result if the order of creation differs
- uasort($rarFiles, 'rarFileCmp'); // sort on filename without path, case insensitive
- // compared with pyReScene when capitals are used: same behavior
- // Parlamentet.S06E02.SWEDiSH-SQC
- $hashContext = hash_init($algorithm);
- $fh = fopen($srrfile, 'rb');
-
- if (flock($fh, LOCK_SH)) {
- // calculate hash only on the RAR metadata
- foreach ($rarFiles as $key => $value) {
- $start = $value['offsetStartRar'];
- $end = $value['offsetEnd'];
- $data = stream_get_contents($fh, ($end - $start), $start);
- hash_update($hashContext, $data);
- }
- flock($fh, LOCK_UN); // release the lock
- }
-
- fclose($fh); // close the file
- return hash_final($hashContext);
- }
- // Comparison function
- function rarFileCmp($a, $b) {
- if ($a['fileName'] == $b['fileName']) {
- return 0;
- }
- return (strtolower($a['fileName']) < strtolower($b['fileName'])) ? -1 : 1;
- }
- function calculateHashHandle($srrHandle, $rarFiles, $algorithm='sha1') {
- // do the calculation only on the sorted RAR volumes
- // this way it still yields the same result if the order of creation differs
- uasort($rarFiles, 'rarFileCmp'); // sort on filename without path, case insensitive
- $hashContext = hash_init($algorithm);
- // calculate hash only on the RAR metadata
- foreach ($rarFiles as $key => $value) {
- $start = $value['offsetStartRar'];
- $end = $value['offsetEnd'];
- $data = stream_get_contents($srrHandle, ($end - $start), $start);
- hash_update($hashContext, $data);
- }
- return hash_final($hashContext);
- }
- function calculateHashString($srrData, $rarFiles, $algorithm='sha1') {
- // do the calculation only on the sorted RAR volumes
- // this way it still yields the same result if the order of creation differs
- uasort($rarFiles, 'rarFileCmp'); // sort on filename without path, case insensitive
- $hashContext = hash_init($algorithm);
- // calculate hash only on the RAR metadata
- foreach ($rarFiles as $key => $value) {
- $start = $value['offsetStartRar'];
- $end = $value['offsetEnd'];
- $memoryLimit = 5 * 1024 * 1024;
- $fp = fopen("php://temp/maxmemory:$memoryLimit", 'r+');
- fputs($fp, $srrData);
- rewind($fp);
- $fileAttributes = fstat($fp);
- $data = stream_get_contents($fp, ($end - $start), $start);
- hash_update($hashContext, $data);
- }
- return hash_final($hashContext);
- }
- /**
- * Compare 2 SRR files and list the differences.
- * @param $one First SRR file path.
- * @param $two Second SRR file path.
- * @return Some complicated array with differences.
- */
- function compareSrr($one, $two) {
- $result = FALSE;
- $fho = fopen($one, 'rb');
- $fht = fopen($two, 'rb');
-
- if (flock($fho, LOCK_SH) && flock($fht, LOCK_SH)) {
- $rone = processSrrHandle($fho);
- $rtwo = processSrrHandle($fht);
-
- $result = compareSrrRaw($rone, $rtwo, $fho, $fht);
-
- flock($fho, LOCK_UN); // release the lock
- flock($fht, LOCK_UN);
- }
-
- fclose($fho); // close the files
- fclose($fht);
-
- return $result;
- }
- /**
- * Same as above, but the info arrays of the SRR files were read before.
- * 2 times less parsing of the SRR files.
- */
- function compareSrrRaw($rone, $rtwo, $fhone, $fhtwo) {
- $hashOne = calculateHashHandle($fhone, $rone['rarFiles']);
- $hashTwo = calculateHashHandle($fhtwo, $rtwo['rarFiles']);
- // ----- The RARs -----
- // rebuild data can be considered the same?
- $sameRarData = $hashOne === $hashTwo;
- // hash => file name
- $hashesOne = hashParts($fhone, $rone['rarFiles']);
- $hashesTwo = hashParts($fhtwo, $rtwo['rarFiles']);
- // hash => file name (of those names unique to the first array)
- $left = array_diff($hashesOne, $hashesTwo);
- $right = array_diff($hashesTwo, $hashesOne);
- if ($sameRarData && count(array_merge($left, $right)) === 0) {
- $sameRarNames = TRUE;
- } else {
- $sameRarNames = FALSE;
- // must be picked in the comparison as the other one doesn't have it
- $uniqueRarOne = array_values(array_diff_key($hashesOne, $hashesTwo));
- $uniqueRarTwo = array_values(array_diff_key($hashesTwo, $hashesOne));
- // of the ones that are the same, the best name should be picked by default
- $twiceHash = array_keys(array_intersect_key($left, $right));
- $namesRarOne = array();
- $namesRarTwo = array();
- foreach ($twiceHash as $value) {
- $l = $left[$value];
- $r = $right[$value];
- // heuristic: we want the one with the longest length
- // this one probably has a path added
- if (strlen($l) > strlen($r)) {
- array_push($namesRarOne, $l);
- } else {
- array_push($namesRarTwo, $r);
- }
- }
- }
- // ----- The stored files -----
- // we compare .nfo, .sfv, .srs and other files to check if they are the same
- // or not a notewhorthy difference (line endings, sfv comments, ...)
- // if they are the same, only the filename/path needs to be chosen
- $filesOne = $rone['storedFiles'];
- $filesTwo = $rtwo['storedFiles'];
- // same name, same data => OK
- // different name, same data => one of both probably has a bad name (paths should always be the same for nfos)
- $same = array(); // list of tuples (fileOne, fileTwo, best) (because they can have different names)
- $sameName = array(); // same name, different data => e.g. Mr.X and Mr.Y sitescripts banner added for NFOs
- // suggest the largest file?
- // different name, different data => nfos from fixes ect.
- $uniqueOne = $filesOne;
- $uniqueTwo = $filesTwo;
- // *** NFO ***
- $oneNfo = getFilesByExt($filesOne, '.nfo');
- $twoNfo = getFilesByExt($filesTwo, '.nfo');
- // do not process these files again
- // Returns an array containing all the values from array1 that are not present in any of the other arrays.
- $filesOne = array_diff_assoc($filesOne, $oneNfo);
- $filesTwo = array_diff_assoc($filesTwo, $twoNfo);
- $oneNfo = addNfoHash($oneNfo, $fhone);
- $twoNfo = addNfoHash($twoNfo, $fhtwo);
- foreach ($oneNfo as $okey => $ovalue) {
- foreach ($twoNfo as $tkey => $tvalue) {
- $toUnset = FALSE;
- if ($ovalue['hash'] === $tvalue['hash']) {
- array_push($same, array($okey, $tkey,
- 'lines1' => $ovalue['lines'], 'lines2' => $tvalue['lines']));
- $toUnset = TRUE;
- } elseif ($ovalue['fileName'] === $tvalue['fileName']) {
- // suggest the largest NFO file
- if ($ovalue['fileSize'] > $tvalue['fileSize']) {
- $best = 0;
- } else {
- $best = 1;
- }
- array_push($sameName, array($okey, $tkey, 'best' => $best,
- 'lines1' => $ovalue['lines'], 'lines2' => $tvalue['lines']));
- $toUnset = TRUE;
- // TODO: show text diff?
- }
- if ($toUnset) {
- // remove from the array
- unset($uniqueOne[$okey]);
- unset($uniqueTwo[$tkey]);
- }
- }
- }
- // *** SFV ***
- $oneSfv = getFilesByExt($filesOne, '.sfv');
- $twoSfv = getFilesByExt($filesTwo, '.sfv');
- // do not process these files again
- $filesOne = array_diff_assoc($filesOne, $oneSfv);
- $filesTwo = array_diff_assoc($filesTwo, $twoSfv);
- $oneSfv = addSfvInfo($oneSfv, $fhone);
- $twoSfv = addSfvInfo($twoSfv, $fhtwo);
- foreach ($oneSfv as $okey => $ovalue) {
- foreach ($twoSfv as $tkey => $tvalue) {
- $toUnset = FALSE;
- if ($ovalue['files'] === $tvalue['files']) {
- // suggest the SFV file with the most comments
- if (count($ovalue['comments']) > count($tvalue['comments'])) {
- $best = 0;
- } elseif (count($ovalue['comments']) < count($tvalue['comments'])) {
- $best = 1;
- } else {
- // SFV with the longest file name has probably path info
- if (strlen($ovalue['fileName']) > strlen($tvalue['fileName'])) {
- $best = 0;
- } else {
- $best = 1;
- }
- }
- array_push($same, array($okey, $tkey, 'best' => $best));
- $toUnset = TRUE;
- } elseif ($ovalue['fileName'] === $tvalue['fileName']) {
- array_push($sameName, array($okey, $tkey));
- $toUnset = TRUE;
- }
- if ($toUnset) {
- unset($uniqueOne[$okey]);
- unset($uniqueTwo[$tkey]);
- }
- }
- }
- // *** SRS ***
- $oneSrs = getFilesByExt($filesOne, '.srs');
- $twoSrs = getFilesByExt($filesTwo, '.srs');
- // do not process these files again
- $filesOne = array_diff_assoc($filesOne, $oneSrs);
- $filesTwo = array_diff_assoc($filesTwo, $twoSrs);
- $oneSrs = addSrsInfo($oneSrs, $fhone);
- $twoSrs = addSrsInfo($twoSrs, $fhtwo);
- //print_r($oneSrs);
- //print_r($twoSrs);
- foreach ($oneSrs as $okey => $ovalue) {
- foreach ($twoSrs as $tkey => $tvalue) {
- $toUnset = FALSE;
- // sample name and crc32 must be the same to be the same sample
- if ($ovalue['fileData']->name === $tvalue['fileData']->name &&
- $ovalue['fileData']->crc32 === $tvalue['fileData']->crc32) {
- // checked against main movie file
- if ($ovalue['trackData'][1]->matchOffset === $tvalue['trackData'][1]->matchOffset) {
- // equal enough
- array_push($same, array($okey, $tkey));
- $toUnset = TRUE;
- } else {
- // -c parameter difference
- // indicate which one had the -c parameter used
- if ($ovalue['trackData'][1]->matchOffset != 0) {
- $best = 0;
- } elseif ($tvalue['trackData'][1]->matchOffset != 0) {
- $best = 1;
- } else {
- // suggest longest file name
- if (strlen($ovalue['fileName']) > strlen($tvalue['fileName'])) {
- $best = 0;
- } else {
- $best = 1;
- }
- }
- array_push($sameName, array($okey, $tkey, 'best' => $best));
- $toUnset = TRUE;
- }
- }
- if ($toUnset) {
- unset($uniqueOne[$okey]);
- unset($uniqueTwo[$tkey]);
- }
- }
- }
- // *** OTHER ***
- foreach ($filesOne as $okey => $ovalue) {
- foreach ($filesTwo as $tkey => $tvalue) {
- $toUnset = FALSE;
- // same CRC: exactly the same
- if ($ovalue['fileCrc'] === $tvalue['fileCrc']) {
- array_push($same, array($okey, $tkey));
- $toUnset = TRUE;
- // they only have the same name
- } elseif ($ovalue['fileName'] === $tvalue['fileName']) {
- array_push($sameName, array($okey, $tkey));
- $toUnset = TRUE;
- }
- if ($toUnset) {
- unset($uniqueOne[$okey]);
- unset($uniqueTwo[$tkey]);
- }
- }
- }
- $result = array(
- 'sameRarData' => $sameRarData,
- 'sameRarNames' => $sameRarNames,
- 'same' => $same,
- 'sameName' => $sameName,
- 'uniqueOne' => array_keys($uniqueOne),
- 'uniqueTwo' => array_keys($uniqueTwo),
- );
- if (!$sameRarNames) {
- // these 4 lists cover all unique RAR metadata
- $result = array_merge($result, array(
- 'uniqueRarOne' => $uniqueRarOne, // RAR files that are new
- 'uniqueRarTwo' => $uniqueRarTwo,
- 'namesRarOne' => $namesRarOne, // the RAR names that are better (when content is the same)
- 'namesRarTwo' => $namesRarTwo, // these should be picked by default when mergeing
- ));
- }
- return $result;
- }
- function getFilesByExt($fileList, $extension) {
- $result = array();
- foreach ($fileList as $key => $value) {
- if (strtolower(substr($value['fileName'], - 4)) === $extension) {
- $result[$key] = $value;
- }
- }
- return $result;
- }
- function addNfoHash($list, $fileHandle) {
- foreach($list as $key => $value) {
- // store nfo hash next to the other stored file data
- $nfoData = stream_get_contents($fileHandle, $value['fileSize'], $value['fileOffset']);
- $list[$key]['hash'] = nfoHash($nfoData);
- // check for which nfo has the fewest lines -> probably no unnessesary white lines
- // Indiana.Jones.And.The.Last.Crusade.1989.PAL.DVDR-DNA
- $list[$key]['lines'] = count(explode("\n", $nfoData));
- }
- return $list;
- }
- function addSfvInfo($list, $fileHandle) {
- foreach($list as $key => $value) {
- $result = processSfv(stream_get_contents($fileHandle, $value['fileSize'], $value['fileOffset']));
- $list[$key]['comments'] = $result['comments'];
- $list[$key]['files'] = $result['files'];
- }
- return $list;
- }
- function addSrsInfo($list, $fileHandle) {
- foreach($list as $key => $value) {
- $result = processSrsData(stream_get_contents($fileHandle, $value['fileSize'], $value['fileOffset']));
- //print_r($result);
- $list[$key] += $result;
- }
- return $list;
- }
- function nfoHash($nfoData) {
- // ignore all new lines
- $string = preg_replace("/(\r\n|\r|\n)/", '', $nfoData);
- // trailing whitespace can be stripped sometimes too
- $string = rtrim($string);
- return md5($string);
- }
- /**
- * Merge two SRR files by selecting the wanted data parts from each of them.
- * @param $one First SRR file.
- * @param $two Second SRR file.
- */
- function mergeSrr($one, $two, $storeOne, $storeTwo, $rarOne, $rarTwo, $result) {
- $rone = processSrr($one);
- $rtwo = processSrr($two);
- }
- function processSrsData(&$srsFileData) {
- // http://www.php.net/manual/en/wrappers.php.php
- // Set the limit to 5 MiB. After this limit, a temporary file will be used.
- $memoryLimit = 5 * 1024 * 1024;
- $fp = fopen("php://temp/maxmemory:$memoryLimit", 'r+');
- fputs($fp, $srsFileData);
- rewind($fp);
- $fileAttributes = fstat($fp);
- return processSrsHandle($fp, $fileAttributes['size']);
- }
- /**
- * Parses an SRS file.
- * @param $fileHandle
- * @param int $srsSize
- * @return info array
- */
- function processSrsHandle($fileHandle, $srsSize) {
- $result = null;
- switch(detectFileFormat($fileHandle)) {
- case FileType::AVI:
- $result = parse_srs_avi($fileHandle, $srsSize);
- break;
- case FileType::MKV:
- $result = parse_srs_mkv($fileHandle, $srsSize);
- break;
- case FileType::MP4:
- $result = parse_srs_mp4($fileHandle, $srsSize);
- break;
- case FileType::WMV:
- $result = parse_srs_wmv($fileHandle, $srsSize);
- break;
- case FileType::FLAC:
- $result = parse_srs_flac($fileHandle, $srsSize);
- break;
- case FileType::MP3:
- $result = parse_srs_mp3($fileHandle, $srsSize);
- break;
- case FileType::STREAM:
- $result = parse_srs_stream($fileHandle, $srsSize);
- break;
- default:
- global $CLI_APP;
- if ($CLI_APP) { // don't show the message when used as library
- echo 'SRS file type not detected';
- }
- break;
- }
- fclose($fileHandle);
- return $result;
- }
- /**
- * Sorts the stored files in $srrFile according to $sortedFileNameList.
- * @param string $srrFile: path to the SRR file
- * @param array $sortedFileNameList: simple array with file names
- * @return bool success status
- */
- function sortStoredFiles($srrFile, $sortedFileNameList) {
- $fh = fopen($srrFile, 'c+b');
-
- if (flock($fh, LOCK_EX)) {
- $srrInfo = processSrrHandle($fh);
- // not the same amount of elements: bad input
- if (count($srrInfo['storedFiles']) != count($sortedFileNameList) ||
- count(preg_grep("/^Duplicate/", $srrInfo['warnings'])) > 0) {
- flock($fh, LOCK_UN);
- fclose($fh);
- return FALSE;
- }
-
- $before = $srrInfo['srrSize'];
- $after = 0;
- // check if each name is the same in both lists
- foreach ($srrInfo['storedFiles'] as $key => $value) {
- if (array_search($key, $sortedFileNameList) === FALSE) {
- flock($fh, LOCK_UN);
- fclose($fh);
- return FALSE;
- }
-
- // offsets where the stored files start and end
- if ($value['blockOffset'] < $before) {
- $before = $value['blockOffset'];
- }
- $offset = $value['fileOffset'] + $value['fileSize'];
- if ($offset > $after) {
- $after = $offset;
- }
- }
-
- fseek($fh, 0, SEEK_SET);
- $beforeData = fread($fh, $before);
- fseek($fh, $after, SEEK_SET);
- $afterData = fread($fh, $srrInfo['srrSize']); // srrSize: the (max) amount to read
-
- // sort the files and grab their blocks
- $between = '';
- foreach ($sortedFileNameList as $key) {
- $bo = $srrInfo['storedFiles'][$key]['blockOffset'];
- $fo = $srrInfo['storedFiles'][$key]['fileOffset'];
- $fs = $srrInfo['storedFiles'][$key]['fileSize'];
- $blockSize = ($fo + $fs) - $bo;
- fseek($fh, $bo, SEEK_SET);
- $between .= fread($fh, $blockSize);
- }
-
- $bytesWritten = 0;
- fseek($fh, 0, SEEK_SET);
- $bytesWritten += fwrite($fh, $beforeData);
- $bytesWritten += fwrite($fh, $between);
- $bytesWritten += fwrite($fh, $afterData);
-
- flock($fh, LOCK_UN); // release the lock
- fclose($fh); // close the file
- return assert($bytesWritten === $srrInfo['srrSize']);
- }
-
- fclose($fh); // close the file
- return TRUE;
- }
- /**
- * Returns the data of an SRR file that only contains the SFV and
- * the RAR meta data of a certain RAR set. Capitals are ignored for $volume.
- * @param file $srrFile
- * @param string $volume
- * @param string $applicationName
- * @return string
- */
- function grabSrrSubset($srrFile, $volume, $applicationName = 'rescene.php partial SRR file') {
- $result = '';
- $fh = fopen($srrFile, 'rb');
-
- if (flock($fh, LOCK_SH)) {
- $srrInfo = processSrrHandle($fh);
- $volume = strtolower($volume);
-
- // 1) construct SRR file header
- $result = createSrrHeaderBlock($applicationName);
-
- // 2) add the right SFV file
- foreach ($srrInfo['storedFiles'] as $key => $value) {
- if (strtolower(substr($key, -4)) === '.sfv' &&
- strtolower($value['basenameVolume']) === $volume) {
- $length = $value['fileSize'] + ($value['fileOffset'] - $value['blockOffset']);
- $result .= stream_get_contents($fh, $length, $value['blockOffset']);
- }
- }
-
- // 3) add the right RAR meta data
- foreach ($srrInfo['rarFiles'] as $key => $value) {
- if (strtolower($value['basenameVolume']) === $volume) {
- $length = $value['offsetEnd'] - $value['offsetStartSrr'];
- $result .= stream_get_contents($fh, $length, $value['offsetStartSrr']);
- }
- }
-
- // 4) ignore everything else
-
- flock($fh, LOCK_UN); // release the lock
- }
-
- fclose($fh); // close the file
- return $result;
- }
- /**
- * Retrieve and parse the vobsubs languages
- * @param string $srrFile
- * @param array $srrInfo
- * @return array with languages structure
- */
- function getVobsubLanguages($srrFile, $srrInfo = null) {
- $languages = array();
- $fh = fopen($srrFile, 'rb');
- if (flock($fh, LOCK_SH)) {
- // avoid parsing SRR again when parameter is provided
- if ($srrInfo === null) {
- $srrInfo = processSrrHandle($fh);
- }
- // get a list of all stored .srr files with languages.diz in them
- $dizFiles = array();
- foreach ($srrInfo['storedFiles'] as $key => $value) {
- if (substr($key, -4) === '.srr') {
- $storedSrr = stream_get_contents($fh, $value['fileSize'], $value['fileOffset']);
- if (strpos($storedSrr, 'languages.diz') !== FALSE) {
- $vobsubSrr = processSrrData($storedSrr);
- $lv = $vobsubSrr['storedFiles']['languages.diz']; // can fail in theory
- $dizFiles[$key] = substr($storedSrr, $lv['fileOffset'], $lv['fileSize']);
- }
- }
- }
- flock($fh, LOCK_UN); // release the lock
- foreach ($dizFiles as $srrVobsubName => $dizData) {
- $languages[$srrVobsubName] = parseLanguagesDiz($dizData);
- }
- }
- fclose($fh); // close the file
- return $languages;
- }
- // Private helper functions -------------------------------------------------------------------------------------------
- function parseLanguagesDiz($data) {
- $idx = array();
- $lastFileName = 'NO IDX FILE NAME DETECTED';
- $lines = preg_split('/$\R?^/m', $data); // early .srr files are not just \n
- foreach ($lines as $line) {
- if (substr($line, 0, 1) === '#') {
- // new idx file
- $lastFileName = substr($line, 2);
- $idx[$lastFileName] = array();
- } else {
- // new language line
- preg_match('/id: ([a-z]{2}).*?,.*/i', $line, $matches);
- if ($matches) { // '--' gets skipped e.g. Zero.Days.2016.PROPER.DVDRip.x264-WiDE
- array_push($idx[$lastFileName], $matches[1]);
- }
- }
- }
- return $idx;
- }
- /**
- * No locking occurs.
- * @param unknown_type $fileHandle
- * @return integer The size of the file.
- */
- function getFileSizeHandle($fileHandle) {
- // PHP uses caching for filesize() and we do not always want that!
- $stat…
Large files files are truncated, but you can click here to view the full file