New: Write genres and album art to track file tags

pull/880/head
ta264 5 years ago
parent a0a96911f8
commit a35f965d31

@ -75,7 +75,6 @@ class RetagPreviewModalContentConnector extends Component {
RetagPreviewModalContentConnector.propTypes = {
artistId: PropTypes.number.isRequired,
albumId: PropTypes.number,
retagTracks: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
isFetching: PropTypes.bool.isRequired,
fetchRetagPreview: PropTypes.func.isRequired,

@ -1,5 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import formatBytes from 'Utilities/Number/formatBytes';
import { icons } from 'Helpers/Props';
import Icon from 'Components/Icon';
import CheckInput from 'Components/Form/CheckInput';
@ -7,16 +8,19 @@ import styles from './RetagPreviewRow.css';
import DescriptionList from 'Components/DescriptionList/DescriptionList';
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
function formatMissing(value) {
if (value === undefined || value === 0 || value === '0') {
function formatValue(field, value) {
if (value === undefined || value === 0 || value === '0' || value === '') {
return (<Icon name={icons.BAN} size={12} />);
}
if (field === 'Image Size') {
return formatBytes(value);
}
return value;
}
function formatChange(oldValue, newValue) {
function formatChange(field, oldValue, newValue) {
return (
<div> {formatMissing(oldValue)} <Icon name={icons.ARROW_RIGHT_NO_CIRCLE} size={12} /> {formatMissing(newValue)} </div>
<div> {formatValue(field, oldValue)} <Icon name={icons.ARROW_RIGHT_NO_CIRCLE} size={12} /> {formatValue(field, newValue)} </div>
);
}
@ -78,7 +82,7 @@ class RetagPreviewRow extends Component {
<DescriptionListItem
key={field}
title={field}
data={formatChange(oldValue, newValue)}
data={formatChange(field, oldValue, newValue)}
/>
);
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

@ -23,7 +23,7 @@ namespace NzbDrone.Core.Test.MediaFiles.AudioTagServiceFixture
{
private static readonly string[] MediaFiles = new [] { "nin.mp2", "nin.mp3", "nin.flac", "nin.m4a", "nin.wma", "nin.ape", "nin.opus" };
private static readonly string[] SkipProperties = new [] { "IsValid", "Duration", "Quality", "MediaInfo" };
private static readonly string[] SkipProperties = new [] { "IsValid", "Duration", "Quality", "MediaInfo", "ImageFile" };
private static readonly Dictionary<string, string[]> SkipPropertiesByFile = new Dictionary<string, string[]> {
{ "nin.mp2", new [] {"OriginalReleaseDate"} }
};
@ -61,6 +61,9 @@ namespace NzbDrone.Core.Test.MediaFiles.AudioTagServiceFixture
.Setup(x => x.WriteAudioTags)
.Returns(WriteAudioTagsType.Sync);
var imageFile = Path.Combine(testdir, "nin.png");
var imageSize = _diskProvider.GetFileSize(imageFile);
// have to manually set the arrays of string parameters and integers to values > 1
testTags = Builder<AudioTag>.CreateNew()
.With(x => x.Track = 2)
@ -73,6 +76,9 @@ namespace NzbDrone.Core.Test.MediaFiles.AudioTagServiceFixture
.With(x => x.OriginalYear = 2009)
.With(x => x.Performers = new [] { "Performer1" })
.With(x => x.AlbumArtists = new [] { "방탄소년단" })
.With(x => x.Genres = new [] { "Genre1", "Genre2" })
.With(x => x.ImageFile = imageFile)
.With(x => x.ImageSize = imageSize)
.Build();
}
@ -228,7 +234,8 @@ namespace NzbDrone.Core.Test.MediaFiles.AudioTagServiceFixture
var tag = Subject.ReadAudioTag(path);
var expected = new AudioTag() {
Performers = new string[0],
AlbumArtists = new string[0]
AlbumArtists = new string[0],
Genres = new string[0]
};
VerifySame(tag, expected, skipProperties);

@ -471,6 +471,9 @@
<Content Include="Files\LongOverview.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Files\Media\nin.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Files\Media\nin.mp2">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@ -639,4 +642,4 @@
</ItemGroup>
<Copy SourceFiles="@(IdentificationTestCases)" DestinationFolder="$(OutputPath)\Files\Identification\" SkipUnchangedFiles="true" />
</Target>
</Project>
</Project>

@ -19,6 +19,7 @@ namespace NzbDrone.Core.MediaCover
{
void ConvertToLocalUrls(int entityId, MediaCoverEntity coverEntity, IEnumerable<MediaCover> covers);
string GetCoverPath(int entityId, MediaCoverEntity coverEntity, MediaCoverTypes mediaCoverTypes, string extension, int? height = null);
void EnsureAlbumCovers(Album album);
}
public class MediaCoverService :
@ -137,7 +138,7 @@ namespace NzbDrone.Core.MediaCover
}
}
private void EnsureAlbumCovers(Album album)
public void EnsureAlbumCovers(Album album)
{
foreach (var cover in album.Images.Where(e => e.CoverType == MediaCoverTypes.Cover))
{

@ -34,6 +34,9 @@ namespace NzbDrone.Core.MediaFiles
public uint OriginalYear { get; set; }
public string Publisher { get; set; }
public TimeSpan Duration { get; set; }
public string[] Genres { get; set; }
public string ImageFile { get; set; }
public long ImageSize { get; set; }
public string MusicBrainzReleaseCountry { get; set; }
public string MusicBrainzReleaseStatus { get; set; }
public string MusicBrainzReleaseType { get; set; }
@ -81,6 +84,8 @@ namespace NzbDrone.Core.MediaFiles
Year = tag.Year;
Publisher = tag.Publisher;
Duration = file.Properties.Duration;
Genres = tag.Genres;
ImageSize = tag.Pictures.FirstOrDefault()?.Data.Count ?? 0;
MusicBrainzReleaseCountry = tag.MusicBrainzReleaseCountry;
MusicBrainzReleaseStatus = tag.MusicBrainzReleaseStatus;
MusicBrainzReleaseType = tag.MusicBrainzReleaseType;
@ -315,6 +320,7 @@ namespace NzbDrone.Core.MediaFiles
// WMA with null performers/albumartists
Performers = Performers ?? new string[0];
AlbumArtists = AlbumArtists ?? new string[0];
Genres = Genres ?? new string[0];
TagLib.File file = null;
try
@ -332,6 +338,7 @@ namespace NzbDrone.Core.MediaFiles
tag.Disc = Disc;
tag.DiscCount = DiscCount;
tag.Publisher = Publisher;
tag.Genres = Genres;
tag.MusicBrainzReleaseCountry = MusicBrainzReleaseCountry;
tag.MusicBrainzReleaseStatus = MusicBrainzReleaseStatus;
tag.MusicBrainzReleaseType = MusicBrainzReleaseType;
@ -341,6 +348,11 @@ namespace NzbDrone.Core.MediaFiles
tag.MusicBrainzReleaseGroupId = MusicBrainzReleaseGroupId;
tag.MusicBrainzTrackId = MusicBrainzTrackId;
if (ImageFile.IsNotNullOrWhiteSpace())
{
tag.Pictures = new IPicture[1] { new Picture(ImageFile) };
}
if (file.TagTypes.HasFlag(TagTypes.Id3v2))
{
var id3tag = (TagLib.Id3v2.Tag) file.GetTag(TagTypes.Id3v2);
@ -524,6 +536,16 @@ namespace NzbDrone.Core.MediaFiles
output.Add("Label", Tuple.Create(Publisher, other.Publisher));
}
if (!Genres.SequenceEqual(other.Genres))
{
output.Add("Genres", Tuple.Create(string.Join(", ", Genres), string.Join(", ", other.Genres)));
}
if (ImageSize != other.ImageSize)
{
output.Add("Image Size", Tuple.Create(ImageSize.ToString(), other.ImageSize.ToString()));
}
return output;
}

@ -16,6 +16,7 @@ using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using TagLib;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaCover;
namespace NzbDrone.Core.MediaFiles
{
@ -40,6 +41,7 @@ namespace NzbDrone.Core.MediaFiles
private readonly IMediaFileService _mediaFileService;
private readonly IDiskProvider _diskProvider;
private readonly IArtistService _artistService;
private readonly IMapCoversToLocal _mediaCoverService;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
@ -47,6 +49,7 @@ namespace NzbDrone.Core.MediaFiles
IMediaFileService mediaFileService,
IDiskProvider diskProvider,
IArtistService artistService,
IMapCoversToLocal mediaCoverService,
IEventAggregator eventAggregator,
Logger logger)
{
@ -54,6 +57,7 @@ namespace NzbDrone.Core.MediaFiles
_mediaFileService = mediaFileService;
_diskProvider = diskProvider;
_artistService = artistService;
_mediaCoverService = mediaCoverService;
_eventAggregator = eventAggregator;
_logger = logger;
}
@ -76,6 +80,23 @@ namespace NzbDrone.Core.MediaFiles
var albumartist = album.Artist.Value;
var artist = track.ArtistMetadata.Value;
var cover = album.Images.FirstOrDefault(x => x.CoverType == MediaCoverTypes.Cover);
string imageFile = null;
long imageSize = 0;
if (cover != null)
{
imageFile = _mediaCoverService.GetCoverPath(album.Id, MediaCoverEntity.Album, cover.CoverType, cover.Extension, null);
var fileInfo = _diskProvider.GetFileInfo(imageFile);
if (fileInfo.Exists)
{
imageSize = fileInfo.Length;
}
else
{
imageFile = null;
}
}
return new AudioTag {
Title = track.Title,
Performers = new [] { artist.Name },
@ -91,6 +112,9 @@ namespace NzbDrone.Core.MediaFiles
OriginalReleaseDate = album.ReleaseDate,
OriginalYear = (uint)album.ReleaseDate?.Year,
Publisher = release.Label.FirstOrDefault(),
Genres = album.Genres.Any() ? album.Genres.ToArray() : artist.Genres.ToArray(),
ImageFile = imageFile,
ImageSize = imageSize,
MusicBrainzReleaseCountry = IsoCountries.Find(release.Country.FirstOrDefault())?.TwoLetterCode,
MusicBrainzReleaseStatus = release.Status.ToLower(),
MusicBrainzReleaseType = album.AlbumType.ToLower(),

@ -12,6 +12,7 @@ using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Music.Commands;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.History;
using NzbDrone.Core.MediaCover;
namespace NzbDrone.Core.Music
{
@ -33,6 +34,7 @@ namespace NzbDrone.Core.Music
private readonly IHistoryService _historyService;
private readonly IEventAggregator _eventAggregator;
private readonly ICheckIfAlbumShouldBeRefreshed _checkIfAlbumShouldBeRefreshed;
private readonly IMapCoversToLocal _mediaCoverService;
private readonly Logger _logger;
public RefreshAlbumService(IAlbumService albumService,
@ -46,6 +48,7 @@ namespace NzbDrone.Core.Music
IHistoryService historyService,
IEventAggregator eventAggregator,
ICheckIfAlbumShouldBeRefreshed checkIfAlbumShouldBeRefreshed,
IMapCoversToLocal mediaCoverService,
Logger logger)
: base(logger, artistMetadataService)
{
@ -59,6 +62,7 @@ namespace NzbDrone.Core.Music
_historyService = historyService;
_eventAggregator = eventAggregator;
_checkIfAlbumShouldBeRefreshed = checkIfAlbumShouldBeRefreshed;
_mediaCoverService = mediaCoverService;
_logger = logger;
}
@ -155,6 +159,13 @@ namespace NzbDrone.Core.Music
{
result = UpdateResult.None;
}
// Force update and fetch covers if images have changed so that we can write them into tags
if (remote.Images.Any() && !local.Images.Select(x => x.Url).SequenceEqual(remote.Images.Select(x => x.Url)))
{
_mediaCoverService.EnsureAlbumCovers(remote);
result = UpdateResult.UpdateTags;
}
local.ArtistMetadataId = remote.ArtistMetadata.Value.Id;
local.ForeignAlbumId = remote.ForeignAlbumId;

Loading…
Cancel
Save