Fixes Misc Issues with Album Metadata Extrafiles (#145)

* Fixes Misc Issues with Album Metadata Extrafiles
* Fixed: Move Empty Subfolders to after ArtistRenamedEvent and Metadata mover
* Remove Path from Album Table, Fix Wdtv, MediaBrowser, Roksbox
* Remove Album Path from UI
* Remove Comments and add Jpeg extension to XMBC image regex
pull/6/head
Qstick 7 years ago committed by GitHub
parent 71cc80aef9
commit b63d9d0146
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -67,7 +67,6 @@ class AlbumRow extends Component {
title, title,
isSaving, isSaving,
artistMonitored, artistMonitored,
path,
columns columns
} = this.props; } = this.props;
@ -122,16 +121,6 @@ class AlbumRow extends Component {
); );
} }
if (name === 'path') {
return (
<TableRowCell key={name}>
{
path
}
</TableRowCell>
);
}
if (name === 'mediumCount') { if (name === 'mediumCount') {
return ( return (
<TableRowCell key={name}> <TableRowCell key={name}>
@ -221,7 +210,6 @@ AlbumRow.propTypes = {
unverifiedSceneNumbering: PropTypes.bool, unverifiedSceneNumbering: PropTypes.bool,
artistMonitored: PropTypes.bool.isRequired, artistMonitored: PropTypes.bool.isRequired,
statistics: PropTypes.object.isRequired, statistics: PropTypes.object.isRequired,
path: PropTypes.string,
mediaInfo: PropTypes.object, mediaInfo: PropTypes.object,
alternateTitles: PropTypes.arrayOf(PropTypes.object).isRequired, alternateTitles: PropTypes.arrayOf(PropTypes.object).isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired, columns: PropTypes.arrayOf(PropTypes.object).isRequired,

@ -39,11 +39,6 @@ export const defaultState = {
label: 'Title', label: 'Title',
isVisible: true isVisible: true
}, },
{
name: 'path',
label: 'Path',
isVisible: false
},
{ {
name: 'releaseDate', name: 'releaseDate',
label: 'Release Date', label: 'Release Date',

@ -16,7 +16,6 @@ namespace Lidarr.Api.V1.Albums
public List<string> AlbumLabel { get; set; } public List<string> AlbumLabel { get; set; }
public string ForeignAlbumId { get; set; } public string ForeignAlbumId { get; set; }
public bool Monitored { get; set; } public bool Monitored { get; set; }
public string Path { get; set; }
public int ProfileId { get; set; } public int ProfileId { get; set; }
public int Duration { get; set; } public int Duration { get; set; }
public string AlbumType { get; set; } public string AlbumType { get; set; }
@ -57,7 +56,6 @@ namespace Lidarr.Api.V1.Albums
ArtistId = model.ArtistId, ArtistId = model.ArtistId,
AlbumLabel = model.Label, AlbumLabel = model.Label,
ForeignAlbumId = model.ForeignAlbumId, ForeignAlbumId = model.ForeignAlbumId,
Path = model.Path,
ProfileId = model.ProfileId, ProfileId = model.ProfileId,
Monitored = model.Monitored, Monitored = model.Monitored,
ReleaseDate = model.ReleaseDate, ReleaseDate = model.ReleaseDate,

@ -66,12 +66,13 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_not_delete_metadata_files_when_they_are_for_the_same_track_but_different_consumers() public void should_not_delete_metadata_files_when_they_are_for_the_same_album_but_different_consumers()
{ {
var files = Builder<MetadataFile>.CreateListOfSize(2) var files = Builder<MetadataFile>.CreateListOfSize(2)
.All() .All()
.With(m => m.Type = MetadataType.TrackMetadata) .With(m => m.Type = MetadataType.AlbumMetadata)
.With(m => m.TrackFileId = 1) .With(m => m.ArtistId = 1)
.With(m => m.AlbumId = 1)
.BuildListOfNew(); .BuildListOfNew();
Db.InsertMany(files); Db.InsertMany(files);
@ -80,12 +81,13 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_not_delete_metadata_files_for_different_track() public void should_not_delete_metadata_files_for_different_album()
{ {
var files = Builder<MetadataFile>.CreateListOfSize(2) var files = Builder<MetadataFile>.CreateListOfSize(2)
.All() .All()
.With(m => m.Type = MetadataType.TrackMetadata) .With(m => m.Type = MetadataType.AlbumMetadata)
.With(m => m.Consumer = "XbmcMetadata") .With(m => m.Consumer = "XbmcMetadata")
.With(m => m.ArtistId = 1)
.BuildListOfNew(); .BuildListOfNew();
Db.InsertMany(files); Db.InsertMany(files);
@ -94,12 +96,13 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_delete_metadata_files_when_they_are_for_the_same_track_and_consumer() public void should_delete_metadata_files_when_they_are_for_the_same_album_and_consumer()
{ {
var files = Builder<MetadataFile>.CreateListOfSize(2) var files = Builder<MetadataFile>.CreateListOfSize(2)
.All() .All()
.With(m => m.Type = MetadataType.TrackMetadata) .With(m => m.Type = MetadataType.AlbumMetadata)
.With(m => m.TrackFileId = 1) .With(m => m.ArtistId = 1)
.With(m => m.AlbumId = 1)
.With(m => m.Consumer = "XbmcMetadata") .With(m => m.Consumer = "XbmcMetadata")
.BuildListOfNew(); .BuildListOfNew();
@ -109,10 +112,10 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_not_delete_metadata_files_when_there_is_only_one_for_that_track_and_consumer() public void should_not_delete_metadata_files_when_there_is_only_one_for_that_album_and_consumer()
{ {
var file = Builder<MetadataFile>.CreateNew() var file = Builder<MetadataFile>.CreateNew()
.BuildNew(); .BuildNew();
Db.Insert(file); Db.Insert(file);
Subject.Clean(); Subject.Clean();
@ -120,11 +123,11 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_not_delete_image_when_they_are_for_the_same_track_but_different_consumers() public void should_not_delete_metadata_files_when_they_are_for_the_same_track_but_different_consumers()
{ {
var files = Builder<MetadataFile>.CreateListOfSize(2) var files = Builder<MetadataFile>.CreateListOfSize(2)
.All() .All()
.With(m => m.Type = MetadataType.TrackImage) .With(m => m.Type = MetadataType.TrackMetadata)
.With(m => m.TrackFileId = 1) .With(m => m.TrackFileId = 1)
.BuildListOfNew(); .BuildListOfNew();
@ -134,11 +137,11 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_not_delete_image_for_different_track() public void should_not_delete_metadata_files_for_different_track()
{ {
var files = Builder<MetadataFile>.CreateListOfSize(2) var files = Builder<MetadataFile>.CreateListOfSize(2)
.All() .All()
.With(m => m.Type = MetadataType.TrackImage) .With(m => m.Type = MetadataType.TrackMetadata)
.With(m => m.Consumer = "XbmcMetadata") .With(m => m.Consumer = "XbmcMetadata")
.BuildListOfNew(); .BuildListOfNew();
@ -148,11 +151,11 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_delete_image_when_they_are_for_the_same_track_and_consumer() public void should_delete_metadata_files_when_they_are_for_the_same_track_and_consumer()
{ {
var files = Builder<MetadataFile>.CreateListOfSize(2) var files = Builder<MetadataFile>.CreateListOfSize(2)
.All() .All()
.With(m => m.Type = MetadataType.TrackImage) .With(m => m.Type = MetadataType.TrackMetadata)
.With(m => m.TrackFileId = 1) .With(m => m.TrackFileId = 1)
.With(m => m.Consumer = "XbmcMetadata") .With(m => m.Consumer = "XbmcMetadata")
.BuildListOfNew(); .BuildListOfNew();
@ -163,7 +166,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_not_delete_image_when_there_is_only_one_for_that_track_and_consumer() public void should_not_delete_metadata_files_when_there_is_only_one_for_that_track_and_consumer()
{ {
var file = Builder<MetadataFile>.CreateNew() var file = Builder<MetadataFile>.CreateNew()
.BuildNew(); .BuildNew();

@ -93,10 +93,15 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
var artist = Builder<Artist>.CreateNew() var artist = Builder<Artist>.CreateNew()
.BuildNew(); .BuildNew();
var album = Builder<Album>.CreateNew()
.BuildNew();
Db.Insert(artist); Db.Insert(artist);
Db.Insert(album);
var metadataFile = Builder<MetadataFile>.CreateNew() var metadataFile = Builder<MetadataFile>.CreateNew()
.With(m => m.ArtistId = artist.Id) .With(m => m.ArtistId = artist.Id)
.With(m => m.AlbumId = album.Id)
.With(m => m.TrackFileId = 10) .With(m => m.TrackFileId = 10)
.BuildNew(); .BuildNew();
@ -134,18 +139,39 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_delete_track_metadata_files_that_have_trackfileid_of_zero() public void should_delete_album_metadata_files_that_have_albumid_of_zero()
{ {
var artist = Builder<Artist>.CreateNew() var artist = Builder<Artist>.CreateNew()
.BuildNew(); .BuildNew();
Db.Insert(artist); Db.Insert(artist);
var metadataFile = Builder<MetadataFile>.CreateNew() var metadataFile = Builder<MetadataFile>.CreateNew()
.With(m => m.ArtistId = artist.Id) .With(m => m.ArtistId = artist.Id)
.With(m => m.Type = MetadataType.TrackMetadata) .With(m => m.Type = MetadataType.AlbumMetadata)
.With(m => m.TrackFileId = 0) .With(m => m.AlbumId = 0)
.BuildNew(); .With(m => m.TrackFileId = null)
.BuildNew();
Db.Insert(metadataFile);
Subject.Clean();
AllStoredModels.Should().HaveCount(0);
}
[Test]
public void should_delete_album_image_files_that_have_albumid_of_zero()
{
var artist = Builder<Artist>.CreateNew()
.BuildNew();
Db.Insert(artist);
var metadataFile = Builder<MetadataFile>.CreateNew()
.With(m => m.ArtistId = artist.Id)
.With(m => m.Type = MetadataType.AlbumImage)
.With(m => m.AlbumId = 0)
.With(m => m.TrackFileId = null)
.BuildNew();
Db.Insert(metadataFile); Db.Insert(metadataFile);
Subject.Clean(); Subject.Clean();
@ -153,7 +179,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
} }
[Test] [Test]
public void should_delete_track_image_files_that_have_trackfileid_of_zero() public void should_delete_track_metadata_files_that_have_trackfileid_of_zero()
{ {
var artist = Builder<Artist>.CreateNew() var artist = Builder<Artist>.CreateNew()
.BuildNew(); .BuildNew();
@ -161,10 +187,10 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
Db.Insert(artist); Db.Insert(artist);
var metadataFile = Builder<MetadataFile>.CreateNew() var metadataFile = Builder<MetadataFile>.CreateNew()
.With(m => m.ArtistId = artist.Id) .With(m => m.ArtistId = artist.Id)
.With(m => m.Type = MetadataType.TrackImage) .With(m => m.Type = MetadataType.TrackMetadata)
.With(m => m.TrackFileId = 0) .With(m => m.TrackFileId = 0)
.BuildNew(); .BuildNew();
Db.Insert(metadataFile); Db.Insert(metadataFile);
Subject.Clean(); Subject.Clean();

@ -13,66 +13,65 @@ namespace NzbDrone.Core.Test.Metadata.Consumers.Roksbox
[TestFixture] [TestFixture]
public class FindMetadataFileFixture : CoreTest<RoksboxMetadata> public class FindMetadataFileFixture : CoreTest<RoksboxMetadata>
{ {
private Artist _series; private Artist _artist;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_series = Builder<Artist>.CreateNew() _artist = Builder<Artist>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic()) .With(s => s.Path = @"C:\Test\Music\The.Artist".AsOsAgnostic())
.Build(); .Build();
} }
[Test] [Test]
public void should_return_null_if_filename_is_not_handled() public void should_return_null_if_filename_is_not_handled()
{ {
var path = Path.Combine(_series.Path, "file.jpg"); var path = Path.Combine(_artist.Path, "file.jpg");
Subject.FindMetadataFile(_series, path).Should().BeNull(); Subject.FindMetadataFile(_artist, path).Should().BeNull();
} }
[TestCase("Specials")] [TestCase("Specials")]
[TestCase("specials")] [TestCase("specials")]
[TestCase("Season 1")] [TestCase("Season 1")]
public void should_return_season_image(string folder) public void should_return_album_image(string folder)
{ {
var path = Path.Combine(_series.Path, folder, folder + ".jpg"); var path = Path.Combine(_artist.Path, folder, folder + ".jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.AlbumImage); Subject.FindMetadataFile(_artist, path).Type.Should().Be(MetadataType.AlbumImage);
} }
[TestCase(".xml", MetadataType.TrackMetadata)] [TestCase(".xml", MetadataType.TrackMetadata)]
[TestCase(".jpg", MetadataType.TrackImage)] public void should_return_metadata_for_track_if_valid_file_for_track(string extension, MetadataType type)
public void should_return_metadata_for_episode_if_valid_file_for_episode(string extension, MetadataType type)
{ {
var path = Path.Combine(_series.Path, "the.series.s01e01.episode" + extension); var path = Path.Combine(_artist.Path, "the.artist.s01e01.track" + extension);
Subject.FindMetadataFile(_series, path).Type.Should().Be(type); Subject.FindMetadataFile(_artist, path).Type.Should().Be(type);
} }
[TestCase(".xml")] [TestCase(".xml")]
[TestCase(".jpg")] [TestCase(".jpg")]
public void should_return_null_if_not_valid_file_for_episode(string extension) public void should_return_null_if_not_valid_file_for_track(string extension)
{ {
var path = Path.Combine(_series.Path, "the.series.episode" + extension); var path = Path.Combine(_artist.Path, "the.artist.track" + extension);
Subject.FindMetadataFile(_series, path).Should().BeNull(); Subject.FindMetadataFile(_artist, path).Should().BeNull();
} }
[Test] [Test]
public void should_not_return_metadata_if_image_file_is_a_thumb() public void should_not_return_metadata_if_image_file_is_a_thumb()
{ {
var path = Path.Combine(_series.Path, "the.series.s01e01.episode-thumb.jpg"); var path = Path.Combine(_artist.Path, "the.artist.s01e01.track-thumb.jpg");
Subject.FindMetadataFile(_series, path).Should().BeNull(); Subject.FindMetadataFile(_artist, path).Should().BeNull();
} }
[Test] [Test]
public void should_return_series_image_for_folder_jpg_in_series_folder() public void should_return_artist_image_for_folder_jpg_in_artist_folder()
{ {
var path = Path.Combine(_series.Path, new DirectoryInfo(_series.Path).Name + ".jpg"); var path = Path.Combine(_artist.Path, new DirectoryInfo(_artist.Path).Name + ".jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.ArtistImage); Subject.FindMetadataFile(_artist, path).Type.Should().Be(MetadataType.ArtistImage);
} }
} }
} }

@ -13,58 +13,57 @@ namespace NzbDrone.Core.Test.Metadata.Consumers.Wdtv
[TestFixture] [TestFixture]
public class FindMetadataFileFixture : CoreTest<WdtvMetadata> public class FindMetadataFileFixture : CoreTest<WdtvMetadata>
{ {
private Artist _series; private Artist _artist;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_series = Builder<Artist>.CreateNew() _artist = Builder<Artist>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\The.Series".AsOsAgnostic()) .With(s => s.Path = @"C:\Test\Music\The.Artist".AsOsAgnostic())
.Build(); .Build();
} }
[Test] [Test]
public void should_return_null_if_filename_is_not_handled() public void should_return_null_if_filename_is_not_handled()
{ {
var path = Path.Combine(_series.Path, "file.jpg"); var path = Path.Combine(_artist.Path, "file.jpg");
Subject.FindMetadataFile(_series, path).Should().BeNull(); Subject.FindMetadataFile(_artist, path).Should().BeNull();
} }
[TestCase("Specials")] [TestCase("Specials")]
[TestCase("specials")] [TestCase("specials")]
[TestCase("Season 1")] [TestCase("Season 1")]
public void should_return_season_image(string folder) public void should_return_album_image(string folder)
{ {
var path = Path.Combine(_series.Path, folder, "folder.jpg"); var path = Path.Combine(_artist.Path, folder, "folder.jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.AlbumImage); Subject.FindMetadataFile(_artist, path).Type.Should().Be(MetadataType.AlbumImage);
} }
[TestCase(".xml", MetadataType.TrackMetadata)] [TestCase(".xml", MetadataType.TrackMetadata)]
[TestCase(".metathumb", MetadataType.TrackImage)] public void should_return_metadata_for_track_if_valid_file_for_track(string extension, MetadataType type)
public void should_return_metadata_for_episode_if_valid_file_for_episode(string extension, MetadataType type)
{ {
var path = Path.Combine(_series.Path, "the.series.s01e01.episode" + extension); var path = Path.Combine(_artist.Path, "the.artist.s01e01.track" + extension);
Subject.FindMetadataFile(_series, path).Type.Should().Be(type); Subject.FindMetadataFile(_artist, path).Type.Should().Be(type);
} }
[TestCase(".xml")] [TestCase(".xml")]
[TestCase(".metathumb")] [TestCase(".metathumb")]
public void should_return_null_if_not_valid_file_for_episode(string extension) public void should_return_null_if_not_valid_file_for_track(string extension)
{ {
var path = Path.Combine(_series.Path, "the.series.episode" + extension); var path = Path.Combine(_artist.Path, "the.artist.track" + extension);
Subject.FindMetadataFile(_series, path).Should().BeNull(); Subject.FindMetadataFile(_artist, path).Should().BeNull();
} }
[Test] [Test]
public void should_return_series_image_for_folder_jpg_in_series_folder() public void should_return_artist_image_for_folder_jpg_in_artist_folder()
{ {
var path = Path.Combine(_series.Path, "folder.jpg"); var path = Path.Combine(_artist.Path, "folder.jpg");
Subject.FindMetadataFile(_series, path).Type.Should().Be(MetadataType.ArtistImage); Subject.FindMetadataFile(_artist, path).Type.Should().Be(MetadataType.ArtistImage);
} }
} }
} }

@ -1,4 +1,4 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
@ -37,7 +37,6 @@ namespace NzbDrone.Core.Test.OrganizerTests
var fakeAlbum = Builder<Album>.CreateNew() var fakeAlbum = Builder<Album>.CreateNew()
.With(s => s.Title = "Fake: Album") .With(s => s.Title = "Fake: Album")
.With(s => s.Path = @"C:\Test\Fake- The Artist\Fake- Album".AsOsAgnostic())
.Build(); .Build();
namingConfig.AlbumFolderFormat = "{Artist Name} {Album Title}"; namingConfig.AlbumFolderFormat = "{Artist Name} {Album Title}";
@ -45,4 +44,4 @@ namespace NzbDrone.Core.Test.OrganizerTests
Subject.BuildTrackFilePath(fakeArtist, fakeAlbum, filename, ".flac").Should().Be(expectedPath.AsOsAgnostic()); Subject.BuildTrackFilePath(fakeArtist, fakeAlbum, filename, ".flac").Should().Be(expectedPath.AsOsAgnostic());
} }
} }
} }

@ -0,0 +1,14 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(7)]
public class change_album_path_to_relative : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Delete.Column("Path").FromTable("Albums");
}
}
}

@ -27,7 +27,6 @@ namespace NzbDrone.Core.Extras
IHandle<ArtistRenamedEvent> IHandle<ArtistRenamedEvent>
{ {
private readonly IMediaFileService _mediaFileService; private readonly IMediaFileService _mediaFileService;
//private readonly IEpisodeService _episodeService;
private readonly IAlbumService _albumService; private readonly IAlbumService _albumService;
private readonly ITrackService _trackService; private readonly ITrackService _trackService;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
@ -36,7 +35,6 @@ namespace NzbDrone.Core.Extras
private readonly Logger _logger; private readonly Logger _logger;
public ExtraService(IMediaFileService mediaFileService, public ExtraService(IMediaFileService mediaFileService,
//IEpisodeService episodeService,
IAlbumService albumService, IAlbumService albumService,
ITrackService trackService, ITrackService trackService,
IDiskProvider diskProvider, IDiskProvider diskProvider,
@ -45,7 +43,6 @@ namespace NzbDrone.Core.Extras
Logger logger) Logger logger)
{ {
_mediaFileService = mediaFileService; _mediaFileService = mediaFileService;
//_episodeService = episodeService;
_albumService = albumService; _albumService = albumService;
_trackService = trackService; _trackService = trackService;
_diskProvider = diskProvider; _diskProvider = diskProvider;
@ -111,22 +108,23 @@ namespace NzbDrone.Core.Extras
public void Handle(MediaCoversUpdatedEvent message) public void Handle(MediaCoversUpdatedEvent message)
{ {
var artist = message.Artist; var artist = message.Artist;
var albums = _albumService.GetAlbumsByArtist(artist.Id);
var trackFiles = GetTrackFiles(artist.Id); var trackFiles = GetTrackFiles(artist.Id);
foreach (var extraFileManager in _extraFileManagers) foreach (var extraFileManager in _extraFileManagers)
{ {
extraFileManager.CreateAfterArtistScan(artist, albums, trackFiles); extraFileManager.CreateAfterArtistScan(artist, trackFiles);
} }
} }
public void Handle(TrackFolderCreatedEvent message) public void Handle(TrackFolderCreatedEvent message)
{ {
var artist = message.Artist; var artist = message.Artist;
var album = _albumService.GetAlbum(message.TrackFile.AlbumId);
foreach (var extraFileManager in _extraFileManagers) foreach (var extraFileManager in _extraFileManagers)
{ {
extraFileManager.CreateAfterTrackImport(artist, message.ArtistFolder, message.AlbumFolder); extraFileManager.CreateAfterTrackImport(artist, album, message.ArtistFolder, message.AlbumFolder);
} }
} }

@ -14,9 +14,9 @@ namespace NzbDrone.Core.Extras.Files
public interface IManageExtraFiles public interface IManageExtraFiles
{ {
int Order { get; } int Order { get; }
IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<Album> albums, List<TrackFile> trackFiles); IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<TrackFile> trackFiles);
IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, TrackFile trackFile); IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, TrackFile trackFile);
IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, string artistFolder, string albumFolder); IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, Album album, string artistFolder, string albumFolder);
IEnumerable<ExtraFile> MoveFilesAfterRename(Artist artist, List<TrackFile> trackFiles); IEnumerable<ExtraFile> MoveFilesAfterRename(Artist artist, List<TrackFile> trackFiles);
ExtraFile Import(Artist artist, TrackFile trackFile, string path, string extension, bool readOnly); ExtraFile Import(Artist artist, TrackFile trackFile, string path, string extension, bool readOnly);
} }
@ -42,9 +42,9 @@ namespace NzbDrone.Core.Extras.Files
} }
public abstract int Order { get; } public abstract int Order { get; }
public abstract IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<Album> albums, List<TrackFile> trackFiles); public abstract IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<TrackFile> trackFiles);
public abstract IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, TrackFile trackFile); public abstract IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, TrackFile trackFile);
public abstract IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, string artistFolder, string albumFolder); public abstract IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, Album album, string artistFolder, string albumFolder);
public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Artist artist, List<TrackFile> trackFiles); public abstract IEnumerable<ExtraFile> MoveFilesAfterRename(Artist artist, List<TrackFile> trackFiles);
public abstract ExtraFile Import(Artist artist, TrackFile trackFile, string path, string extension, bool readOnly); public abstract ExtraFile Import(Artist artist, TrackFile trackFile, string path, string extension, bool readOnly);

@ -32,7 +32,7 @@ namespace NzbDrone.Core.Extras.Lyrics
public override int Order => 1; public override int Order => 1;
public override IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<Album> albums, List<TrackFile> trackFiles) public override IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<TrackFile> trackFiles)
{ {
return Enumerable.Empty<LyricFile>(); return Enumerable.Empty<LyricFile>();
} }
@ -42,7 +42,7 @@ namespace NzbDrone.Core.Extras.Lyrics
return Enumerable.Empty<LyricFile>(); return Enumerable.Empty<LyricFile>();
} }
public override IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, string artistFolder, string albumFolder) public override IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, Album album, string artistFolder, string albumFolder)
{ {
return Enumerable.Empty<LyricFile>(); return Enumerable.Empty<LyricFile>();
} }

@ -73,7 +73,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.MediaBrowser
artistElement.Add(new XElement("LocalTitle", artist.Name)); artistElement.Add(new XElement("LocalTitle", artist.Name));
artistElement.Add(new XElement("Rating", artist.Ratings.Value)); artistElement.Add(new XElement("Rating", artist.Ratings.Value));
artistElement.Add(new XElement("Genres", artist.Genres.Select(genre => new XElement("Genre", genre))));
var persons = new XElement("Persons"); var persons = new XElement("Persons");
@ -98,7 +97,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.MediaBrowser
} }
} }
public override MetadataFileResult AlbumMetadata(Artist artist, Album album) public override MetadataFileResult AlbumMetadata(Artist artist, Album album, string albumPath)
{ {
return null; return null;
} }
@ -113,7 +112,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.MediaBrowser
return new List<ImageFileResult>(); return new List<ImageFileResult>();
} }
public override List<ImageFileResult> AlbumImages(Artist artist, Album season) public override List<ImageFileResult> AlbumImages(Artist artist, Album album, string albumFolder)
{ {
return new List<ImageFileResult>(); return new List<ImageFileResult>();
} }

@ -7,9 +7,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.MediaBrowser
{ {
public class MediaBrowserSettingsValidator : AbstractValidator<MediaBrowserMetadataSettings> public class MediaBrowserSettingsValidator : AbstractValidator<MediaBrowserMetadataSettings>
{ {
public MediaBrowserSettingsValidator()
{
}
} }
public class MediaBrowserMetadataSettings : IProviderConfig public class MediaBrowserMetadataSettings : IProviderConfig

@ -31,7 +31,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
_logger = logger; _logger = logger;
} }
private static List<string> ValidCertification = new List<string> { "G", "NC-17", "PG", "PG-13", "R", "UR", "UNRATED", "NR", "TV-Y", "TV-Y7", "TV-Y7-FV", "TV-G", "TV-PG", "TV-14", "TV-MA" };
private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?<season>\d+))|(?<specials>specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex SeasonImagesRegex = new Regex(@"^(season (?<season>\d+))|(?<specials>specials)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public override string Name => "Roksbox"; public override string Name => "Roksbox";
@ -40,11 +39,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
{ {
var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath); var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath);
if (metadataFile.Type == MetadataType.TrackImage)
{
return GetTrackImageFilename(trackFilePath);
}
if (metadataFile.Type == MetadataType.TrackMetadata) if (metadataFile.Type == MetadataType.TrackMetadata)
{ {
return GetTrackMetadataFilename(trackFilePath); return GetTrackMetadataFilename(trackFilePath);
@ -104,16 +98,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
{ {
metadata.Type = MetadataType.TrackMetadata; metadata.Type = MetadataType.TrackMetadata;
return metadata; return metadata;
} }
if (extension == ".jpg")
{
if (!Path.GetFileNameWithoutExtension(filename).EndsWith("-thumb"))
{
metadata.Type = MetadataType.TrackImage;
return metadata;
}
}
} }
return null; return null;
@ -125,14 +110,14 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
return null; return null;
} }
public override MetadataFileResult AlbumMetadata(Artist artist, Album album) public override MetadataFileResult AlbumMetadata(Artist artist, Album album, string albumPath)
{ {
return null; return null;
} }
public override MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile) public override MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile)
{ {
if (!Settings.EpisodeMetadata) if (!Settings.TrackMetadata)
{ {
return null; return null;
} }
@ -151,11 +136,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
{ {
var doc = new XDocument(); var doc = new XDocument();
var details = new XElement("video"); var details = new XElement("song");
details.Add(new XElement("title", string.Format("{0} - {1} - {2}", artist.Name, track.TrackNumber, track.Title))); details.Add(new XElement("title", track.Title));
details.Add(new XElement("genre", string.Join(" / ", artist.Genres))); details.Add(new XElement("performingartist", artist.Name));
var actors = string.Join(" , ", artist.Members.ConvertAll(c => c.Name + " - " + c.Instrument).GetRange(0, Math.Min(3, artist.Members.Count)));
details.Add(new XElement("actors", actors));
doc.Add(details); doc.Add(details);
doc.Save(xw); doc.Save(xw);
@ -188,34 +171,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
return new List<ImageFileResult>{ new ImageFileResult(destination, source) }; return new List<ImageFileResult>{ new ImageFileResult(destination, source) };
} }
public override List<ImageFileResult> AlbumImages(Artist artist, Album album) public override List<ImageFileResult> AlbumImages(Artist artist, Album album, string albumFolder)
{ {
if (!Settings.AlbumImages) return new List<ImageFileResult>();
{
return new List<ImageFileResult>();
}
var albumFolders = GetAlbumFolders(artist);
string albumFolder;
if (!albumFolders.TryGetValue(album.ArtistId, out albumFolder))
{
_logger.Trace("Failed to find album folder for artit {0}, album {1}.", artist.Name, album.Title);
return new List<ImageFileResult>();
}
//Roksbox only supports one season image, so first of all try for poster otherwise just use whatever is first in the collection
var image = album.Images.SingleOrDefault(c => c.CoverType == MediaCoverTypes.Poster) ?? album.Images.FirstOrDefault();
if (image == null)
{
_logger.Trace("Failed to find suitable album image for artist {0}, album {1}.", artist.Name, album.Title);
return new List<ImageFileResult>();
}
var filename = Path.GetFileName(albumFolder) + ".jpg";
var path = artist.Path.GetRelativePath(Path.Combine(artist.Path, albumFolder, filename));
return new List<ImageFileResult> { new ImageFileResult(path, image.Url) };
} }
public override List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile) public override List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile)

@ -7,9 +7,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
{ {
public class RoksboxSettingsValidator : AbstractValidator<RoksboxMetadataSettings> public class RoksboxSettingsValidator : AbstractValidator<RoksboxMetadataSettings>
{ {
public RoksboxSettingsValidator()
{
}
} }
public class RoksboxMetadataSettings : IProviderConfig public class RoksboxMetadataSettings : IProviderConfig
@ -18,23 +15,19 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
public RoksboxMetadataSettings() public RoksboxMetadataSettings()
{ {
EpisodeMetadata = true; TrackMetadata = true;
ArtistImages = true; ArtistImages = true;
AlbumImages = true; AlbumImages = true;
EpisodeImages = true;
} }
[FieldDefinition(0, Label = "Episode Metadata", Type = FieldType.Checkbox, HelpText = "Season##\\filename.xml")] [FieldDefinition(0, Label = "Track Metadata", Type = FieldType.Checkbox, HelpText = "Season##\\filename.xml")]
public bool EpisodeMetadata { get; set; } public bool TrackMetadata { get; set; }
[FieldDefinition(1, Label = "Artist Images", Type = FieldType.Checkbox, HelpText = "Artist Title.jpg")] [FieldDefinition(1, Label = "Artist Images", Type = FieldType.Checkbox, HelpText = "Artist Title.jpg")]
public bool ArtistImages { get; set; } public bool ArtistImages { get; set; }
[FieldDefinition(2, Label = "Album Images", Type = FieldType.Checkbox, HelpText = "Album Title.jpg")] [FieldDefinition(2, Label = "Album Images", Type = FieldType.Checkbox, HelpText = "Album Title.jpg")]
public bool AlbumImages { get; set; } public bool AlbumImages { get; set; }
[FieldDefinition(3, Label = "Episode Images", Type = FieldType.Checkbox, HelpText = "Season##\\filename.jpg")]
public bool EpisodeImages { get; set; }
public bool IsValid => true; public bool IsValid => true;

@ -39,11 +39,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
{ {
var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath); var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath);
if (metadataFile.Type == MetadataType.TrackImage)
{
return GetTrackImageFilename(trackFilePath);
}
if (metadataFile.Type == MetadataType.TrackMetadata) if (metadataFile.Type == MetadataType.TrackMetadata)
{ {
return GetTrackMetadataFilename(trackFilePath); return GetTrackMetadataFilename(trackFilePath);
@ -102,9 +97,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
case ".xml": case ".xml":
metadata.Type = MetadataType.TrackMetadata; metadata.Type = MetadataType.TrackMetadata;
return metadata; return metadata;
case ".metathumb":
metadata.Type = MetadataType.TrackImage;
return metadata;
} }
} }
@ -118,14 +110,14 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
return null; return null;
} }
public override MetadataFileResult AlbumMetadata(Artist artist, Album album) public override MetadataFileResult AlbumMetadata(Artist artist, Album album, string albumPath)
{ {
return null; return null;
} }
public override MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile) public override MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile)
{ {
if (!Settings.EpisodeMetadata) if (!Settings.TrackMetadata)
{ {
return null; return null;
} }
@ -150,7 +142,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
details.Add(new XElement("artist_name", artist.Name)); details.Add(new XElement("artist_name", artist.Name));
details.Add(new XElement("track_name", track.Title)); details.Add(new XElement("track_name", track.Title));
details.Add(new XElement("track_number", track.AbsoluteTrackNumber.ToString("00"))); details.Add(new XElement("track_number", track.AbsoluteTrackNumber.ToString("00")));
details.Add(new XElement("genre", string.Join(" / ", artist.Genres)));
details.Add(new XElement("member", string.Join(" / ", artist.Members.ConvertAll(c => c.Name + " - " + c.Instrument)))); details.Add(new XElement("member", string.Join(" / ", artist.Members.ConvertAll(c => c.Name + " - " + c.Instrument))));
@ -195,7 +186,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
}; };
} }
public override List<ImageFileResult> AlbumImages(Artist artist, Album album) public override List<ImageFileResult> AlbumImages(Artist artist, Album album, string albumFolder)
{ {
if (!Settings.AlbumImages) if (!Settings.AlbumImages)
{ {

@ -7,9 +7,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
{ {
public class WdtvSettingsValidator : AbstractValidator<WdtvMetadataSettings> public class WdtvSettingsValidator : AbstractValidator<WdtvMetadataSettings>
{ {
public WdtvSettingsValidator()
{
}
} }
public class WdtvMetadataSettings : IProviderConfig public class WdtvMetadataSettings : IProviderConfig
@ -18,23 +15,19 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
public WdtvMetadataSettings() public WdtvMetadataSettings()
{ {
EpisodeMetadata = true; TrackMetadata = true;
ArtistImages = true; ArtistImages = true;
AlbumImages = true; AlbumImages = true;
EpisodeImages = true;
} }
[FieldDefinition(0, Label = "Episode Metadata", Type = FieldType.Checkbox)] [FieldDefinition(0, Label = "Track Metadata", Type = FieldType.Checkbox)]
public bool EpisodeMetadata { get; set; } public bool TrackMetadata { get; set; }
[FieldDefinition(1, Label = "Artist Images", Type = FieldType.Checkbox)] [FieldDefinition(1, Label = "Artist Images", Type = FieldType.Checkbox)]
public bool ArtistImages { get; set; } public bool ArtistImages { get; set; }
[FieldDefinition(2, Label = "Album Images", Type = FieldType.Checkbox)] [FieldDefinition(2, Label = "Album Images", Type = FieldType.Checkbox)]
public bool AlbumImages { get; set; } public bool AlbumImages { get; set; }
[FieldDefinition(3, Label = "Episode Images", Type = FieldType.Checkbox)]
public bool EpisodeImages { get; set; }
public bool IsValid => true; public bool IsValid => true;

@ -27,9 +27,8 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
_logger = logger; _logger = logger;
} }
private static readonly Regex ArtistImagesRegex = new Regex(@"^(?<type>poster|banner|fanart|logo)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex ArtistImagesRegex = new Regex(@"^(?<type>poster|banner|fanart|logo)\.(?:png|jpg|jpeg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex AlbumImagesRegex = new Regex(@"^season(?<season>\d{2,}|-all|-specials)-(?<type>poster|banner|fanart|cover)\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex AlbumImagesRegex = new Regex(@"^(?<type>cover|disc)\.(?:png|jpg|jpeg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex EpisodeImageRegex = new Regex(@"-thumb\.(?:png|jpg)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public override string Name => "Kodi (XBMC) / Emby"; public override string Name => "Kodi (XBMC) / Emby";
@ -37,14 +36,9 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
{ {
var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath); var trackFilePath = Path.Combine(artist.Path, trackFile.RelativePath);
if (metadataFile.Type == MetadataType.TrackImage)
{
return GetEpisodeImageFilename(trackFilePath);
}
if (metadataFile.Type == MetadataType.TrackMetadata) if (metadataFile.Type == MetadataType.TrackMetadata)
{ {
return GetEpisodeMetadataFilename(trackFilePath); return GetTrackMetadataFilename(trackFilePath);
} }
_logger.Debug("Unknown episode file metadata: {0}", metadataFile.RelativePath); _logger.Debug("Unknown episode file metadata: {0}", metadataFile.RelativePath);
@ -70,36 +64,11 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
return metadata; return metadata;
} }
var seasonMatch = AlbumImagesRegex.Match(filename); var albumMatch = AlbumImagesRegex.Match(filename);
if (seasonMatch.Success) if (albumMatch.Success)
{ {
metadata.Type = MetadataType.AlbumImage; metadata.Type = MetadataType.AlbumImage;
var seasonNumberMatch = seasonMatch.Groups["season"].Value;
int seasonNumber;
if (seasonNumberMatch.Contains("specials"))
{
metadata.AlbumId = 0;
}
else if (int.TryParse(seasonNumberMatch, out seasonNumber))
{
metadata.AlbumId = seasonNumber;
}
else
{
return null;
}
return metadata;
}
if (EpisodeImageRegex.IsMatch(filename))
{
metadata.Type = MetadataType.TrackImage;
return metadata; return metadata;
} }
@ -186,7 +155,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
} }
} }
public override MetadataFileResult AlbumMetadata(Artist artist, Album album) public override MetadataFileResult AlbumMetadata(Artist artist, Album album, string albumPath)
{ {
if (!Settings.AlbumMetadata) if (!Settings.AlbumMetadata)
{ {
@ -217,9 +186,11 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
var doc = new XDocument(albumElement); var doc = new XDocument(albumElement);
doc.Save(xw); doc.Save(xw);
_logger.Debug("Saving album.nfo for {0}", artist.Name); _logger.Debug("Saving album.nfo for {0}", album.Title);
var fileName = Path.Combine(albumPath, "album.nfo");
return new MetadataFileResult("album.nfo", doc.ToString()); return new MetadataFileResult(fileName, doc.ToString());
} }
} }
@ -311,7 +282,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
} }
} }
return new MetadataFileResult(GetEpisodeMetadataFilename(trackFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray())); return new MetadataFileResult(GetTrackMetadataFilename(trackFile.RelativePath), xmlResult.Trim(Environment.NewLine.ToCharArray()));
} }
public override List<ImageFileResult> ArtistImages(Artist artist) public override List<ImageFileResult> ArtistImages(Artist artist)
@ -324,14 +295,14 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
return ProcessArtistImages(artist).ToList(); return ProcessArtistImages(artist).ToList();
} }
public override List<ImageFileResult> AlbumImages(Artist artist, Album album) public override List<ImageFileResult> AlbumImages(Artist artist, Album album, string albumPath)
{ {
if (!Settings.AlbumImages) if (!Settings.AlbumImages)
{ {
return new List<ImageFileResult>(); return new List<ImageFileResult>();
} }
return ProcessAlbumImages(album).ToList(); return ProcessAlbumImages(artist, album, albumPath).ToList();
} }
public override List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile) public override List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile)
@ -351,24 +322,21 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
} }
} }
private IEnumerable<ImageFileResult> ProcessAlbumImages(Album album) private IEnumerable<ImageFileResult> ProcessAlbumImages(Artist artist, Album album, string albumPath)
{ {
foreach (var image in album.Images) foreach (var image in album.Images)
{ {
var destination = image.CoverType.ToString().ToLowerInvariant() + Path.GetExtension(image.Url); // TODO: Make Source fallback to URL if local does not exist
// var source = _mediaCoverService.GetCoverPath(album.ArtistId, image.CoverType, null, album.Id);
var destination = Path.Combine(albumPath, image.CoverType.ToString().ToLowerInvariant() + Path.GetExtension(image.Url));
yield return new ImageFileResult(destination, image.Url); yield return new ImageFileResult(destination, image.Url);
} }
} }
private string GetEpisodeMetadataFilename(string episodeFilePath) private string GetTrackMetadataFilename(string trackFilePath)
{
return Path.ChangeExtension(episodeFilePath, "nfo");
}
private string GetEpisodeImageFilename(string episodeFilePath)
{ {
return Path.ChangeExtension(episodeFilePath, "").Trim('.') + "-thumb.jpg"; return Path.ChangeExtension(trackFilePath, "nfo");
} }
private string GetAudioCodec(string audioCodec) private string GetAudioCodec(string audioCodec)

@ -7,9 +7,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
{ {
public class XbmcSettingsValidator : AbstractValidator<XbmcMetadataSettings> public class XbmcSettingsValidator : AbstractValidator<XbmcMetadataSettings>
{ {
public XbmcSettingsValidator()
{
}
} }
public class XbmcMetadataSettings : IProviderConfig public class XbmcMetadataSettings : IProviderConfig
@ -23,7 +20,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
TrackMetadata = true; TrackMetadata = true;
ArtistImages = true; ArtistImages = true;
AlbumImages = true; AlbumImages = true;
EpisodeImages = true;
} }
[FieldDefinition(0, Label = "Artist Metadata", Type = FieldType.Checkbox)] [FieldDefinition(0, Label = "Artist Metadata", Type = FieldType.Checkbox)]
@ -41,9 +37,6 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
[FieldDefinition(4, Label = "Album Images", Type = FieldType.Checkbox)] [FieldDefinition(4, Label = "Album Images", Type = FieldType.Checkbox)]
public bool AlbumImages { get; set; } public bool AlbumImages { get; set; }
[FieldDefinition(5, Label = "Episode Images", Type = FieldType.Checkbox)]
public bool EpisodeImages { get; set; }
public bool IsValid => true; public bool IsValid => true;
public NzbDroneValidationResult Validate() public NzbDroneValidationResult Validate()

@ -57,8 +57,20 @@ namespace NzbDrone.Core.Extras.Metadata
continue; continue;
} }
if (metadata.Type == MetadataType.TrackImage || if (metadata.Type == MetadataType.AlbumImage || metadata.Type == MetadataType.AlbumMetadata)
metadata.Type == MetadataType.TrackMetadata) {
var localAlbum = _parsingService.GetLocalAlbum(possibleMetadataFile, artist);
if (localAlbum == null)
{
_logger.Debug("Extra file folder has multiple Albums: {0}", possibleMetadataFile);
continue;
}
metadata.AlbumId = localAlbum.Id;
}
if (metadata.Type == MetadataType.TrackMetadata)
{ {
var localTrack = _parsingService.GetLocalTrack(possibleMetadataFile, artist); var localTrack = _parsingService.GetLocalTrack(possibleMetadataFile, artist);
@ -70,7 +82,7 @@ namespace NzbDrone.Core.Extras.Metadata
if (localTrack.Tracks.Empty()) if (localTrack.Tracks.Empty())
{ {
_logger.Debug("Cannot find related episodes for: {0}", possibleMetadataFile); _logger.Debug("Cannot find related tracks for: {0}", possibleMetadataFile);
continue; continue;
} }
@ -79,8 +91,7 @@ namespace NzbDrone.Core.Extras.Metadata
_logger.Debug("Extra file: {0} does not match existing files.", possibleMetadataFile); _logger.Debug("Extra file: {0} does not match existing files.", possibleMetadataFile);
continue; continue;
} }
metadata.AlbumId = localTrack.Album.Id;
metadata.TrackFileId = localTrack.Tracks.First().TrackFileId; metadata.TrackFileId = localTrack.Tracks.First().TrackFileId;
} }

@ -9,12 +9,13 @@ namespace NzbDrone.Core.Extras.Metadata
public interface IMetadata : IProvider public interface IMetadata : IProvider
{ {
string GetFilenameAfterMove(Artist artist, TrackFile trackFile, MetadataFile metadataFile); string GetFilenameAfterMove(Artist artist, TrackFile trackFile, MetadataFile metadataFile);
string GetFilenameAfterMove(Artist artist, string albumPath, MetadataFile metadataFile);
MetadataFile FindMetadataFile(Artist artist, string path); MetadataFile FindMetadataFile(Artist artist, string path);
MetadataFileResult ArtistMetadata(Artist artist); MetadataFileResult ArtistMetadata(Artist artist);
MetadataFileResult AlbumMetadata(Artist artist, Album album); MetadataFileResult AlbumMetadata(Artist artist, Album album, string albumPath);
MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile); MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile);
List<ImageFileResult> ArtistImages(Artist artist); List<ImageFileResult> ArtistImages(Artist artist);
List<ImageFileResult> AlbumImages(Artist artist, Album album); List<ImageFileResult> AlbumImages(Artist artist, Album album, string albumPath);
List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile); List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile);
} }
} }

@ -35,13 +35,21 @@ namespace NzbDrone.Core.Extras.Metadata
return newFileName; return newFileName;
} }
public virtual string GetFilenameAfterMove(Artist artist, string albumPath, MetadataFile metadataFile)
{
var existingFilename = Path.GetFileName(metadataFile.RelativePath);
var newFileName = Path.Combine(artist.Path, albumPath, existingFilename);
return newFileName;
}
public abstract MetadataFile FindMetadataFile(Artist artist, string path); public abstract MetadataFile FindMetadataFile(Artist artist, string path);
public abstract MetadataFileResult ArtistMetadata(Artist artist); public abstract MetadataFileResult ArtistMetadata(Artist artist);
public abstract MetadataFileResult AlbumMetadata(Artist artist, Album album); public abstract MetadataFileResult AlbumMetadata(Artist artist, Album album, string albumPath);
public abstract MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile); public abstract MetadataFileResult TrackMetadata(Artist artist, TrackFile trackFile);
public abstract List<ImageFileResult> ArtistImages(Artist artist); public abstract List<ImageFileResult> ArtistImages(Artist artist);
public abstract List<ImageFileResult> AlbumImages(Artist artist, Album album); public abstract List<ImageFileResult> AlbumImages(Artist artist, Album album, string albumPath);
public abstract List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile); public abstract List<ImageFileResult> TrackImages(Artist artist, TrackFile trackFile);
public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; } public virtual object RequestAction(string action, IDictionary<string, string> query) { return null; }

@ -12,6 +12,7 @@ using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.Extras.Metadata.Files; using NzbDrone.Core.Extras.Metadata.Files;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
using NzbDrone.Core.Organizer;
namespace NzbDrone.Core.Extras.Metadata namespace NzbDrone.Core.Extras.Metadata
{ {
@ -52,7 +53,7 @@ namespace NzbDrone.Core.Extras.Metadata
public override int Order => 0; public override int Order => 0;
public override IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<Album> albums, List<TrackFile> trackFiles) public override IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<TrackFile> trackFiles)
{ {
var metadataFiles = _metadataFileService.GetFilesByArtist(artist.Id); var metadataFiles = _metadataFileService.GetFilesByArtist(artist.Id);
_cleanMetadataService.Clean(artist); _cleanMetadataService.Clean(artist);
@ -71,18 +72,20 @@ namespace NzbDrone.Core.Extras.Metadata
files.AddIfNotNull(ProcessArtistMetadata(consumer, artist, consumerFiles)); files.AddIfNotNull(ProcessArtistMetadata(consumer, artist, consumerFiles));
files.AddRange(ProcessArtistImages(consumer, artist, consumerFiles)); files.AddRange(ProcessArtistImages(consumer, artist, consumerFiles));
files.AddRange(ProcessAlbumImages(consumer, artist, consumerFiles));
var albumGroups = trackFiles.GroupBy(s => Path.GetDirectoryName(s.RelativePath)).ToList();
foreach (var album in albums) foreach (var group in albumGroups)
{ {
album.Artist = artist; var album = _albumService.GetAlbum(group.First().AlbumId);
files.AddIfNotNull(ProcessAlbumMetadata(consumer, album, consumerFiles)); var albumFolder = group.Key;
} files.AddIfNotNull(ProcessAlbumMetadata(consumer, artist, album, albumFolder, consumerFiles));
files.AddRange(ProcessAlbumImages(consumer, artist, album, albumFolder, consumerFiles));
foreach (var trackFile in trackFiles) foreach (var trackFile in group)
{ {
files.AddIfNotNull(ProcessEpisodeMetadata(consumer, artist, trackFile, consumerFiles)); files.AddIfNotNull(ProcessTrackMetadata(consumer, artist, trackFile, consumerFiles));
files.AddRange(ProcessEpisodeImages(consumer, artist, trackFile, consumerFiles)); }
} }
} }
@ -97,9 +100,7 @@ namespace NzbDrone.Core.Extras.Metadata
foreach (var consumer in _metadataFactory.Enabled()) foreach (var consumer in _metadataFactory.Enabled())
{ {
files.AddIfNotNull(ProcessTrackMetadata(consumer, artist, trackFile, new List<MetadataFile>()));
files.AddIfNotNull(ProcessEpisodeMetadata(consumer, artist, trackFile, new List<MetadataFile>()));
files.AddRange(ProcessEpisodeImages(consumer, artist, trackFile, new List<MetadataFile>()));
} }
_metadataFileService.Upsert(files); _metadataFileService.Upsert(files);
@ -107,7 +108,7 @@ namespace NzbDrone.Core.Extras.Metadata
return files; return files;
} }
public override IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, string artistFolder, string albumFolder) public override IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, Album album, string artistFolder, string albumFolder)
{ {
var metadataFiles = _metadataFileService.GetFilesByArtist(artist.Id); var metadataFiles = _metadataFileService.GetFilesByArtist(artist.Id);
@ -127,11 +128,6 @@ namespace NzbDrone.Core.Extras.Metadata
files.AddIfNotNull(ProcessArtistMetadata(consumer, artist, consumerFiles)); files.AddIfNotNull(ProcessArtistMetadata(consumer, artist, consumerFiles));
files.AddRange(ProcessArtistImages(consumer, artist, consumerFiles)); files.AddRange(ProcessArtistImages(consumer, artist, consumerFiles));
} }
if (albumFolder.IsNotNullOrWhiteSpace())
{
files.AddRange(ProcessAlbumImages(consumer, artist, consumerFiles));
}
} }
_metadataFileService.Upsert(files); _metadataFileService.Upsert(files);
@ -143,12 +139,42 @@ namespace NzbDrone.Core.Extras.Metadata
{ {
var metadataFiles = _metadataFileService.GetFilesByArtist(artist.Id); var metadataFiles = _metadataFileService.GetFilesByArtist(artist.Id);
var movedFiles = new List<MetadataFile>(); var movedFiles = new List<MetadataFile>();
var distinctTrackFilePaths = trackFiles.DistinctBy(s => Path.GetDirectoryName(s.RelativePath)).ToList();
// TODO: Move EpisodeImage and EpisodeMetadata metadata files, instead of relying on consumers to do it // TODO: Move EpisodeImage and EpisodeMetadata metadata files, instead of relying on consumers to do it
// (Xbmc's EpisodeImage is more than just the extension) // (Xbmc's EpisodeImage is more than just the extension)
foreach (var consumer in _metadataFactory.GetAvailableProviders()) foreach (var consumer in _metadataFactory.GetAvailableProviders())
{ {
foreach (var filePath in distinctTrackFilePaths)
{
var metadataFilesForConsumer = GetMetadataFilesForConsumer(consumer, metadataFiles)
.Where(m => m.AlbumId == filePath.AlbumId)
.Where(m => m.Type == MetadataType.AlbumImage || m.Type == MetadataType.AlbumMetadata)
.ToList();
foreach (var metadataFile in metadataFilesForConsumer)
{
var newFileName = consumer.GetFilenameAfterMove(artist, Path.GetDirectoryName(filePath.RelativePath), metadataFile);
var existingFileName = Path.Combine(artist.Path, metadataFile.RelativePath);
if (newFileName.PathNotEquals(existingFileName))
{
try
{
_diskProvider.MoveFile(existingFileName, newFileName);
metadataFile.RelativePath = artist.Path.GetRelativePath(newFileName);
movedFiles.Add(metadataFile);
}
catch (Exception ex)
{
_logger.Warn(ex, "Unable to move metadata file after rename: {0}", existingFileName);
}
}
}
}
foreach (var trackFile in trackFiles) foreach (var trackFile in trackFiles)
{ {
var metadataFilesForConsumer = GetMetadataFilesForConsumer(consumer, metadataFiles).Where(m => m.TrackFileId == trackFile.Id).ToList(); var metadataFilesForConsumer = GetMetadataFilesForConsumer(consumer, metadataFiles).Where(m => m.TrackFileId == trackFile.Id).ToList();
@ -233,9 +259,9 @@ namespace NzbDrone.Core.Extras.Metadata
return metadata; return metadata;
} }
private MetadataFile ProcessAlbumMetadata(IMetadata consumer, Album album, List<MetadataFile> existingMetadataFiles) private MetadataFile ProcessAlbumMetadata(IMetadata consumer, Artist artist, Album album, string albumPath, List<MetadataFile> existingMetadataFiles)
{ {
var albumMetadata = consumer.AlbumMetadata(album.Artist, album); var albumMetadata = consumer.AlbumMetadata(artist, album, albumPath);
if (albumMetadata == null) if (albumMetadata == null)
{ {
@ -244,10 +270,10 @@ namespace NzbDrone.Core.Extras.Metadata
var hash = albumMetadata.Contents.SHA256Hash(); var hash = albumMetadata.Contents.SHA256Hash();
var metadata = GetMetadataFile(album.Artist, existingMetadataFiles, e => e.Type == MetadataType.AlbumMetadata && e.AlbumId == album.Id) ?? var metadata = GetMetadataFile(artist, existingMetadataFiles, e => e.Type == MetadataType.AlbumMetadata && e.AlbumId == album.Id) ??
new MetadataFile new MetadataFile
{ {
ArtistId = album.ArtistId, ArtistId = artist.Id,
AlbumId = album.Id, AlbumId = album.Id,
Consumer = consumer.GetType().Name, Consumer = consumer.GetType().Name,
Type = MetadataType.AlbumMetadata Type = MetadataType.AlbumMetadata
@ -265,7 +291,7 @@ namespace NzbDrone.Core.Extras.Metadata
return null; return null;
} }
var fullPath = Path.Combine(album.Path, albumMetadata.RelativePath); var fullPath = Path.Combine(artist.Path, albumMetadata.RelativePath);
_logger.Debug("Writing Album Metadata to: {0}", fullPath); _logger.Debug("Writing Album Metadata to: {0}", fullPath);
SaveMetadataFile(fullPath, albumMetadata.Contents); SaveMetadataFile(fullPath, albumMetadata.Contents);
@ -277,16 +303,16 @@ namespace NzbDrone.Core.Extras.Metadata
return metadata; return metadata;
} }
private MetadataFile ProcessEpisodeMetadata(IMetadata consumer, Artist artist, TrackFile trackFile, List<MetadataFile> existingMetadataFiles) private MetadataFile ProcessTrackMetadata(IMetadata consumer, Artist artist, TrackFile trackFile, List<MetadataFile> existingMetadataFiles)
{ {
var episodeMetadata = consumer.TrackMetadata(artist, trackFile); var trackMetadata = consumer.TrackMetadata(artist, trackFile);
if (episodeMetadata == null) if (trackMetadata == null)
{ {
return null; return null;
} }
var fullPath = Path.Combine(artist.Path, episodeMetadata.RelativePath); var fullPath = Path.Combine(artist.Path, trackMetadata.RelativePath);
var existingMetadata = GetMetadataFile(artist, existingMetadataFiles, c => c.Type == MetadataType.TrackMetadata && var existingMetadata = GetMetadataFile(artist, existingMetadataFiles, c => c.Type == MetadataType.TrackMetadata &&
c.TrackFileId == trackFile.Id); c.TrackFileId == trackFile.Id);
@ -297,11 +323,11 @@ namespace NzbDrone.Core.Extras.Metadata
if (fullPath.PathNotEquals(existingFullPath)) if (fullPath.PathNotEquals(existingFullPath))
{ {
_diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move); _diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move);
existingMetadata.RelativePath = episodeMetadata.RelativePath; existingMetadata.RelativePath = trackMetadata.RelativePath;
} }
} }
var hash = episodeMetadata.Contents.SHA256Hash(); var hash = trackMetadata.Contents.SHA256Hash();
var metadata = existingMetadata ?? var metadata = existingMetadata ??
new MetadataFile new MetadataFile
@ -311,7 +337,7 @@ namespace NzbDrone.Core.Extras.Metadata
TrackFileId = trackFile.Id, TrackFileId = trackFile.Id,
Consumer = consumer.GetType().Name, Consumer = consumer.GetType().Name,
Type = MetadataType.TrackMetadata, Type = MetadataType.TrackMetadata,
RelativePath = episodeMetadata.RelativePath, RelativePath = trackMetadata.RelativePath,
Extension = Path.GetExtension(fullPath) Extension = Path.GetExtension(fullPath)
}; };
@ -321,7 +347,7 @@ namespace NzbDrone.Core.Extras.Metadata
} }
_logger.Debug("Writing Track Metadata to: {0}", fullPath); _logger.Debug("Writing Track Metadata to: {0}", fullPath);
SaveMetadataFile(fullPath, episodeMetadata.Contents); SaveMetadataFile(fullPath, trackMetadata.Contents);
metadata.Hash = hash; metadata.Hash = hash;
@ -361,92 +387,39 @@ namespace NzbDrone.Core.Extras.Metadata
return result; return result;
} }
private List<MetadataFile> ProcessAlbumImages(IMetadata consumer, Artist artist, List<MetadataFile> existingMetadataFiles) private List<MetadataFile> ProcessAlbumImages(IMetadata consumer, Artist artist, Album album, string albumFolder, List<MetadataFile> existingMetadataFiles)
{ {
var result = new List<MetadataFile>(); var result = new List<MetadataFile>();
var albums = _albumService.GetAlbumsByArtist(artist.Id); foreach (var image in consumer.AlbumImages(artist, album, albumFolder))
foreach (var album in albums)
{
foreach (var image in consumer.AlbumImages(artist, album))
{
var fullPath = Path.Combine(artist.Path, image.RelativePath);
if (_diskProvider.FileExists(fullPath))
{
_logger.Debug("Album image already exists: {0}", fullPath);
continue;
}
var metadata = GetMetadataFile(artist, existingMetadataFiles, c => c.Type == MetadataType.AlbumImage &&
c.AlbumId == album.Id &&
c.RelativePath == image.RelativePath) ??
new MetadataFile
{
ArtistId = artist.Id,
AlbumId = album.Id,
Consumer = consumer.GetType().Name,
Type = MetadataType.AlbumImage,
RelativePath = image.RelativePath,
Extension = Path.GetExtension(fullPath)
};
DownloadImage(album, image);
result.Add(metadata);
}
}
return result;
}
private List<MetadataFile> ProcessEpisodeImages(IMetadata consumer, Artist artist, TrackFile trackFile, List<MetadataFile> existingMetadataFiles)
{
var result = new List<MetadataFile>();
foreach (var image in consumer.TrackImages(artist, trackFile))
{ {
var fullPath = Path.Combine(artist.Path, image.RelativePath); var fullPath = Path.Combine(artist.Path, image.RelativePath);
if (_diskProvider.FileExists(fullPath)) if (_diskProvider.FileExists(fullPath))
{ {
_logger.Debug("Track image already exists: {0}", fullPath); _logger.Debug("Album image already exists: {0}", fullPath);
continue; continue;
} }
var existingMetadata = GetMetadataFile(artist, existingMetadataFiles, c => c.Type == MetadataType.TrackImage && var metadata = GetMetadataFile(artist, existingMetadataFiles, c => c.Type == MetadataType.AlbumImage &&
c.TrackFileId == trackFile.Id); c.AlbumId == album.Id &&
c.RelativePath == image.RelativePath) ??
if (existingMetadata != null) new MetadataFile
{ {
var existingFullPath = Path.Combine(artist.Path, existingMetadata.RelativePath); ArtistId = artist.Id,
if (fullPath.PathNotEquals(existingFullPath)) AlbumId = album.Id,
{ Consumer = consumer.GetType().Name,
_diskTransferService.TransferFile(existingFullPath, fullPath, TransferMode.Move); Type = MetadataType.AlbumImage,
existingMetadata.RelativePath = image.RelativePath; RelativePath = image.RelativePath,
Extension = Path.GetExtension(fullPath)
return new List<MetadataFile>{ existingMetadata }; };
}
}
var metadata = existingMetadata ??
new MetadataFile
{
ArtistId = artist.Id,
AlbumId = trackFile.AlbumId,
TrackFileId = trackFile.Id,
Consumer = consumer.GetType().Name,
Type = MetadataType.TrackImage,
RelativePath = image.RelativePath,
Extension = Path.GetExtension(fullPath)
};
DownloadImage(artist, image); DownloadImage(artist, image);
result.Add(metadata); result.Add(metadata);
} }
return result; return result;
} }
@ -476,32 +449,6 @@ namespace NzbDrone.Core.Extras.Metadata
} }
} }
private void DownloadImage(Album album, ImageFileResult image)
{
var fullPath = Path.Combine(album.Path, image.RelativePath);
try
{
if (image.Url.StartsWith("http"))
{
_httpClient.DownloadFile(image.Url, fullPath);
}
else
{
_diskProvider.CopyFile(image.Url, fullPath);
}
_mediaFileAttributeService.SetFilePermissions(fullPath);
}
catch (WebException ex)
{
_logger.Warn(ex, "Couldn't download image {0} for {1}. {2}", image.Url, album, ex.Message);
}
catch (Exception ex)
{
_logger.Error(ex, "Couldn't download image {0} for {1}. {2}", image.Url, album, ex.Message);
}
}
private void SaveMetadataFile(string path, string contents) private void SaveMetadataFile(string path, string contents)
{ {
_diskProvider.WriteAllText(path, contents); _diskProvider.WriteAllText(path, contents);

@ -28,7 +28,7 @@ namespace NzbDrone.Core.Extras.Others
public override int Order => 2; public override int Order => 2;
public override IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<Album> albums, List<TrackFile> trackFiles) public override IEnumerable<ExtraFile> CreateAfterArtistScan(Artist artist, List<TrackFile> trackFiles)
{ {
return Enumerable.Empty<ExtraFile>(); return Enumerable.Empty<ExtraFile>();
} }
@ -38,7 +38,7 @@ namespace NzbDrone.Core.Extras.Others
return Enumerable.Empty<ExtraFile>(); return Enumerable.Empty<ExtraFile>();
} }
public override IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, string artistFolder, string albumFolder) public override IEnumerable<ExtraFile> CreateAfterTrackImport(Artist artist, Album album, string artistFolder, string albumFolder)
{ {
return Enumerable.Empty<ExtraFile>(); return Enumerable.Empty<ExtraFile>();
} }

@ -14,6 +14,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
public void Clean() public void Clean()
{ {
DeleteDuplicateArtistMetadata(); DeleteDuplicateArtistMetadata();
DeleteDuplicateAlbumMetadata();
DeleteDuplicateTrackMetadata(); DeleteDuplicateTrackMetadata();
DeleteDuplicateTrackImages(); DeleteDuplicateTrackImages();
} }

@ -16,6 +16,7 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
DeleteOrphanedByArtist(); DeleteOrphanedByArtist();
DeleteOrphanedByAlbum(); DeleteOrphanedByAlbum();
DeleteOrphanedByTrackFile(); DeleteOrphanedByTrackFile();
DeleteWhereAlbumIdIsZero();
DeleteWhereTrackFileIsZero(); DeleteWhereTrackFileIsZero();
} }
@ -57,6 +58,17 @@ namespace NzbDrone.Core.Housekeeping.Housekeepers
AND TrackFiles.Id IS NULL)"); AND TrackFiles.Id IS NULL)");
} }
private void DeleteWhereAlbumIdIsZero()
{
var mapper = _database.GetDataMapper();
mapper.ExecuteNonQuery(@"DELETE FROM MetadataFiles
WHERE Id IN (
SELECT Id FROM MetadataFiles
WHERE Type IN (4, 6)
AND AlbumId = 0)");
}
private void DeleteWhereTrackFileIsZero() private void DeleteWhereTrackFileIsZero()
{ {
var mapper = _database.GetDataMapper(); var mapper = _database.GetDataMapper();

@ -144,9 +144,10 @@ namespace NzbDrone.Core.MediaFiles
if (renamed.Any()) if (renamed.Any())
{ {
_diskProvider.RemoveEmptySubfolders(artist.Path);
_eventAggregator.PublishEvent(new ArtistRenamedEvent(artist)); _eventAggregator.PublishEvent(new ArtistRenamedEvent(artist));
_logger.Debug("Removing Empty Subfolders from: {0}", artist.Path);
_diskProvider.RemoveEmptySubfolders(artist.Path);
} }
} }

@ -2,8 +2,6 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Music namespace NzbDrone.Core.Music
{ {
@ -24,12 +22,9 @@ namespace NzbDrone.Core.Music
public string CleanTitle { get; set; } public string CleanTitle { get; set; }
public DateTime? ReleaseDate { get; set; } public DateTime? ReleaseDate { get; set; }
public List<string> Label { get; set; } public List<string> Label { get; set; }
//public int TrackCount { get; set; }
public string Path { get; set; }
public int ProfileId { get; set; } public int ProfileId { get; set; }
public int Duration { get; set; } public int Duration { get; set; }
public List<Track> Tracks { get; set; } public List<Track> Tracks { get; set; }
//public int DiscCount { get; set; }
public bool Monitored { get; set; } public bool Monitored { get; set; }
public List<MediaCover.MediaCover> Images { get; set; } public List<MediaCover.MediaCover> Images { get; set; }
public List<string> Genres { get; set; } public List<string> Genres { get; set; }

@ -8,12 +8,12 @@ using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using Marr.Data.QGen; using Marr.Data.QGen;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Music namespace NzbDrone.Core.Music
{ {
public interface IAlbumRepository : IBasicRepository<Album> public interface IAlbumRepository : IBasicRepository<Album>
{ {
bool AlbumPathExists(string path);
List<Album> GetAlbums(int artistId); List<Album> GetAlbums(int artistId);
Album FindByName(string cleanTitle); Album FindByName(string cleanTitle);
Album FindByTitle(int artistId, string title); Album FindByTitle(int artistId, string title);
@ -37,11 +37,6 @@ namespace NzbDrone.Core.Music
} }
public bool AlbumPathExists(string path)
{
return Query.Where(c => c.Path == path).Any();
}
public List<Album> GetAlbums(int artistId) public List<Album> GetAlbums(int artistId)
{ {
return Query.Where(s => s.ArtistId == artistId).ToList(); return Query.Where(s => s.ArtistId == artistId).ToList();

@ -33,7 +33,6 @@ namespace NzbDrone.Core.Music
void InsertMany(List<Album> albums); void InsertMany(List<Album> albums);
void UpdateMany(List<Album> albums); void UpdateMany(List<Album> albums);
void DeleteMany(List<Album> albums); void DeleteMany(List<Album> albums);
bool AlbumPathExists(string folder);
void RemoveAddOptions(Album album); void RemoveAddOptions(Album album);
} }
@ -67,11 +66,6 @@ namespace NzbDrone.Core.Music
return newAlbum; return newAlbum;
} }
public bool AlbumPathExists(string folder)
{
return _albumRepository.AlbumPathExists(folder);
}
public void DeleteAlbum(int albumId, bool deleteFiles) public void DeleteAlbum(int albumId, bool deleteFiles)
{ {
var album = _albumRepository.Get(albumId); var album = _albumRepository.Get(albumId);
@ -89,7 +83,6 @@ namespace NzbDrone.Core.Music
return _albumRepository.FindByTitle(artistId, title); return _albumRepository.FindByTitle(artistId, title);
} }
public Album FindByTitleInexact(string title) public Album FindByTitleInexact(string title)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

@ -62,11 +62,6 @@ namespace NzbDrone.Core.Music
albumToUpdate.ProfileId = artist.ProfileId; albumToUpdate.ProfileId = artist.ProfileId;
albumToUpdate.Added = DateTime.UtcNow; albumToUpdate.Added = DateTime.UtcNow;
if (string.IsNullOrWhiteSpace(albumToUpdate.Path))
{
albumToUpdate.Path = _fileNameBuilder.BuildAlbumPath(artist, album);
}
newList.Add(albumToUpdate); newList.Add(albumToUpdate);
} }

@ -178,6 +178,7 @@
<Compile Include="Datastore\Migration\004_add_various_qualities_in_profile.cs" /> <Compile Include="Datastore\Migration\004_add_various_qualities_in_profile.cs" />
<Compile Include="Datastore\Migration\003_add_medium_support.cs" /> <Compile Include="Datastore\Migration\003_add_medium_support.cs" />
<Compile Include="Datastore\Migration\006_separate_automatic_and_interactive_search.cs" /> <Compile Include="Datastore\Migration\006_separate_automatic_and_interactive_search.cs" />
<Compile Include="Datastore\Migration\007_change_album_path_to_relative.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" />

@ -20,6 +20,7 @@ namespace NzbDrone.Core.Parser
List<Album> GetAlbums(ParsedAlbumInfo parsedAlbumInfo, Artist artist, SearchCriteriaBase searchCriteria = null); List<Album> GetAlbums(ParsedAlbumInfo parsedAlbumInfo, Artist artist, SearchCriteriaBase searchCriteria = null);
// Music stuff here // Music stuff here
Album GetLocalAlbum(string filename, Artist artist);
LocalTrack GetLocalTrack(string filename, Artist artist); LocalTrack GetLocalTrack(string filename, Artist artist);
LocalTrack GetLocalTrack(string filename, Artist artist, ParsedTrackInfo folderInfo); LocalTrack GetLocalTrack(string filename, Artist artist, ParsedTrackInfo folderInfo);
@ -30,11 +31,13 @@ namespace NzbDrone.Core.Parser
private readonly IArtistService _artistService; private readonly IArtistService _artistService;
private readonly IAlbumService _albumService; private readonly IAlbumService _albumService;
private readonly ITrackService _trackService; private readonly ITrackService _trackService;
private readonly IMediaFileService _mediaFileService;
private readonly Logger _logger; private readonly Logger _logger;
public ParsingService(ITrackService trackService, public ParsingService(ITrackService trackService,
IArtistService artistService, IArtistService artistService,
IAlbumService albumService, IAlbumService albumService,
IMediaFileService mediaFileService,
// ISceneMappingService sceneMappingService, // ISceneMappingService sceneMappingService,
Logger logger) Logger logger)
{ {
@ -42,6 +45,7 @@ namespace NzbDrone.Core.Parser
_artistService = artistService; _artistService = artistService;
// _sceneMappingService = sceneMappingService; // _sceneMappingService = sceneMappingService;
_trackService = trackService; _trackService = trackService;
_mediaFileService = mediaFileService;
_logger = logger; _logger = logger;
} }
@ -174,6 +178,24 @@ namespace NzbDrone.Core.Parser
return artist; return artist;
} }
public Album GetLocalAlbum(string filename, Artist artist)
{
if (Path.HasExtension(filename))
{
filename = Path.GetDirectoryName(filename);
}
filename = artist.Path.GetRelativePath(filename);
var tracksInAlbum = _mediaFileService.GetFilesByArtist(artist.Id)
.FindAll(s => Path.GetDirectoryName(s.RelativePath) == filename)
.DistinctBy(s => s.AlbumId)
.ToList();
return tracksInAlbum.Count == 1 ? _albumService.GetAlbum(tracksInAlbum.First().AlbumId) : null;
}
public LocalTrack GetLocalTrack(string filename, Artist artist) public LocalTrack GetLocalTrack(string filename, Artist artist)
{ {
return GetLocalTrack(filename, artist, null); return GetLocalTrack(filename, artist, null);

Loading…
Cancel
Save