mirror of
https://github.com/ceratic/MediaCollectorLibary.git
synced 2026-05-13 23:56:46 +02:00
jellyfin music :)
This commit is contained in:
@@ -5,6 +5,9 @@ namespace App\Services;
|
||||
use App\Models\Movie;
|
||||
use App\Models\TvShow;
|
||||
use App\Models\TvEpisode;
|
||||
use App\Models\MusicArtist;
|
||||
use App\Models\MusicAlbum;
|
||||
use App\Models\MusicTrack;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Exception;
|
||||
@@ -94,8 +97,19 @@ class JellyfinSyncService extends BaseSyncService
|
||||
$this->logProgress('Skipping TV shows sync (sync type: ' . $syncType . ')');
|
||||
}
|
||||
|
||||
// Sync music (artists, albums, tracks) - TODO: Implement when music models are created
|
||||
// $this->syncMusic();
|
||||
// Sync music (artists, albums, tracks) if requested
|
||||
if (in_array($syncType, ['all', 'music'])) {
|
||||
try {
|
||||
$this->syncMusic();
|
||||
} catch (Exception $e) {
|
||||
$this->logProgress('Error syncing music: ' . $e->getMessage());
|
||||
if ($syncType === 'music') {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->logProgress('Skipping music sync (sync type: ' . $syncType . ')');
|
||||
}
|
||||
|
||||
$this->logProgress("Processed {$this->processedCount} items");
|
||||
}
|
||||
@@ -519,6 +533,299 @@ class JellyfinSyncService extends BaseSyncService
|
||||
$this->logProgress("--- Completed sync for episode: {$episodeName} ---");
|
||||
}
|
||||
|
||||
private function syncMusic(): void
|
||||
{
|
||||
try {
|
||||
$this->logProgress('=== Starting Music Sync ===');
|
||||
|
||||
// Sync artists first
|
||||
$this->logProgress('Fetching music artists from Jellyfin...');
|
||||
$artists = $this->getJellyfinItems('MusicArtist');
|
||||
$artistCount = count($artists);
|
||||
$this->logProgress("Found {$artistCount} artists in Jellyfin");
|
||||
|
||||
$processedArtists = 0;
|
||||
$successfulArtists = 0;
|
||||
$failedArtists = 0;
|
||||
|
||||
foreach ($artists as $artistData) {
|
||||
$processedArtists++;
|
||||
$artistName = $artistData['Name'] ?? 'Unknown Artist';
|
||||
$this->logProgress("Processing artist {$processedArtists}/{$artistCount}: {$artistName}");
|
||||
|
||||
try {
|
||||
$this->syncMusicArtist($artistData);
|
||||
$successfulArtists++;
|
||||
$this->logProgress("✓ Successfully synced artist: {$artistName}");
|
||||
} catch (Exception $e) {
|
||||
$failedArtists++;
|
||||
$this->logProgress("✗ Failed to sync artist {$artistName}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->logProgress("Artists sync summary: {$successfulArtists} successful, {$failedArtists} failed");
|
||||
|
||||
// Sync albums
|
||||
$this->logProgress('Fetching music albums from Jellyfin...');
|
||||
$albums = $this->getJellyfinItems('MusicAlbum');
|
||||
$albumCount = count($albums);
|
||||
$this->logProgress("Found {$albumCount} albums in Jellyfin");
|
||||
|
||||
$processedAlbums = 0;
|
||||
$successfulAlbums = 0;
|
||||
$failedAlbums = 0;
|
||||
|
||||
foreach ($albums as $albumData) {
|
||||
$processedAlbums++;
|
||||
$albumName = $albumData['Name'] ?? 'Unknown Album';
|
||||
$this->logProgress("Processing album {$processedAlbums}/{$albumCount}: {$albumName}");
|
||||
|
||||
try {
|
||||
$this->syncMusicAlbum($albumData);
|
||||
$successfulAlbums++;
|
||||
$this->logProgress("✓ Successfully synced album: {$albumName}");
|
||||
} catch (Exception $e) {
|
||||
$failedAlbums++;
|
||||
$this->logProgress("✗ Failed to sync album {$albumName}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->logProgress("Albums sync summary: {$successfulAlbums} successful, {$failedAlbums} failed");
|
||||
|
||||
// Sync tracks
|
||||
$this->logProgress('Fetching music tracks from Jellyfin...');
|
||||
$tracks = $this->getJellyfinItems('Audio');
|
||||
$trackCount = count($tracks);
|
||||
$this->logProgress("Found {$trackCount} tracks in Jellyfin");
|
||||
|
||||
$processedTracks = 0;
|
||||
$successfulTracks = 0;
|
||||
$failedTracks = 0;
|
||||
|
||||
foreach ($tracks as $trackData) {
|
||||
$processedTracks++;
|
||||
$trackName = $trackData['Name'] ?? 'Unknown Track';
|
||||
$this->logProgress("Processing track {$processedTracks}/{$trackCount}: {$trackName}");
|
||||
|
||||
try {
|
||||
$this->syncMusicTrack($trackData);
|
||||
$successfulTracks++;
|
||||
$this->logProgress("✓ Successfully synced track: {$trackName}");
|
||||
} catch (Exception $e) {
|
||||
$failedTracks++;
|
||||
$this->logProgress("✗ Failed to sync track {$trackName}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->logProgress("Tracks sync summary: {$successfulTracks} successful, {$failedTracks} failed");
|
||||
$this->logProgress("=== Music Sync Completed ===");
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logProgress('CRITICAL ERROR in music sync: ' . $e->getMessage());
|
||||
$this->logProgress('Stack trace: ' . $e->getTraceAsString());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function syncMusicArtist(array $artistData): void
|
||||
{
|
||||
$artistModel = new MusicArtist($this->pdo);
|
||||
|
||||
// Check if artist already exists
|
||||
$existingArtist = $artistModel->findAll([
|
||||
'name' => $artistData['Name'],
|
||||
'source_id' => $this->source['id']
|
||||
]);
|
||||
|
||||
$artistDataForDb = [
|
||||
'name' => $artistData['Name'],
|
||||
'biography' => $artistData['Overview'] ?? null,
|
||||
'formed_date' => isset($artistData['PremiereDate']) ? date('Y-m-d', strtotime($artistData['PremiereDate'])) : null,
|
||||
'genre' => isset($artistData['Genres'][0]) ? $artistData['Genres'][0] : null,
|
||||
'country' => null, // Jellyfin doesn't provide country info
|
||||
'image_url' => $this->getImageUrl($artistData['Id'], 'Primary'),
|
||||
'banner_url' => $this->getImageUrl($artistData['Id'], 'Backdrop'),
|
||||
'spotify_id' => $artistData['ProviderIds']['MusicBrainzArtist'] ?? null,
|
||||
'musicbrainz_id' => $artistData['ProviderIds']['MusicBrainzArtist'] ?? null,
|
||||
'source_id' => $this->source['id'],
|
||||
'metadata' => json_encode([
|
||||
'jellyfin_id' => $artistData['Id']
|
||||
])
|
||||
];
|
||||
|
||||
if (empty($existingArtist)) {
|
||||
$artistModel->create($artistDataForDb);
|
||||
$this->newCount++;
|
||||
} else {
|
||||
$artistModel->update($existingArtist[0]['id'], $artistDataForDb);
|
||||
$this->updatedCount++;
|
||||
}
|
||||
|
||||
$this->processedCount++;
|
||||
}
|
||||
|
||||
private function syncMusicAlbum(array $albumData): void
|
||||
{
|
||||
$albumModel = new MusicAlbum($this->pdo);
|
||||
|
||||
// Get or create artist first
|
||||
$artistId = null;
|
||||
if (isset($albumData['AlbumArtists']) && !empty($albumData['AlbumArtists'])) {
|
||||
$artistName = $albumData['AlbumArtists'][0]['Name'];
|
||||
$artistId = $this->getOrCreateMusicArtist($artistName);
|
||||
}
|
||||
|
||||
// Check if album already exists
|
||||
$existingAlbum = $albumModel->findAll([
|
||||
'title' => $albumData['Name'],
|
||||
'artist_id' => $artistId,
|
||||
'source_id' => $this->source['id']
|
||||
]);
|
||||
|
||||
// Download cover image
|
||||
$coverPath = $this->downloadPosterImage($albumData['Id'], $albumData['Name']);
|
||||
if (!$coverPath) {
|
||||
$coverPath = $this->getImageUrl($albumData['Id'], 'Primary');
|
||||
}
|
||||
|
||||
$albumDataForDb = [
|
||||
'title' => $albumData['Name'],
|
||||
'artist_name' => isset($albumData['AlbumArtists'][0]['Name']) ? $albumData['AlbumArtists'][0]['Name'] : 'Unknown Artist',
|
||||
'release_date' => isset($albumData['PremiereDate']) ? date('Y-m-d', strtotime($albumData['PremiereDate'])) : null,
|
||||
'genre' => isset($albumData['Genres'][0]) ? $albumData['Genres'][0] : null,
|
||||
'track_count' => 0, // Will be updated when tracks are synced
|
||||
'total_duration_seconds' => 0, // Will be updated when tracks are synced
|
||||
'cover_url' => $coverPath,
|
||||
'spotify_id' => $albumData['ProviderIds']['MusicBrainzAlbum'] ?? null,
|
||||
'musicbrainz_id' => $albumData['ProviderIds']['MusicBrainzAlbum'] ?? null,
|
||||
'artist_id' => $artistId,
|
||||
'source_id' => $this->source['id'],
|
||||
'metadata' => json_encode([
|
||||
'jellyfin_id' => $albumData['Id']
|
||||
])
|
||||
];
|
||||
|
||||
if (empty($existingAlbum)) {
|
||||
$albumModel->create($albumDataForDb);
|
||||
$this->newCount++;
|
||||
} else {
|
||||
$albumModel->update($existingAlbum[0]['id'], $albumDataForDb);
|
||||
$this->updatedCount++;
|
||||
}
|
||||
|
||||
$this->processedCount++;
|
||||
}
|
||||
|
||||
private function syncMusicTrack(array $trackData): void
|
||||
{
|
||||
$trackModel = new MusicTrack($this->pdo);
|
||||
|
||||
// Get or create artist
|
||||
$artistId = null;
|
||||
if (isset($trackData['AlbumArtists']) && !empty($trackData['AlbumArtists'])) {
|
||||
$artistName = $trackData['AlbumArtists'][0]['Name'];
|
||||
$artistId = $this->getOrCreateMusicArtist($artistName);
|
||||
}
|
||||
|
||||
// Get or create album
|
||||
$albumId = null;
|
||||
if (isset($trackData['Album']) && !empty($trackData['Album'])) {
|
||||
$albumId = $this->getOrCreateMusicAlbum($trackData['Album'], $artistId);
|
||||
}
|
||||
|
||||
// Check if track already exists
|
||||
$existingTrack = $trackModel->findAll([
|
||||
'title' => $trackData['Name'],
|
||||
'artist_id' => $artistId,
|
||||
'album_id' => $albumId,
|
||||
'source_id' => $this->source['id']
|
||||
]);
|
||||
|
||||
$durationSeconds = isset($trackData['RunTimeTicks']) ? intval($trackData['RunTimeTicks'] / 10000000) : null;
|
||||
|
||||
$trackDataForDb = [
|
||||
'title' => $trackData['Name'],
|
||||
'artist_name' => isset($trackData['AlbumArtists'][0]['Name']) ? $trackData['AlbumArtists'][0]['Name'] : 'Unknown Artist',
|
||||
'album_name' => $trackData['Album'] ?? null,
|
||||
'track_number' => $trackData['IndexNumber'] ?? null,
|
||||
'duration_seconds' => $durationSeconds,
|
||||
'genre' => isset($trackData['Genres'][0]) ? $trackData['Genres'][0] : null,
|
||||
'release_date' => isset($trackData['PremiereDate']) ? date('Y-m-d', strtotime($trackData['PremiereDate'])) : null,
|
||||
'play_count' => 0,
|
||||
'artist_id' => $artistId,
|
||||
'album_id' => $albumId,
|
||||
'source_id' => $this->source['id'],
|
||||
'metadata' => json_encode([
|
||||
'jellyfin_id' => $trackData['Id']
|
||||
])
|
||||
];
|
||||
|
||||
if (empty($existingTrack)) {
|
||||
$trackModel->create($trackDataForDb);
|
||||
$this->newCount++;
|
||||
} else {
|
||||
$trackModel->update($existingTrack[0]['id'], $trackDataForDb);
|
||||
$this->updatedCount++;
|
||||
}
|
||||
|
||||
$this->processedCount++;
|
||||
}
|
||||
|
||||
private function getOrCreateMusicArtist(string $artistName): ?int
|
||||
{
|
||||
$artistModel = new MusicArtist($this->pdo);
|
||||
|
||||
// Check if artist exists
|
||||
$existingArtist = $artistModel->findAll([
|
||||
'name' => $artistName,
|
||||
'source_id' => $this->source['id']
|
||||
]);
|
||||
|
||||
if (!empty($existingArtist)) {
|
||||
return $existingArtist[0]['id'];
|
||||
}
|
||||
|
||||
// Create new artist
|
||||
$artistData = [
|
||||
'name' => $artistName,
|
||||
'source_id' => $this->source['id'],
|
||||
'metadata' => json_encode([])
|
||||
];
|
||||
|
||||
$artistId = $artistModel->create($artistData);
|
||||
return $artistId;
|
||||
}
|
||||
|
||||
private function getOrCreateMusicAlbum(string $albumName, ?int $artistId): ?int
|
||||
{
|
||||
$albumModel = new MusicAlbum($this->pdo);
|
||||
|
||||
// Check if album exists
|
||||
$existingAlbum = $albumModel->findAll([
|
||||
'title' => $albumName,
|
||||
'artist_id' => $artistId,
|
||||
'source_id' => $this->source['id']
|
||||
]);
|
||||
|
||||
if (!empty($existingAlbum)) {
|
||||
return $existingAlbum[0]['id'];
|
||||
}
|
||||
|
||||
// Create new album
|
||||
$albumData = [
|
||||
'title' => $albumName,
|
||||
'artist_name' => 'Unknown Artist', // Will be updated when artist is found
|
||||
'track_count' => 0,
|
||||
'total_duration_seconds' => 0,
|
||||
'artist_id' => $artistId,
|
||||
'source_id' => $this->source['id'],
|
||||
'metadata' => json_encode([])
|
||||
];
|
||||
|
||||
$albumId = $albumModel->create($albumData);
|
||||
return $albumId;
|
||||
}
|
||||
|
||||
private function getShowEpisodes(string $jellyfinShowId): array
|
||||
{
|
||||
$this->logProgress("--- Fetching episodes for show ID: {$jellyfinShowId} ---");
|
||||
|
||||
Reference in New Issue
Block a user