@@ -535,4 +535,170 @@ private function invokePrivateGetFlacDurationFromHandle(string $path): ?int
535535 fclose ($ handle );
536536 }
537537 }
538+
539+ // -------------------------------------------------------------------------
540+ // findCoverArtFile() — scans a directory for cover art (SV-COVERART-FIX)
541+ // -------------------------------------------------------------------------
542+
543+ /**
544+ * Invokes the private findCoverArtFile method via reflection.
545+ *
546+ * @param string $directoryPath Directory to search for cover art
547+ * @return string|null Path to cover art file, or null if not found
548+ */
549+ private function invokeFindCoverArtFile (string $ directoryPath ): ?string
550+ {
551+ $ method = new \ReflectionMethod (AudioScanner::class, 'findCoverArtFile ' );
552+ $ method ->setAccessible (true );
553+
554+ // Create a SplFileInfo for a dummy file in the directory
555+ // SplFileInfo can wrap a path to a non-existent file
556+ $ dummyFilePath = $ directoryPath . '/.dummy ' ;
557+ $ fileInfo = new \SplFileInfo ($ dummyFilePath );
558+
559+ return $ method ->invoke ($ this ->scanner , $ fileInfo );
560+ }
561+
562+ public function testFindCoverArtFileReturnsCoverJpg (): void
563+ {
564+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
565+ mkdir ($ tempDir , 0755 , true );
566+ file_put_contents ($ tempDir . '/cover.jpg ' , "\xFF\xD8\xFF\xE0" );
567+
568+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
569+
570+ $ this ->assertSame ($ tempDir . '/cover.jpg ' , $ result );
571+
572+ unlink ($ tempDir . '/cover.jpg ' );
573+ rmdir ($ tempDir );
574+ }
575+
576+ public function testFindCoverArtFileReturnsCoverPng (): void
577+ {
578+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
579+ mkdir ($ tempDir , 0755 , true );
580+ file_put_contents ($ tempDir . '/cover.png ' , "\x89PNG \r\n\x1A\n" );
581+
582+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
583+
584+ $ this ->assertSame ($ tempDir . '/cover.png ' , $ result );
585+
586+ unlink ($ tempDir . '/cover.png ' );
587+ rmdir ($ tempDir );
588+ }
589+
590+ public function testFindCoverArtFileReturnsFolderJpg (): void
591+ {
592+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
593+ mkdir ($ tempDir , 0755 , true );
594+ file_put_contents ($ tempDir . '/folder.jpg ' , "\xFF\xD8\xFF\xE0" );
595+
596+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
597+
598+ $ this ->assertSame ($ tempDir . '/folder.jpg ' , $ result );
599+
600+ unlink ($ tempDir . '/folder.jpg ' );
601+ rmdir ($ tempDir );
602+ }
603+
604+ public function testFindCoverArtFileReturnsFolderPng (): void
605+ {
606+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
607+ mkdir ($ tempDir , 0755 , true );
608+ file_put_contents ($ tempDir . '/folder.png ' , "\x89PNG \r\n\x1A\n" );
609+
610+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
611+
612+ $ this ->assertSame ($ tempDir . '/folder.png ' , $ result );
613+
614+ unlink ($ tempDir . '/folder.png ' );
615+ rmdir ($ tempDir );
616+ }
617+
618+ public function testFindCoverArtFilePrefersCoverOverFolder (): void
619+ {
620+ // When both cover.jpg and folder.jpg exist, cover.jpg takes precedence.
621+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
622+ mkdir ($ tempDir , 0755 , true );
623+ file_put_contents ($ tempDir . '/cover.jpg ' , "\xFF\xD8\xFF\xE0" );
624+ file_put_contents ($ tempDir . '/folder.jpg ' , "\xFF\xD8\xFF\xE0" );
625+
626+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
627+
628+ $ this ->assertSame ($ tempDir . '/cover.jpg ' , $ result );
629+
630+ unlink ($ tempDir . '/cover.jpg ' );
631+ unlink ($ tempDir . '/folder.jpg ' );
632+ rmdir ($ tempDir );
633+ }
634+
635+ public function testFindCoverArtFileReturnsNullWhenNoCoverArtExists (): void
636+ {
637+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
638+ mkdir ($ tempDir , 0755 , true );
639+ // Only a non-image file present.
640+ file_put_contents ($ tempDir . '/track.mp3 ' , 'fake mp3 ' );
641+
642+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
643+
644+ $ this ->assertNull ($ result );
645+
646+ unlink ($ tempDir . '/track.mp3 ' );
647+ rmdir ($ tempDir );
648+ }
649+
650+ public function testFindCoverArtFileReturnsNullForNonExistentDirectory (): void
651+ {
652+ $ result = $ this ->invokeFindCoverArtFile ('/non/existent/directory/path ' );
653+
654+ $ this ->assertNull ($ result );
655+ }
656+
657+ public function testFindCoverArtFileIsCaseSensitive (): void
658+ {
659+ // Covers must be lowercase; Cover.jpg / FOLDER.JPG must NOT match.
660+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
661+ mkdir ($ tempDir , 0755 , true );
662+ file_put_contents ($ tempDir . '/Cover.jpg ' , "\xFF\xD8\xFF\xE0" ); // wrong case
663+ file_put_contents ($ tempDir . '/FOLDER.jpg ' , "\xFF\xD8\xFF\xE0" ); // wrong case
664+ file_put_contents ($ tempDir . '/track.mp3 ' , 'fake mp3 ' );
665+
666+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
667+
668+ $ this ->assertNull ($ result , 'case-insensitive names must not be matched ' );
669+
670+ unlink ($ tempDir . '/Cover.jpg ' );
671+ unlink ($ tempDir . '/FOLDER.jpg ' );
672+ unlink ($ tempDir . '/track.mp3 ' );
673+ rmdir ($ tempDir );
674+ }
675+
676+ public function testFindCoverArtFileReturnsNullForEmptyDirectory (): void
677+ {
678+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
679+ mkdir ($ tempDir , 0755 , true );
680+
681+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
682+
683+ $ this ->assertNull ($ result );
684+
685+ rmdir ($ tempDir );
686+ }
687+
688+ public function testFindCoverArtFilePrioritizesJpgOverPng (): void
689+ {
690+ // When both cover.jpg and cover.png exist, .jpg takes precedence.
691+ $ tempDir = sys_get_temp_dir () . '/phlix_cover_ ' . uniqid ();
692+ mkdir ($ tempDir , 0755 , true );
693+ file_put_contents ($ tempDir . '/cover.jpg ' , "\xFF\xD8\xFF\xE0" );
694+ file_put_contents ($ tempDir . '/cover.png ' , "\x89PNG \r\n\x1A\n" );
695+
696+ $ result = $ this ->invokeFindCoverArtFile ($ tempDir );
697+
698+ $ this ->assertSame ($ tempDir . '/cover.jpg ' , $ result );
699+
700+ unlink ($ tempDir . '/cover.jpg ' );
701+ unlink ($ tempDir . '/cover.png ' );
702+ rmdir ($ tempDir );
703+ }
538704}
0 commit comments