<commit_before><?php
/**
 * This file is part of the arhitector/jumper library.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @author    <NAME> <<EMAIL>>
 *
 * @license   http://opensource.org/licenses/MIT MIT
 * @copyright Copyright (c) 2017 <NAME> <<EMAIL>>
 */
namespace Arhitector\Jumper;

use Arhitector\Jumper\Exception\InvalidFilterException;
use Arhitector\Jumper\Exception\TranscoderException;
use Arhitector\Jumper\Filter\AudioFilterInterface;
use Arhitector\Jumper\Filter\Collection as FiltersCollection;
use Arhitector\Jumper\Filter\FilterInterface;
use Arhitector\Jumper\Format\AudioFormat;
use Arhitector\Jumper\Format\AudioFormatInterface;
use Arhitector\Jumper\Format\FormatInterface;
use Arhitector\Jumper\Service\ServiceFactory;
use Arhitector\Jumper\Service\ServiceFactoryInterface;
use Arhitector\Jumper\Stream\AudioStream;
use Arhitector\Jumper\Stream\Collection;
use Arhitector\Jumper\Stream\FrameStream;
use Symfony\Component\Process\Exception\ProcessFailedException;

/**
 * Class Audio.
 *
 * @package Arhitector\Jumper
 */
class Audio implements AudioInterface
{
	use Traits\FilePathAwareTrait, TranscoderTrait {
		TranscoderTrait::getFormat as private _getFormat;
	}
	
	/**
	 * @var \Arhitector\Jumper\Filter\Collection List of filters.
	 */
	protected $filters;
	
	/**
	 * Audio constructor.
	 *
	 * @param string                  $filePath
	 * @param ServiceFactoryInterface $service
	 *
	 * @throws \Arhitector\Jumper\Exception\TranscoderException
	 * @throws \InvalidArgumentException
	 */
	public function __construct($filePath, ServiceFactoryInterface $service = null)
	{
		$this->setFilePath($filePath);
		$this->setService($service ?: new ServiceFactory());
		
		/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
		$demuxing = $this->getService()->getDecoderService()->demuxing($this);
		
		if (count($demuxing->streams) < 1 || ( ! $this->isSupportedFileType() && empty($demuxing->format['format'])))
		{
			throw new TranscoderException('File type unsupported or the file is corrupted.');
		}
		
		/** @var AudioFormat $className */
		$className = $this->findFormatClass($demuxing->format['format']) ?: AudioFormat::class;
		$this->format = $className::fromArray(array_filter($demuxing->format, function ($value) {
			return $value !== null;
		}));
		
		$this->streams = new Collection(array_map(function ($parameters) {
			if ($parameters['type'] == 'audio')
			{
				$stream = AudioStream::create($this, $parameters);
				
				if ($stream->getChannels() !== null)
				{
					$this->getFormat()->setChannels($stream->getChannels());
				}
				
				if ($stream->getFrequency() !== null)
				{
					$this->getFormat()->setFrequency($stream->getFrequency());
				}
				
				$this->getFormat()->setAudioBitrate($stream->getBitrate());
				$this->getFormat()->setAudioCodec($stream->getCodec());
				
				return $stream;
			}
			
			if ($parameters['type'] == 'video')
			{
				return FrameStream::create($this, $parameters);
			}
			
			throw new TranscoderException('This stream unsupported.');
		}, $demuxing->streams));
		
		$this->filters = new FiltersCollection();
	}
	
	/**
	 * Gets the audio channels value.
	 *
	 * @return int
	 */
	public function getAudioChannels()
	{
		return $this->getFormat()->getChannels();
	}
	
	/**
	 * Gets the audio kilo bitrate value.
	 *
	 * @return int
	 */
	public function getAudioKiloBitrate()
	{
		return (int) ($this->getFormat()->getAudioBitrate() / 1000);
	}
	
	/**
	 * Returns the audio codec.
	 *
	 * @return Codec|null
	 */
	public function getAudioCodec()
	{
		return $this->getFormat()->getAudioCodec();
	}
	
	/**
	 * Get sample frequency value.
	 *
	 * @return int
	 */
	public function getFrequency()
	{
		return $this->getFormat()->getFrequency();
	}
	
	/**
	 * Get duration value.
	 *
	 * @return float
	 */
	public function getDuration()
	{
		return $this->getFormat()->getDuration();
	}
	
	/**
	 * Get current format.
	 *
	 * @return AudioFormatInterface|\Arhitector\Jumper\Format\FormatInterface
	 */
	public function getFormat()
	{
		return $this->_getFormat();
	}
	
	/**
	 * Transcoding.
	 *
	 * @param FormatInterface $format
	 * @param string          $filePath
	 * @param bool            $overwrite
	 *
	 * @return float|int Size of the new file or -1
	 * @throws \Symfony\Component\Process\Exception\RuntimeException
	 * @throws \Symfony\Component\Process\Exception\LogicException
	 * @throws \Symfony\Component\Process\Exception\ProcessFailedException
	 * @throws \Arhitector\Jumper\Exception\TranscoderException
	 * @throws \InvalidArgumentException
	 */
	public function save(FormatInterface $format, $filePath, $overwrite = true)
	{
		if ( ! is_string($filePath) || empty($filePath))
		{
			throw new \InvalidArgumentException('File path must not be an empty string.');
		}
	
		if ( ! $overwrite && file_exists($filePath))
		{
			throw new TranscoderException('File path already exists.');
		}
		
		$options = ['output' => $filePath];
		
		foreach (clone $this->filters as $filter)
		{
			$options = array_merge($options, $filter->apply($this, $format));
		}
	
		/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
		$processes = $this->getService()->getEncoderService()->transcoding($this, $format, [
			'output' => $filePath
		]);
		
		foreach ($processes as $process)
		{
			$process->start();
			
			if ($process->wait() !== 0)
			{
				throw new ProcessFailedException($process);
			}
		}
		
		return filesize($filePath);
	}
	
	/**
	 * Add a new filter.
	 *
	 * @param FilterInterface $filter
	 * @param int             $priority range 0-99.
	 *
	 * @return TranscoderInterface
	 * @throws \RangeException
	 * @throws InvalidFilterException
	 */
	public function addFilter(FilterInterface $filter, $priority = 0)
	{
		if ( ! $filter instanceof AudioFilterInterface)
		{
			throw new InvalidFilterException('Filter type is not supported.');
		}
		
		if ($priority > 99)
		{
			throw new \RangeException('Priority should be in the range from 0 to 99.');
		}
		
		$this->filters->insert($filter, $priority);
		
		return $this;
	}
	
	/**
	 * Reset filters.
	 *
	 * @return AudioInterface
	 */
	public function withoutFilters()
	{
		$self = clone $this;
		$self->filters = new FiltersCollection();
			
		return $self;
	}
	
	/**
	 * It supports the type of media.
	 *
	 * @return bool
	 */
	protected function isSupportedFileType()
	{
		if (stripos($this->getMimeType(), 'audio/') !== 0)
		{
			return false;
		}
		
		return true;
	}
	
}<commit_msg>Audio: создание формата и потоков вынесено в отдельный метод<commit_after><?php
/**
 * This file is part of the arhitector/jumper library.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @author    <NAME> <<EMAIL>>
 *
 * @license   http://opensource.org/licenses/MIT MIT
 * @copyright Copyright (c) 2017 <NAME> <<EMAIL>>
 */
namespace Arhitector\Jumper;

use Arhitector\Jumper\Exception\InvalidFilterException;
use Arhitector\Jumper\Exception\TranscoderException;
use Arhitector\Jumper\Filter\AudioFilterInterface;
use Arhitector\Jumper\Filter\Collection as FiltersCollection;
use Arhitector\Jumper\Filter\FilterInterface;
use Arhitector\Jumper\Format\AudioFormat;
use Arhitector\Jumper\Format\AudioFormatInterface;
use Arhitector\Jumper\Format\FormatInterface;
use Arhitector\Jumper\Service\ServiceFactory;
use Arhitector\Jumper\Service\ServiceFactoryInterface;
use Arhitector\Jumper\Stream\AudioStream;
use Arhitector\Jumper\Stream\Collection;
use Arhitector\Jumper\Stream\FrameStream;
use Symfony\Component\Process\Exception\ProcessFailedException;

/**
 * Class Audio.
 *
 * @package Arhitector\Jumper
 */
class Audio implements AudioInterface
{
	use Traits\FilePathAwareTrait, TranscoderTrait {
		TranscoderTrait::getFormat as private _getFormat;
	}
	
	/**
	 * @var \Arhitector\Jumper\Filter\Collection List of filters.
	 */
	protected $filters;
	
	/**
	 * Audio constructor.
	 *
	 * @param string                  $filePath
	 * @param ServiceFactoryInterface $service
	 *
	 * @throws \Arhitector\Jumper\Exception\TranscoderException
	 * @throws \InvalidArgumentException
	 */
	public function __construct($filePath, ServiceFactoryInterface $service = null)
	{
		$this->setFilePath($filePath);
		$this->setService($service ?: new ServiceFactory());
		
		/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
		$demuxing = $this->getService()->getDecoderService()->demuxing($this);
		
		if (count($demuxing->streams) < 1 || ( ! $this->isSupportedFileType() && empty($demuxing->format['format'])))
		{
			throw new TranscoderException('File type unsupported or the file is corrupted.');
		}
		
		$this->createFormat($demuxing);
		$this->filters = new FiltersCollection();
	}
	
	/**
	 * Gets the audio channels value.
	 *
	 * @return int
	 */
	public function getAudioChannels()
	{
		return $this->getFormat()->getChannels();
	}
	
	/**
	 * Gets the audio kilo bitrate value.
	 *
	 * @return int
	 */
	public function getAudioKiloBitrate()
	{
		return (int) ($this->getFormat()->getAudioBitrate() / 1000);
	}
	
	/**
	 * Returns the audio codec.
	 *
	 * @return Codec|null
	 */
	public function getAudioCodec()
	{
		return $this->getFormat()->getAudioCodec();
	}
	
	/**
	 * Get sample frequency value.
	 *
	 * @return int
	 */
	public function getFrequency()
	{
		return $this->getFormat()->getFrequency();
	}
	
	/**
	 * Get duration value.
	 *
	 * @return float
	 */
	public function getDuration()
	{
		return $this->getFormat()->getDuration();
	}
	
	/**
	 * Get current format.
	 *
	 * @return AudioFormatInterface|\Arhitector\Jumper\Format\FormatInterface
	 */
	public function getFormat()
	{
		return $this->_getFormat();
	}
	
	/**
	 * Transcoding.
	 *
	 * @param FormatInterface $format
	 * @param string          $filePath
	 * @param bool            $overwrite
	 *
	 * @return float|int Size of the new file or -1
	 * @throws \Symfony\Component\Process\Exception\RuntimeException
	 * @throws \Symfony\Component\Process\Exception\LogicException
	 * @throws \Symfony\Component\Process\Exception\ProcessFailedException
	 * @throws \Arhitector\Jumper\Exception\TranscoderException
	 * @throws \InvalidArgumentException
	 */
	public function save(FormatInterface $format, $filePath, $overwrite = true)
	{
		if ( ! is_string($filePath) || empty($filePath))
		{
			throw new \InvalidArgumentException('File path must not be an empty string.');
		}
	
		if ( ! $overwrite && file_exists($filePath))
		{
			throw new TranscoderException('File path already exists.');
		}
		
		$options = ['output' => $filePath];
		
		foreach (clone $this->filters as $filter)
		{
			$options = array_merge($options, $filter->apply($this, $format));
		}
	
		/** @noinspection ExceptionsAnnotatingAndHandlingInspection */
		$processes = $this->getService()->getEncoderService()->transcoding($this, $format, [
			'output' => $filePath
		]);
		
		foreach ($processes as $process)
		{
			$process->start();
			
			if ($process->wait() !== 0)
			{
				throw new ProcessFailedException($process);
			}
		}
		
		return filesize($filePath);
	}
	
	/**
	 * Add a new filter.
	 *
	 * @param FilterInterface $filter
	 * @param int             $priority range 0-99.
	 *
	 * @return TranscoderInterface
	 * @throws \RangeException
	 * @throws InvalidFilterException
	 */
	public function addFilter(FilterInterface $filter, $priority = 0)
	{
		if ( ! $filter instanceof AudioFilterInterface)
		{
			throw new InvalidFilterException('Filter type is not supported.');
		}
		
		if ($priority > 99)
		{
			throw new \RangeException('Priority should be in the range from 0 to 99.');
		}
		
		$this->filters->insert($filter, $priority);
		
		return $this;
	}
	
	/**
	 * Reset filters.
	 *
	 * @return AudioInterface
	 */
	public function withoutFilters()
	{
		$self = clone $this;
		$self->filters = new FiltersCollection();
			
		return $self;
	}
	
	/**
	 * It supports the type of media.
	 *
	 * @return bool
	 */
	protected function isSupportedFileType()
	{
		if (stripos($this->getMimeType(), 'audio/') !== 0)
		{
			return false;
		}
		
		return true;
	}
	
	/**
	 * @param \stdClass $demuxing
	 *
	 * @throws \Arhitector\Jumper\Exception\TranscoderException
	 * @throws \InvalidArgumentException
	 */
	private function createFormat($demuxing)
	{
		// TODO: проверить что это действительно AudioFormatInterface
		/** @var AudioFormatInterface $className */
		$className = $this->findFormatClass($demuxing->format['format'], AudioFormat::class);
		$this->format = $className::fromArray(array_filter($demuxing->format, function ($value) {
			return $value !== null;
		}));
		
		$this->streams = new Collection(array_map(function ($parameters) {
			if ($parameters['type'] == 'audio')
			{
				$stream = AudioStream::create($this, $parameters);
				
				if ($stream->getChannels() !== null)
				{
					$this->getFormat()->setChannels($stream->getChannels());
				}
				
				if ($stream->getFrequency() !== null)
				{
					$this->getFormat()->setFrequency($stream->getFrequency());
				}
				
				$this->getFormat()->setAudioBitrate($stream->getBitrate());
				$this->getFormat()->setAudioCodec($stream->getCodec());
				
				return $stream;
			}
			
			if ($parameters['type'] == 'video')
			{
				return FrameStream::create($this, $parameters);
			}
			
			throw new TranscoderException('This stream unsupported.');
		}, $demuxing->streams));
	}
	
}
{'fraction_non_alphanumeric': 0.14225257999851512, 'fraction_numerical': 0.003118271586606281, 'mean_word_length': 3.703212290502793, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 124, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3773081', 'n_tokens_mistral': 4671, 'n_tokens_neox': 4072, 'n_words': 889}