@@ -27,12 +27,12 @@ before_install: | |||
install: | |||
- composer install --ansi --prefer-dist --no-interaction --optimize-autoloader --no-suggest --no-progress | |||
- pip3 install --user chardet xld_logchecker eac_logchecker | |||
script: | |||
- bin/logchecker --version | |||
- bin/logchecker --help | |||
- bin/logchecker tests/logs/wgdbcm.log | |||
- bin/logchecker tests/logs/xld_perfect.log | |||
- bin/logchecker analyze tests/logs/wgdbcm.log | |||
- bin/logchecker analyze tests/logs/xld_perfect.log | |||
before_deploy: | |||
- php -d phar.readonly=0 bin/compile | |||
@@ -46,4 +46,4 @@ deploy: | |||
on: | |||
tags: true | |||
repo: OPSnet/Logchecker | |||
php: '7.2' | |||
php: '7.2' |
@@ -1,7 +1,7 @@ | |||
{ | |||
"name": "orpheusnet/logchecker", | |||
"description": "Logchecker for validating logs generated from supported ripping programs (like EAC and XLD)", | |||
"version": "0.8.6", | |||
"version": "0.9.0", | |||
"license": "Unlicense", | |||
"type": "library", | |||
"authors": [ | |||
@@ -3,12 +3,11 @@ | |||
namespace OrpheusNET\Logchecker; | |||
use OrpheusNET\Logchecker\Exception\FileNotFoundException; | |||
use Symfony\Component\Process\Exception\ProcessFailedException; | |||
use Symfony\Component\Process\Process; | |||
class Chardet | |||
{ | |||
private $executable = null; | |||
private static $executable = null; | |||
private $executables = [ | |||
'chardet', | |||
'chardetect' | |||
@@ -16,15 +15,17 @@ class Chardet | |||
public function __construct() | |||
{ | |||
foreach ($this->executables as $executable) { | |||
if (Util::commandExists($executable)) { | |||
$this->executable = $executable; | |||
break; | |||
if (static::$executable === null) { | |||
foreach ($this->executables as $executable) { | |||
if (Util::commandExists($executable)) { | |||
static::$executable = $executable; | |||
break; | |||
} | |||
} | |||
} | |||
if ($this->executable === null) { | |||
throw new \RuntimeException('chardet not installed'); | |||
if (static::$executable === null) { | |||
throw new \RuntimeException('chardet not installed'); | |||
} | |||
} | |||
} | |||
@@ -35,7 +36,7 @@ class Chardet | |||
throw new FileNotFoundException($filename); | |||
} | |||
$process = new Process([$this->executable, $filename]); | |||
$process = new Process([static::$executable, $filename]); | |||
$process->run(); | |||
// Following regex: | |||
@@ -1,6 +1,6 @@ | |||
<?php | |||
namespace OrpheusNET\Logchecker\Checks; | |||
namespace OrpheusNET\Logchecker\Check; | |||
use OrpheusNET\Logchecker\Util; | |||
use Symfony\Component\Process\Process; |
@@ -1,6 +1,6 @@ | |||
<?php | |||
namespace OrpheusNET\Logchecker\Checks; | |||
namespace OrpheusNET\Logchecker\Check; | |||
class ChecksumStates | |||
{ |
@@ -2,7 +2,7 @@ | |||
declare(strict_types=1); | |||
namespace OrpheusNET\Logchecker\Checks; | |||
namespace OrpheusNET\Logchecker\Check; | |||
use OrpheusNET\Logchecker\Exception\UnknownRipperException; | |||
@@ -21,10 +21,13 @@ class Ripper | |||
return Ripper::XLD; | |||
} elseif (strpos($log, "Exact Audio Copy") !== false) { | |||
return Ripper::EAC; | |||
} else if (strpos($log, "EAC") === 0) { | |||
return Ripper::EAC; | |||
} else { | |||
throw new UnknownRipperException("Could not determine ripper"); | |||
$firstLine = strstr($log, "\n", true); | |||
if ($firstLine !== false && strpos($firstLine, "EAC") !== false) { | |||
return Ripper::EAC; | |||
} else { | |||
throw new UnknownRipperException("Could not determine ripper"); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
<?php | |||
namespace OrpheusNET\Logchecker\Command; | |||
use OrpheusNET\Logchecker\Util; | |||
use Symfony\Component\Console\Command\Command; | |||
use Symfony\Component\Console\Input\InputArgument; | |||
use Symfony\Component\Console\Input\InputInterface; | |||
use Symfony\Component\Console\Output\OutputInterface; | |||
class DecodeCommand extends Command | |||
{ | |||
protected function configure() | |||
{ | |||
$this | |||
->setName('decode') | |||
->setDescription('Decodes log from whatever encoding into UTF-8') | |||
->setHelp(<<<HELP | |||
This command decodes a log from whatever encoding into UTF-8. | |||
XLD and Whipper generates logs that are in UTF-8, while EAC uses UTF-16. However, older | |||
EAC logs will often be in a smattering of different encoding (most popular is CP-1251, which | |||
is a Cyrillic code page), which are in-compatible with UTF-8 based analysis, and so require | |||
decoding first. Due to the difficulty of this problem, we use chardet (if installed) to give | |||
us the encoding if we cannot detect it via a BOM. | |||
If no [out_file] is specified, the decoded log will be printed to stdout. | |||
HELP | |||
) | |||
->addArgument('file', InputArgument::REQUIRED, 'Log file to decode') | |||
->addArgument('out_file', InputArgument::OPTIONAL, 'File to write decoded log file to'); | |||
} | |||
protected function execute(InputInterface $input, OutputInterface $output) | |||
{ | |||
$filename = $input->getArgument('file'); | |||
if (!file_exists($filename)) { | |||
$output->writeln("Invalid file"); | |||
return 1; | |||
} | |||
$log = file_get_contents($filename); | |||
$log = Util::decodeEncoding($log, $filename); | |||
if ($input->getArgument('out_file')) { | |||
file_put_contents($input->getArgument('out_file'), $log); | |||
} | |||
return 0; | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
<?php | |||
namespace OrpheusNET\Logchecker\Command; | |||
use OrpheusNET\Logchecker\Parser\EAC\Translator; | |||
use Symfony\Component\Console\Command\Command; | |||
use Symfony\Component\Console\Input\InputArgument; | |||
use Symfony\Component\Console\Input\InputInterface; | |||
use Symfony\Component\Console\Input\InputOption; | |||
use Symfony\Component\Console\Output\OutputInterface; | |||
class TranslateCommand extends Command | |||
{ | |||
protected function configure() | |||
{ | |||
$this | |||
->setName('translate') | |||
->setDescription('Translates a log into english') | |||
->setHelp("Translates a log into english") | |||
->addOption('language', 'l', InputOption::VALUE_OPTIONAL, 'Force language to use') | |||
->addArgument('file', InputArgument::REQUIRED, 'Log file to decode') | |||
->addArgument('out_file', InputArgument::OPTIONAL, 'File to write decoded log file to'); | |||
} | |||
protected function execute(InputInterface $input, OutputInterface $output) | |||
{ | |||
$filename = $input->getArgument('file'); | |||
if (!file_exists($filename)) { | |||
$output->writeln("Invalid file"); | |||
return 1; | |||
} | |||
$log = file_get_contents($filename); | |||
if ($input->getOption('language')) { | |||
$code = $input->getOption('language'); | |||
$output->writeln("Translating from {$code} to English"); | |||
} else { | |||
$language = Translator::getLanguage($log); | |||
$code = $language['code']; | |||
$output->writeln("Translating from {$language['name']} ({$language['name_english']}) to English"); | |||
} | |||
$log = Translator::translate($log, $language['code']); | |||
if ($input->getArgument('out_file')) { | |||
file_put_contents($input->getArgument('out_file'), $log); | |||
} else { | |||
$output->write($log); | |||
} | |||
return 0; | |||
} | |||
} |
@@ -2,7 +2,7 @@ | |||
namespace OrpheusNET\Logchecker; | |||
use OrpheusNET\Logchecker\Checks\Ripper; | |||
use OrpheusNET\Logchecker\Check\Ripper; | |||
use OrpheusNET\Logchecker\Parser\EAC\Translator; | |||
use Symfony\Component\Yaml\Yaml; | |||
use Symfony\Component\Yaml\Exception\ParseException; | |||
@@ -17,7 +17,7 @@ class Logchecker | |||
private $logPath = null; | |||
private $logs = array(); | |||
private $Tracks = array(); | |||
private $checksumStatus = Checks\ChecksumStates::CHECKSUM_OK; | |||
private $checksumStatus = Check\ChecksumStates::CHECKSUM_OK; | |||
private $Score = 100; | |||
private $Details = array(); | |||
private $Offsets = array(); | |||
@@ -37,7 +37,6 @@ class Logchecker | |||
private $Range = null; | |||
private $ARSummary = null; | |||
private $XLDSecureRipper = false; | |||
private $Chardet = null; | |||
private $FakeDrives = [ | |||
'Generic DVD-ROM SCSI CdRom Device' | |||
]; | |||
@@ -46,13 +45,6 @@ class Logchecker | |||
public function __construct() | |||
{ | |||
try { | |||
$this->Chardet = new Chardet(); | |||
} catch (\Exception $exc) { | |||
// Could not find chardet | |||
$this->Chardet = null; | |||
} | |||
$this->AllDrives = array_map(function ($elem) { | |||
return explode(',', $elem); | |||
}, file(__DIR__ . '/offsets.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)); | |||
@@ -78,7 +70,7 @@ class Logchecker | |||
$this->logPath = null; | |||
$this->logs = array(); | |||
$this->Tracks = array(); | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_OK; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_OK; | |||
$this->Score = 100; | |||
$this->Details = array(); | |||
$this->Offsets = array(); | |||
@@ -104,31 +96,6 @@ class Logchecker | |||
$this->ValidateChecksum = $Bool; | |||
} | |||
private function convertEncoding() | |||
{ | |||
// Whipper uses UTF-8 so we don't need to bother checking, especially as it's | |||
// possible a log may be falsely detected as a different encoding by chardet | |||
if (strpos($this->log, "Log created by: whipper") !== false) { | |||
return; | |||
} | |||
// To parse the log, we want to deal with the log in UTF-8. EAC by default should | |||
// always output to UTF-16 and XLD to UTF-8, but sometimes people view the log and | |||
// re-encode them to something else (like Windows-1251), and we need to use chardet | |||
// to detect this so we can then convert it to UTF-8. | |||
if (ord($this->log[0]) . ord($this->log[1]) == 0xFF . 0xFE) { | |||
$this->log = mb_convert_encoding(substr($this->log, 2), 'UTF-8', 'UTF-16LE'); | |||
} elseif (ord($this->log[0]) . ord($this->log[1]) == 0xFE . 0xFF) { | |||
$this->log = mb_convert_encoding(substr($this->log, 2), 'UTF-8', 'UTF-16BE'); | |||
} elseif (ord($this->log[0]) == 0xEF && ord($this->log[1]) == 0xBB && ord($this->log[2]) == 0xBF) { | |||
$this->log = substr($this->log, 3); | |||
} elseif ($this->Chardet !== null) { | |||
$Results = $this->Chardet->analyze($this->logPath); | |||
if ($Results['charset'] !== 'utf-8' && $Results['confidence'] > 0.7) { | |||
$this->log = mb_convert_encoding($this->log, 'UTF-8', $Results['charset']); | |||
} | |||
} | |||
} | |||
/** | |||
* @return array Returns an array that contains [Score, Details, Checksum, Log] | |||
*/ | |||
@@ -136,7 +103,7 @@ class Logchecker | |||
{ | |||
try { | |||
$this->convertEncoding(); | |||
$this->log = Util::decodeEncoding($this->log, $this->logPath); | |||
} catch (\Exception $exc) { | |||
$this->Score = 0; | |||
$this->account('Could not detect log encoding, log is corrupt.'); | |||
@@ -186,7 +153,7 @@ class Logchecker | |||
$Class = $this->checksumStatus ? 'good' : 'bad'; | |||
$Yaml['SHA-256 hash'] = "<span class='{$Class}'>{$Hash}</span>"; | |||
} else { | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_MISSING; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_MISSING; | |||
} | |||
$RippingKey = 'Ripping phase information'; | |||
@@ -356,7 +323,7 @@ class Logchecker | |||
PREG_SPLIT_DELIM_CAPTURE | |||
); | |||
} else { //no checksum | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_MISSING; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_MISSING; | |||
$this->logs = preg_split("/(\nEnd of status report)/i", $this->log, -1, PREG_SPLIT_DELIM_CAPTURE); | |||
foreach ($this->logs as $Key => $Value) { | |||
if (preg_match("/---- CUETools DB Plugin V.+/i", $Value)) { | |||
@@ -370,7 +337,7 @@ class Logchecker | |||
if ($Log === "" || preg_match('/^\-+$/i', $Log)) { | |||
unset($this->logs[$Key]); | |||
} elseif ( | |||
$this->checksumStatus !== Checks\ChecksumStates::CHECKSUM_OK | |||
$this->checksumStatus !== Check\ChecksumStates::CHECKSUM_OK | |||
&& preg_match("/End of status report/i", $Log) | |||
) { | |||
//strip empty | |||
@@ -378,13 +345,13 @@ class Logchecker | |||
$this->logs[$Key - 1] .= $Log; | |||
unset($this->logs[$Key]); | |||
} elseif ( | |||
$this->checksumStatus === Checks\ChecksumStates::CHECKSUM_OK | |||
$this->checksumStatus === Check\ChecksumStates::CHECKSUM_OK | |||
&& preg_match("/[\=]+\s+Log checksum/i", $Log) | |||
) { | |||
$this->logs[$Key - 1] .= $Log; | |||
unset($this->logs[$Key]); | |||
} elseif ( | |||
$this->checksumStatus === Checks\ChecksumStates::CHECKSUM_OK | |||
$this->checksumStatus === Check\ChecksumStates::CHECKSUM_OK | |||
&& preg_match("/[\-]+BEGIN XLD SIGNATURE/i", $Log) | |||
) { | |||
$this->logs[$Key - 1] .= $Log; | |||
@@ -404,7 +371,7 @@ class Logchecker | |||
if ($Matches[1]) { | |||
$this->version = floatval(explode(" ", substr($Matches[1], 1))[0]); | |||
if ($this->version < 1) { | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_MISSING; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_MISSING; | |||
if ($this->version <= 0.95) { | |||
# EAC 0.95 and before was missing a handful of stuff for full log validation | |||
# that 0.99 included (-30 points) | |||
@@ -412,14 +379,14 @@ class Logchecker | |||
} | |||
} else { | |||
// Above version 1 and no checksum | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_MISSING; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_MISSING; | |||
} | |||
} else { | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_MISSING; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_MISSING; | |||
$this->account("EAC version older than 0.99", 30); | |||
} | |||
} elseif (preg_match('/EAC extraction logfile from/i', $Log)) { | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_MISSING; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_MISSING; | |||
$this->account("EAC version older than 0.99", 30); | |||
} | |||
@@ -433,7 +400,7 @@ class Logchecker | |||
if (preg_match('/X Lossless Decoder version (\d+) \((.+)\)/i', $Log, $Matches)) { //xld version & checksum | |||
$this->version = $Matches[1]; | |||
if ($this->version >= 20121222 && !$Count) { | |||
$this->checksumStatus = Checks\ChecksumStates::CHECKSUM_MISSING; | |||
$this->checksumStatus = Check\ChecksumStates::CHECKSUM_MISSING; | |||
//$this->account('No checksum with XLD 20121222 or newer', 15); | |||
} | |||
} | |||
@@ -482,11 +449,11 @@ class Logchecker | |||
if ( | |||
$this->ValidateChecksum | |||
&& $this->checksumStatus == Checks\ChecksumStates::CHECKSUM_OK | |||
&& $this->checksumStatus == Check\ChecksumStates::CHECKSUM_OK | |||
&& !empty($this->logPath) | |||
) { | |||
if (Checks\Checksum::logcheckerExists($EAC)) { | |||
$this->checksumStatus = Checks\Checksum::validate($this->logPath, $EAC); | |||
if (Check\Checksum::logcheckerExists($EAC)) { | |||
$this->checksumStatus = Check\Checksum::validate($this->logPath, $EAC); | |||
} else { | |||
$this->account( | |||
"Could not find {$this->ripper} logchecker, checksum not validated.", | |||
@@ -16,9 +16,11 @@ class LogcheckerConsole extends Application | |||
$analyze_command = new Command\AnalyzeCommand(); | |||
$this->addCommands([ | |||
$analyze_command | |||
$analyze_command, | |||
new Command\DecodeCommand(), | |||
new Command\TranslateCommand() | |||
]); | |||
$this->setDefaultCommand($analyze_command->getName(), true); | |||
//$this->setDefaultCommand($analyze_command->getName(), false); | |||
} | |||
} |
@@ -0,0 +1,171 @@ | |||
{ | |||
"31": "Filename will be ignored", | |||
"50": "Value out of range !", | |||
"51": "Invalid characters !", | |||
"52": "Invalid filename !", | |||
"1200": "Status and Error Messages", | |||
"2501": "Track status and errors", | |||
"1203": "Possible Errors", | |||
"1204": "Create Log", | |||
"1210": "Stato intervallo ed errori", | |||
"1211": "Intervallo selezionato", | |||
"1212": " Timing problem ", | |||
"1213": " Suspicious position ", | |||
"1214": " Missing samples", | |||
"1215": " Too many samples", | |||
"1216": " File write error", | |||
"1217": " Livello di picco ", | |||
"1299": " Extraction speed ", | |||
"1218": " Qualità intervallo ", | |||
"1219": " CRC ", | |||
"1220": " Copia corretta", | |||
"1221": " Copy finished", | |||
"1227": " Track quality ", | |||
"1228": " Copy aborted", | |||
"1269": " Nome file ", | |||
"1270": " Pre-gap length ", | |||
"1271": " Test CRC ", | |||
"1272": " Copy CRC ", | |||
"1273": " Compressing", | |||
"1280": " Track not fully ripped for AccurateRip lookup", | |||
"1281": " Accurately ripped (confidence ", | |||
"1282": " Not accurately ripped (confidence ", | |||
"1283": " Track not present in AccurateRip database", | |||
"1330": " Cannot be verified as accurate", | |||
"1337": "cannot be verified as accurate", | |||
"1331": ", AccurateRip returned", | |||
"1332": "(confidence ", | |||
"1333": "No tracks could be verified as accurate", | |||
"1334": "You may have a different pressing from the one(s) in the database", | |||
"1335": "Some tracks could not be verified as accurate", | |||
"1336": "All tracks accurately ripped", | |||
"1338": "Null samples used in CRC calculations", | |||
"1339": "track(s) not present in the AccurateRip database", | |||
"1340": "track(s) accurately ripped", | |||
"1341": "track(s) could not be verified as accurate", | |||
"1342": "track(s) not fully ripped for AccurateRip lookup", | |||
"1343": "track(s) canceled", | |||
"1344": "None of the tracks are present in the AccurateRip database", | |||
"1284": "Not all tracks ripped accurately", | |||
"1222": "Non soNo stati riscontrati errori", | |||
"1223": "Review Range", | |||
"1224": "There were errors", | |||
"1225": "Fine del resoconto di stato", | |||
"1321": "Not detected, thus appended to previous track", | |||
"1322": "Appended to previous track", | |||
"1323": "Appended to next track", | |||
"1325": "Log checksum", | |||
"1226": "Track", | |||
"1230": "Index", | |||
"1229": "Review Tracks", | |||
"1274": "Estrazione file di log EAC da ", | |||
"1240": "January", | |||
"1241": "February", | |||
"1242": "March", | |||
"1243": "April", | |||
"1244": "May", | |||
"1245": "June", | |||
"1246": "July", | |||
"1247": "August", | |||
"1248": "September", | |||
"1249": "October", | |||
"1250": "November", | |||
"1251": "December", | |||
"1232": "EAC extraction log file", | |||
"1233": "Unità predefinita: ", | |||
"1234": "Modalità di lettura", | |||
"1235": "Burst", | |||
"1236": "Fast", | |||
"1237": "Paranoid", | |||
"1238": "Secure with NO C2, accurate stream, NO disable cache", | |||
"1239": "Secure with NO C2, NO accurate stream, disable cache", | |||
"1252": "Secure with C2, accurate stream, NO disable cache", | |||
"1253": "Secure with C2, accurate stream, disable cache", | |||
"1254": "Secure with NO C2, accurate stream, disable cache", | |||
"1255": "Combined read/write offset correction", | |||
"1256": "Correzione offset di lettura", | |||
"1257": "Sovrascrivi anche nel Lead-In e Lead-Out", | |||
"1258": "Formato di destinazione scelto", | |||
"1259": "Additional command line options", | |||
"1260": "Routine interne WAV", | |||
"1261": "44.100 Hz; 16 Bit; Stereo", | |||
"1262": "Use compression offset", | |||
"1263": "Altre opzioni : ", | |||
"1264": "Riempi sample offset mancanti con silenzio", | |||
"1265": "Rimuovi blocchi di silezio ad inizio e fine", | |||
"1266": "Normalize to", | |||
"13303": "Native Win32 interface for XP/Vista/Win 7", | |||
"1267": "Native Win32 interface for Win NT & 2000", | |||
"1268": "Interfaccia esterna ASPI (installata)", | |||
"1275": "AccurateRip summary", | |||
"1276": "not ripped completely", | |||
"1277": "accurately ripped (confidence ", | |||
"1278": "not ripped accurately (confidence ", | |||
"1279": "not present in database", | |||
"1285": ", but should be", | |||
"1286": "Performing a test extraction only", | |||
"1287": "Sectors", | |||
"1288": "Exact Audio Copy", | |||
"1289": "TOC of the extracted CD", | |||
"1290": "Track", | |||
"1291": "Start", | |||
"1292": "Length", | |||
"1293": "Start sector", | |||
"1294": "End sector", | |||
"1295": "Secure", | |||
"1296": "Make use of C2 pointers", | |||
"1297": "Utilize accurate stream", | |||
"1298": "Defeat audio cache", | |||
"1305": "Used interface", | |||
"1306": "Command line compressor", | |||
"1307": "Selected bitrate", | |||
"1308": "Quality", | |||
"1328": "High", | |||
"1329": "Low", | |||
"1309": "Add ID3 tag", | |||
"1310": "Sample format", | |||
"1320": "Gap handling", | |||
"1324": "Left out", | |||
"81700": "L3Enc MP3 Encoder & Compatible", | |||
"81701": "Fraunhofer MP3Enc MP3 Encoder", | |||
"81702": "Xing X3Enc MP3 Encoder", | |||
"81703": "Xing ToMPG MP3 Encoder", | |||
"81704": "LAME MP3 Encoder", | |||
"81705": "GOGO MP3 Encoder", | |||
"81706": "MPC Encoder", | |||
"81707": "Ogg Vorbis Encoder", | |||
"81708": "Microsoft WMA9 Encoder", | |||
"81709": "FAAC AAC Encoder", | |||
"81710": "Homeboy AAC Encoder", | |||
"81711": "Quartex AAC Encoder", | |||
"81712": "PsyTEL AAC Encoder", | |||
"81713": "MBSoft AAC Encoder", | |||
"81714": "Yamaha VQF Encoder", | |||
"81715": "Real Audio Encoder", | |||
"81716": "Monkey's Audio Lossless Encoder", | |||
"81717": "Shorten Lossless Encoder", | |||
"81718": "RKAU Lossless Encoder", | |||
"81719": "LPAC Lossless Encoder", | |||
"81720": "User Defined Encoder", | |||
"1000000": "per CD", | |||
"4270": "Low", | |||
"4271": "Medium", | |||
"4272": "High", | |||
"1": "Corsu", | |||
"2": "Corsican", | |||
"5": "Error Message", | |||
"6": "Warning", | |||
"7": "Success", | |||
"8": "Information", | |||
"10": "OK", | |||
"11": "Cancel", | |||
"12": "Apply", | |||
"15": "Yes", | |||
"16": "No" | |||
} |
@@ -6,6 +6,13 @@ | |||
"name": "Български", | |||
"name_english": "Bulgarian" | |||
}, | |||
"co": { | |||
"eac_strings": [ | |||
"Estrazione file di log EAC da" | |||
], | |||
"name": "Corsu", | |||
"name_english": "Corsican" | |||
}, | |||
"cs": { | |||
"eac_strings": [ | |||
"Protokol extrakce EAC z " | |||
@@ -11,4 +11,36 @@ class Util | |||
exec("{$where} {$cmd} 2>/dev/null", $output, $return_var); | |||
return $return_var === 0; | |||
} | |||
public static function decodeEncoding(string $log, string $logPath): string | |||
{ | |||
try { | |||
$chardet = new Chardet(); | |||
} catch (\RuntimeException $exc) { | |||
$chardet = null; | |||
} | |||
// Whipper uses UTF-8 so we don't need to bother checking, especially as it's | |||
// possible a log may be falsely detected as a different encoding by chardet | |||
if (strpos($log, "Log created by: whipper") !== false) { | |||
return $log; | |||
} | |||
// To parse the log, we want to deal with the log in UTF-8. EAC by default should | |||
// always output to UTF-16 and XLD to UTF-8, but sometimes people view the log and | |||
// re-encode them to something else (like Windows-1251), and we need to use chardet | |||
// to detect this so we can then convert it to UTF-8. | |||
if (ord($log[0]) . ord($log[1]) == 0xFF . 0xFE) { | |||
$log = mb_convert_encoding(substr($log, 2), 'UTF-8', 'UTF-16LE'); | |||
} elseif (ord($log[0]) . ord($log[1]) == 0xFE . 0xFF) { | |||
$log = mb_convert_encoding(substr($log, 2), 'UTF-8', 'UTF-16BE'); | |||
} elseif (ord($log[0]) == 0xEF && ord($log[1]) == 0xBB && ord($log[2]) == 0xBF) { | |||
$log = substr($log, 3); | |||
} elseif ($chardet !== null) { | |||
$Results = $chardet->analyze($logPath); | |||
if ($Results['charset'] !== 'utf-8' && $Results['confidence'] > 0.7) { | |||
$log = mb_convert_encoding($log, 'UTF-8', $Results['charset']); | |||
} | |||
} | |||
return $log; | |||
} | |||
} |
@@ -2,10 +2,10 @@ | |||
declare(strict_types=1); | |||
namespace OrpheusNET\Logchecker\Checks; | |||
namespace OrpheusNET\Logchecker\Check; | |||
use PHPUnit\Framework\TestCase; | |||
use OrpheusNET\Logchecker\Checks\Ripper; | |||
use OrpheusNET\Logchecker\Check\Ripper; | |||
use OrpheusNET\Logchecker\Exception\UnknownRipperException; | |||
class RipperTest extends TestCase | |||
@@ -18,7 +18,12 @@ class RipperTest extends TestCase | |||
Ripper::EAC | |||
], | |||
[ | |||
"EAC 展開 ログファイル 日付: 24. 12月 2005, 18:37 for CD", | |||
"EAC 展開 ログファイル 日付: 24. 12月 2005, 18:37 for CD\nTest", | |||
Ripper::EAC | |||
], | |||
[ | |||
"Отчёт EAC об извлечении, выполненном 15. января 2010, 16:06 для диска:\n" . | |||
"Girls Against Boys / Cruise Yourself", | |||
Ripper::EAC | |||
], | |||
[ | |||
@@ -13,7 +13,7 @@ class TranslatorTest extends TestCase | |||
public function foreignLogDataProvider() | |||
{ | |||
$logs = []; | |||
$logPath = implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..', 'logs', 'transcoded_foreign_logs']); | |||
$logPath = implode(DIRECTORY_SEPARATOR, [__DIR__, '..', '..', 'logs', 'eac', 'transcoded_logs']); | |||
foreach (new FilesystemIterator($logPath, FilesystemIterator::SKIP_DOTS) as $dir) { | |||
if ($dir->isFile()) { | |||
continue; | |||
@@ -0,0 +1,29 @@ | |||
Estrazione file di log EAC da 13. Giugno 2006, 12:34 per CD | |||
Cousteau / Cousteau | |||
Unità predefinita: HL-DT-STCD-RW GCE-8480B Adapter: 2 ID: 1 | |||
Modalità di lettura: Sicuro con NO C2, Lettura Accurata, Disattiva Cache | |||
Correzione offset di lettura:6 | |||
Sovrascrivi anche nel Lead-In e Lead-Out : No | |||
Formato di destinazione scelto: Routine interne WAV | |||
44.100 Hz; 16 Bit; Stereo | |||
Altre opzioni : | |||
Riempi sample offset mancanti con silenzio : Sì | |||
Rimuovi blocchi di silezio ad inizio e fine : No | |||
Interfaccia esterna ASPI (installata) | |||
Stato intervallo ed errori | |||
Intervallo selezionato | |||
Nome file C:\Documents and Settings\maurizio\Desktop\Cousteau - Cousteau.wav | |||
Livello di picco 99.9 % | |||
Qualità intervallo 100.0 % | |||
CRC 5B545F17 | |||
Copia corretta | |||
Non sono stati riscontrati errori | |||
Fine del resoconto di stato |
@@ -0,0 +1,29 @@ | |||
EAC extraction logfile from 13. GiugNo 2006, 12:34 for disc | |||
Cousteau / Cousteau | |||
Used drive : HL-DT-STCD-RW GCE-8480B Adapter: 2 ID: 1 | |||
Read mode: Sicuro con No C2, Lettura Accurata, Disattiva Cache | |||
Read offset correction:6 | |||
Overread into Lead-In and Lead-Out : No | |||
Used output format: Internal WAV Routines | |||
44.100 Hz; 16 Bit; Stereo | |||
Other options : | |||
Fill up missing offset samples with silence : Sì | |||
Delete leading and trailing silent blocks : No | |||
Installed external ASPI interface | |||
Range status and errors | |||
Selected range | |||
Filename C:\Documents and Settings\maurizio\Desktop\Cousteau - Cousteau.wav | |||
Peak level 99.9 % | |||
Range Quality 100.0 % | |||
CRC 5B545F17 | |||
Copy OK | |||
No errors occurred | |||
End of status report |