Skip to content

Commit

Permalink
v4.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ewilan-riviere committed Oct 3, 2024
1 parent 8035c3e commit e081da4
Show file tree
Hide file tree
Showing 8 changed files with 490 additions and 365 deletions.
113 changes: 93 additions & 20 deletions src/Id3/Id3Writer.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ protected function __construct(
protected array $warnings = [],
protected array $errors = [],
protected bool $remove_old_tags = false,
protected ?array $cover = null,
protected bool $has_new_cover = false,
protected bool $delete_cover = false,
protected bool $skip_errors = true,
protected array $tag_formats = [],
protected bool $success = false,
Expand All @@ -46,6 +49,11 @@ public static function make(Audio $audio): self
return $self;
}

public function getCore(): AudioCore
{
return $this->core;
}

/**
* Allow to remove other tags when writing tags.
*/
Expand Down Expand Up @@ -254,18 +262,60 @@ public function tag(string $key, string|int|bool|null $value): self
}

/**
* To update path to save file.
*/
public function path(string $path): self
{
if (file_exists($path)) {
unlink($path);
}
copy($this->audio->getPath(), $path);

$this->writer->filename = $path;

return $this;
}

/**
* Advanced usage only to save manually tags.
*
* @param string[] $tag_formats
*/
public function tagFormats(array $tag_formats): self
{
$this->tag_formats = $tag_formats;

return $this;
}

/**
* Update cover is only supported by `id3` format.
*
* @param string $pathOrData Path to cover image or binary data
*/
public function cover(string $pathOrData): self
{
$this->core->cover = AudioCoreCover::make($pathOrData);
$this->has_new_cover = true;

return $this;
}

/**
* Remove cover from tags.
*/
public function removeCover(): self
{
$this->delete_cover = true;

return $this;
}

/**
* Set manually tags, to know which key used for which tag, you have to refer to documentation.
*
* WARNING: This method is for advanced usage only, if you use it, this will override all other tags.
*
* @docs https://github.com/kiwilan/php-audio#convert-properties
*
* For example, album artist for `id3` encoded files, is `band` key.
Expand All @@ -292,6 +342,7 @@ public function handleErrors(): self

public function save(): bool
{
$this->attachCover();
$this->parseTagFormats();
if (! $this->is_manual) {
$this->assignTags();
Expand Down Expand Up @@ -324,6 +375,7 @@ private function parseErrors(): void
AudioFormatEnum::flac => true,
AudioFormatEnum::mp3 => true,
AudioFormatEnum::ogg => true,
AudioFormatEnum::m4b => true,
default => false
};

Expand Down Expand Up @@ -378,12 +430,21 @@ private function assignTags(): self
return $this;

Check warning on line 430 in src/Id3/Id3Writer.php

View check run for this annotation

Codecov / codecov/patch

src/Id3/Id3Writer.php#L430

Added line #L430 was not covered by tests
}

$oldTags = [];
if (! $this->writer->remove_other_tags) {
$oldTags = $this->audio->getRaw();
}

$this->new_tags = [
...$this->audio->getRaw(), // old tags
...$oldTags, // old tags
...$convert->toArray(), // new tags
];
$this->new_tags = $this->convertTags($this->new_tags);

if ($this->cover && ! $this->delete_cover) {
$this->new_tags['attached_picture'][0] = $this->cover;
}

return $this;
}

Expand Down Expand Up @@ -437,25 +498,37 @@ private function parseTagFormats(): self
return $this;
}

// private function attachCover(array &$tags): void
// {
// $coverFormatsAllowed = [AudioFormatEnum::mp3];
// if ($this->core->cover && in_array($this->audio->getFormat(), $coverFormatsAllowed)) {
// // $tags = [
// // ...$tags,
// // 'CTOC' => $old_tags['id3v2']['CTOC'],
// // 'CHAP' => $old_tags['id3v2']['CHAP'],
// // 'chapters' => $old_tags['id3v2']['chapters'],
// // ];
// $tags['attached_picture'][0] = [
// 'data' => base64_decode($this->core->cover->data),
// 'picturetypeid' => $this->core->cover->picture_type_id,
// 'description' => $this->core->cover->description,
// 'mime' => $this->core->cover->mime,
// ];
// $this->core->has_cover = true;
// }
// }
private function attachCover(): void
{
if ($this->audio->hasCover() && ! $this->has_new_cover && ! $this->delete_cover) {
$this->core->cover = new AudioCoreCover(
data: $this->audio->getCover()->getContents(base64: true),
mime: $this->audio->getCover()->getMimeType(),
);
}

$coverFormatsAllowed = [AudioFormatEnum::mp3];
if ($this->core->cover && in_array($this->audio->getFormat(), $coverFormatsAllowed)) {
// $tags = [
// ...$tags,
// 'CTOC' => $old_tags['id3v2']['CTOC'],
// 'CHAP' => $old_tags['id3v2']['CHAP'],
// 'chapters' => $old_tags['id3v2']['chapters'],
// ];
$this->cover = [
'data' => base64_decode($this->core->cover->data),
'picturetypeid' => $this->core->cover->picture_type_id ?? 1,
'description' => $this->core->cover->description ?? 'cover',
'mime' => $this->core->cover->mime,
];
$this->core->has_cover = true;
}

if ($this->delete_cover) {
$this->cover = null;
$this->core->has_cover = false;
}
}

/**
* @param array<string, string> $tags
Expand Down
8 changes: 8 additions & 0 deletions src/Models/AudioCover.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,12 @@ public function getHeight(): ?int
{
return $this->height;
}

/**
* Extract the cover to a file.
*/
public function extractCover(string $path): void
{
file_put_contents($path, $this->getContents());
}
}
26 changes: 26 additions & 0 deletions tests/Pest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
define('AUDIOBOOK', __DIR__.'/media/audiobook.m4b');
define('AUDIOBOOK_MP3', __DIR__.'/media/audiobook.mp3');
define('MD', __DIR__.'/media/test.md');
define('DEFAULT_FOLDER', __DIR__.'/media/default-folder.jpg');
define('FOLDER', __DIR__.'/media/folder.jpg');

function addWriterFilesForTests()
Expand Down Expand Up @@ -178,3 +179,28 @@ function testMp3Writed(Audio $audio)
expect($audio->getDiscNumber())->toBe('2/2');
expect($audio->isCompilation())->toBeFalse();
}

function pathTo(string $filename, string $subDirectory = 'output'): string
{
return __DIR__.'/'.$subDirectory.'/'.$filename;
}

function resetMp3Writer()
{
$audio = Audio::read(MP3_WRITER);

$audio->update()
->title('Introduction')
->artist('Mr Piouf')
->album('P1PDD Le conclave de Troie')
->genre('Roleplaying game')
->year(2016)
->trackNumber('1')
->comment('http://www.p1pdd.com')
->albumArtist('P1PDD & Mr Piouf')
->composer('P1PDD & Piouf')
->discNumber('1')
->isCompilation()
->cover(DEFAULT_FOLDER)
->save();
}
122 changes: 122 additions & 0 deletions tests/WriterCoverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

use Kiwilan\Audio\Audio;

beforeEach(function () {
resetMp3Writer();
});

it('can update cover', function (string $path) {
$audio = Audio::read($path);
$audio->update()
->cover(FOLDER)
->save();

$audio = Audio::read($path);
expect($audio->getTitle())->toBe('Introduction');

$content = base64_encode(file_get_contents(FOLDER));
expect($audio->getCover()->getContents(true))->toBe($content);
})->with([MP3_WRITER]);

it('can read use file content as cover', function (string $path) {
$audio = Audio::read($path);

$tag = $audio->update()
->cover(file_get_contents(FOLDER));

$tag->save();

$audio = Audio::read($path);

$content = file_get_contents(FOLDER);
expect($audio->getCover()->getContents(true))->toBe(base64_encode($content));
})->with([MP3_WRITER]);

it('can read use tags', function (string $path) {
$audio = Audio::read($path);

$random = (string) rand(1, 1000);
$image = getimagesize(FOLDER);
$coverData = file_get_contents(FOLDER);
$coverPicturetypeid = $image[2];
$coverDescription = 'cover';
$coverMime = $image['mime'];
$tag = $audio->update()
->tags([
'title' => $random,
'attached_picture' => [
[
'data' => $coverData,
'picturetypeid' => $coverPicturetypeid,
'description' => $coverDescription,
'mime' => $coverMime,
],
],
]);

$tag->save();

$audio = Audio::read($path);
expect($audio->getTitle())->toBe($random);

$content = file_get_contents(FOLDER);
expect($audio->getCover()->getContents())->toBe($content);
})->with([MP3_WRITER]);

it('can use tags with cover', function (string $path) {
$audio = Audio::read($path);

$tag = $audio->update()
->tags([
'title' => 'New Title',
])
->cover(FOLDER);

$tag->save();

$audio = Audio::read($path);

$content = file_get_contents(FOLDER);
expect($audio->getTitle())->toBe('New Title');
expect($tag->getCore()->cover->data)->toBe(base64_encode($content));
})->with([MP3_WRITER]);

it('can update cover with path', function () {
$audio = Audio::read(MP3_WRITER);

$path = pathTo('cover.jpg', 'media');

if (file_exists($path)) {
unlink($path);
}
$audio->getCover()->extractCover($path);
expect(file_exists($path))->toBeTrue();

$audio->update()
->cover(FOLDER)
->handleErrors()
->save();

$audio = Audio::read(MP3_WRITER);
unlink($path);
$audio->getCover()->extractCover($path);
expect(file_exists($path))->toBeTrue();

$content = file_get_contents(FOLDER);
expect($audio->getCover()->getContents(true))->toBe(base64_encode($content));
});

it('can remove cover', function () {
$audio = Audio::read(MP3_WRITER);

$audio->update()
->removeCover()
->handleErrors()
->save();

$audio = Audio::read(MP3_WRITER);

expect($audio->hasCover())->toBeFalse();
expect($audio->getCover())->toBeNull();
});
Loading

0 comments on commit e081da4

Please sign in to comment.