2017-08-31 02:42:40 +00:00
|
|
|
using System;
|
2015-03-04 00:42:37 +00:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using NLog;
|
|
|
|
using NzbDrone.Common.Disk;
|
|
|
|
using NzbDrone.Common.Extensions;
|
|
|
|
using NzbDrone.Common.Instrumentation.Extensions;
|
|
|
|
using NzbDrone.Core.Download;
|
|
|
|
using NzbDrone.Core.Download.TrackedDownloads;
|
|
|
|
using NzbDrone.Core.Messaging.Commands;
|
|
|
|
using NzbDrone.Core.Messaging.Events;
|
|
|
|
using NzbDrone.Core.Parser;
|
|
|
|
using NzbDrone.Core.Parser.Model;
|
2017-09-13 03:28:45 +00:00
|
|
|
using NzbDrone.Core.Music;
|
2018-11-19 03:16:55 +00:00
|
|
|
using NzbDrone.Common.Crypto;
|
|
|
|
using NzbDrone.Common.Cache;
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2017-08-31 02:42:40 +00:00
|
|
|
namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
2015-03-04 00:42:37 +00:00
|
|
|
{
|
|
|
|
public interface IManualImportService
|
|
|
|
{
|
2017-12-30 03:23:04 +00:00
|
|
|
List<ManualImportItem> GetMediaFiles(string path, string downloadId, bool filterExistingFiles);
|
2019-02-16 14:49:24 +00:00
|
|
|
void UpdateItems(List<ManualImportItem> item);
|
2018-11-19 03:16:55 +00:00
|
|
|
ManualImportItem Find(int id);
|
2015-03-04 00:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
|
|
|
|
{
|
|
|
|
private readonly IDiskProvider _diskProvider;
|
|
|
|
private readonly IParsingService _parsingService;
|
|
|
|
private readonly IDiskScanService _diskScanService;
|
|
|
|
private readonly IMakeImportDecision _importDecisionMaker;
|
2017-09-13 03:28:45 +00:00
|
|
|
private readonly IArtistService _artistService;
|
|
|
|
private readonly IAlbumService _albumService;
|
2018-12-15 00:02:43 +00:00
|
|
|
private readonly IReleaseService _releaseService;
|
2017-09-13 03:28:45 +00:00
|
|
|
private readonly ITrackService _trackService;
|
|
|
|
private readonly IImportApprovedTracks _importApprovedTracks;
|
2015-03-04 00:42:37 +00:00
|
|
|
private readonly ITrackedDownloadService _trackedDownloadService;
|
2018-11-19 03:16:55 +00:00
|
|
|
private readonly IDownloadedTracksImportService _downloadedTracksImportService;
|
|
|
|
private readonly ICached<ManualImportItem> _cache;
|
2015-03-04 00:42:37 +00:00
|
|
|
private readonly IEventAggregator _eventAggregator;
|
|
|
|
private readonly Logger _logger;
|
|
|
|
|
|
|
|
public ManualImportService(IDiskProvider diskProvider,
|
|
|
|
IParsingService parsingService,
|
|
|
|
IDiskScanService diskScanService,
|
|
|
|
IMakeImportDecision importDecisionMaker,
|
2017-09-13 03:28:45 +00:00
|
|
|
IArtistService artistService,
|
|
|
|
IAlbumService albumService,
|
2018-12-15 00:02:43 +00:00
|
|
|
IReleaseService releaseService,
|
2017-09-13 03:28:45 +00:00
|
|
|
ITrackService trackService,
|
|
|
|
IImportApprovedTracks importApprovedTracks,
|
2015-03-04 00:42:37 +00:00
|
|
|
ITrackedDownloadService trackedDownloadService,
|
2018-11-19 03:16:55 +00:00
|
|
|
IDownloadedTracksImportService downloadedTracksImportService,
|
|
|
|
ICacheManager cacheManager,
|
2015-03-04 00:42:37 +00:00
|
|
|
IEventAggregator eventAggregator,
|
|
|
|
Logger logger)
|
|
|
|
{
|
|
|
|
_diskProvider = diskProvider;
|
|
|
|
_parsingService = parsingService;
|
|
|
|
_diskScanService = diskScanService;
|
|
|
|
_importDecisionMaker = importDecisionMaker;
|
2017-09-13 03:28:45 +00:00
|
|
|
_artistService = artistService;
|
|
|
|
_albumService = albumService;
|
2018-12-15 00:02:43 +00:00
|
|
|
_releaseService = releaseService;
|
2017-09-13 03:28:45 +00:00
|
|
|
_trackService = trackService;
|
|
|
|
_importApprovedTracks = importApprovedTracks;
|
2015-03-04 00:42:37 +00:00
|
|
|
_trackedDownloadService = trackedDownloadService;
|
2018-11-19 03:16:55 +00:00
|
|
|
_downloadedTracksImportService = downloadedTracksImportService;
|
|
|
|
_cache = cacheManager.GetCache<ManualImportItem>(GetType());
|
2015-03-04 00:42:37 +00:00
|
|
|
_eventAggregator = eventAggregator;
|
|
|
|
_logger = logger;
|
|
|
|
}
|
|
|
|
|
2018-11-19 03:16:55 +00:00
|
|
|
public ManualImportItem Find(int id)
|
|
|
|
{
|
|
|
|
return _cache.Find(id.ToString());
|
|
|
|
}
|
|
|
|
|
2017-12-30 03:23:04 +00:00
|
|
|
public List<ManualImportItem> GetMediaFiles(string path, string downloadId, bool filterExistingFiles)
|
2015-03-04 00:42:37 +00:00
|
|
|
{
|
2018-11-19 03:16:55 +00:00
|
|
|
_cache.Clear();
|
|
|
|
|
2015-03-04 00:42:37 +00:00
|
|
|
if (downloadId.IsNotNullOrWhiteSpace())
|
|
|
|
{
|
|
|
|
var trackedDownload = _trackedDownloadService.Find(downloadId);
|
|
|
|
|
|
|
|
if (trackedDownload == null)
|
|
|
|
{
|
|
|
|
return new List<ManualImportItem>();
|
|
|
|
}
|
|
|
|
|
|
|
|
path = trackedDownload.DownloadItem.OutputPath.FullPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_diskProvider.FolderExists(path))
|
|
|
|
{
|
|
|
|
if (!_diskProvider.FileExists(path))
|
|
|
|
{
|
|
|
|
return new List<ManualImportItem>();
|
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
var decision = _importDecisionMaker.GetImportDecisions(new List<string> { path }, null, null, null, null, false, true, false);
|
|
|
|
var result = MapItem(decision.First(), Path.GetDirectoryName(path), downloadId);
|
|
|
|
_cache.Set(result.Id.ToString(), result);
|
2018-11-19 03:16:55 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
return new List<ManualImportItem> { result };
|
2015-03-04 00:42:37 +00:00
|
|
|
}
|
|
|
|
|
2018-11-19 03:16:55 +00:00
|
|
|
var items = ProcessFolder(path, downloadId, filterExistingFiles);
|
|
|
|
foreach (var item in items)
|
|
|
|
{
|
|
|
|
_cache.Set(item.Id.ToString(), item);
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
2015-03-04 00:42:37 +00:00
|
|
|
}
|
|
|
|
|
2017-12-30 03:23:04 +00:00
|
|
|
private List<ManualImportItem> ProcessFolder(string folder, string downloadId, bool filterExistingFiles)
|
2015-03-04 00:42:37 +00:00
|
|
|
{
|
2017-09-13 03:28:45 +00:00
|
|
|
var directoryInfo = new DirectoryInfo(folder);
|
|
|
|
var artist = _parsingService.GetArtist(directoryInfo.Name);
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2017-09-13 03:28:45 +00:00
|
|
|
if (artist == null && downloadId.IsNotNullOrWhiteSpace())
|
|
|
|
{
|
|
|
|
var trackedDownload = _trackedDownloadService.Find(downloadId);
|
|
|
|
artist = trackedDownload.RemoteAlbum.Artist;
|
|
|
|
}
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2017-09-13 03:28:45 +00:00
|
|
|
var folderInfo = Parser.Parser.ParseMusicTitle(directoryInfo.Name);
|
|
|
|
var artistFiles = _diskScanService.GetAudioFiles(folder).ToList();
|
2019-02-16 14:49:24 +00:00
|
|
|
var decisions = _importDecisionMaker.GetImportDecisions(artistFiles, artist, null, null, folderInfo, filterExistingFiles, true, false);
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2017-09-13 03:28:45 +00:00
|
|
|
return decisions.Select(decision => MapItem(decision, folder, downloadId)).ToList();
|
2015-03-04 00:42:37 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
public void UpdateItems(List<ManualImportItem> items)
|
2018-11-19 03:16:55 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
var groupedItems = items.GroupBy(x => x.Album?.Id);
|
|
|
|
_logger.Debug("UpdateItems, {0} groups", groupedItems.Count());
|
|
|
|
foreach(var group in groupedItems)
|
2018-11-19 03:16:55 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
// generate dummy decisions that don't match the release
|
|
|
|
_logger.Debug("UpdateItems, group key: {0}", group.Key);
|
|
|
|
var decisions = _importDecisionMaker.GetImportDecisions(group.Select(x => x.Path).ToList(), group.First().Artist, group.First().Album, null, null, false, true, true);
|
2018-11-19 03:16:55 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
foreach (var decision in decisions)
|
|
|
|
{
|
|
|
|
var item = items.Where(x => x.Path == decision.Item.Path).Single();
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
if (decision.Item.Artist != null)
|
|
|
|
{
|
|
|
|
item.Artist = decision.Item.Artist;
|
|
|
|
}
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
if (decision.Item.Album != null)
|
|
|
|
{
|
|
|
|
item.Album = decision.Item.Album;
|
|
|
|
item.Release = decision.Item.Release;
|
|
|
|
}
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
if (decision.Item.Tracks.Any())
|
|
|
|
{
|
|
|
|
item.Tracks = decision.Item.Tracks;
|
|
|
|
}
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
item.Rejections = decision.Rejections;
|
2015-03-04 00:42:37 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
_cache.Set(item.Id.ToString(), item);
|
2017-10-08 05:00:54 +00:00
|
|
|
}
|
2019-02-16 14:49:24 +00:00
|
|
|
}
|
2015-03-04 00:42:37 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
private ManualImportItem MapItem(ImportDecision<LocalTrack> decision, string folder, string downloadId)
|
2015-03-04 00:42:37 +00:00
|
|
|
{
|
2017-09-13 03:28:45 +00:00
|
|
|
var item = new ManualImportItem();
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
item.Id = HashConverter.GetHashInt31(decision.Item.Path);
|
|
|
|
item.Path = decision.Item.Path;
|
|
|
|
item.RelativePath = folder.GetRelativePath(decision.Item.Path);
|
|
|
|
item.Name = Path.GetFileNameWithoutExtension(decision.Item.Path);
|
2017-09-13 03:28:45 +00:00
|
|
|
item.DownloadId = downloadId;
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
if (decision.Item.Artist != null)
|
2017-09-13 03:28:45 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
item.Artist = decision.Item.Artist;
|
2017-09-13 03:28:45 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
if (decision.Item.Album != null)
|
2017-09-26 01:27:18 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
item.Album = decision.Item.Album;
|
|
|
|
item.Release = decision.Item.Release;
|
2017-09-26 01:27:18 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
if (decision.Item.Tracks.Any())
|
2017-09-13 03:28:45 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
item.Tracks = decision.Item.Tracks;
|
2017-09-13 03:28:45 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
item.Quality = decision.Item.Quality;
|
|
|
|
item.Language = decision.Item.Language;
|
|
|
|
item.Size = _diskProvider.GetFileSize(decision.Item.Path);
|
2017-09-13 03:28:45 +00:00
|
|
|
item.Rejections = decision.Rejections;
|
2019-02-16 14:49:24 +00:00
|
|
|
item.Tags = decision.Item.FileTrackInfo;
|
2017-09-13 03:28:45 +00:00
|
|
|
|
|
|
|
return item;
|
2015-03-04 00:42:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Execute(ManualImportCommand message)
|
|
|
|
{
|
2016-10-18 19:17:50 +00:00
|
|
|
_logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode);
|
2017-09-13 03:28:45 +00:00
|
|
|
|
|
|
|
var imported = new List<ImportResult>();
|
|
|
|
var importedTrackedDownload = new List<ManuallyImportedFile>();
|
2019-02-16 14:49:24 +00:00
|
|
|
var albumIds = message.Files.GroupBy(e => e.AlbumId).ToList();
|
|
|
|
var fileCount = 0;
|
2017-09-13 03:28:45 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
foreach (var importAlbumId in albumIds)
|
2017-09-13 03:28:45 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
var albumImportDecisions = new List<ImportDecision<LocalTrack>>();
|
|
|
|
|
|
|
|
foreach (var file in importAlbumId)
|
2017-09-13 03:28:45 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
_logger.ProgressTrace("Processing file {0} of {1}", fileCount + 1, message.Files.Count);
|
|
|
|
|
|
|
|
var artist = _artistService.GetArtist(file.ArtistId);
|
|
|
|
var album = _albumService.GetAlbum(file.AlbumId);
|
|
|
|
var release = _releaseService.GetRelease(file.AlbumReleaseId);
|
|
|
|
var tracks = _trackService.GetTracks(file.TrackIds);
|
|
|
|
var fileTrackInfo = Parser.Parser.ParseMusicPath(file.Path) ?? new ParsedTrackInfo();
|
|
|
|
|
|
|
|
var localTrack = new LocalTrack
|
|
|
|
{
|
|
|
|
ExistingFile = false,
|
|
|
|
Tracks = tracks,
|
|
|
|
MediaInfo = null,
|
|
|
|
FileTrackInfo = fileTrackInfo,
|
|
|
|
Path = file.Path,
|
|
|
|
Quality = file.Quality,
|
|
|
|
Language = file.Language,
|
|
|
|
Artist = artist,
|
|
|
|
Album = album,
|
|
|
|
Release = release,
|
|
|
|
Size = 0
|
|
|
|
};
|
|
|
|
|
|
|
|
albumImportDecisions.Add(new ImportDecision<LocalTrack>(localTrack));
|
|
|
|
fileCount += 1;
|
2017-09-13 03:28:45 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
var existingFile = albumImportDecisions.First().Item.Artist.Path.IsParentPath(importAlbumId.First().Path);
|
|
|
|
|
|
|
|
if (importAlbumId.First().DownloadId.IsNullOrWhiteSpace())
|
|
|
|
{
|
|
|
|
imported.AddRange(_importApprovedTracks.Import(albumImportDecisions, !existingFile, null, message.ImportMode));
|
|
|
|
}
|
2017-09-13 03:28:45 +00:00
|
|
|
else
|
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
var trackedDownload = _trackedDownloadService.Find(importAlbumId.First().DownloadId);
|
|
|
|
var importResults = _importApprovedTracks.Import(albumImportDecisions, true, trackedDownload.DownloadItem, message.ImportMode);
|
2017-09-13 03:28:45 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
imported.AddRange(importResults);
|
2017-09-13 03:28:45 +00:00
|
|
|
|
2019-02-16 14:49:24 +00:00
|
|
|
foreach (var importResult in importResults)
|
2017-09-13 03:28:45 +00:00
|
|
|
{
|
2019-02-16 14:49:24 +00:00
|
|
|
importedTrackedDownload.Add(new ManuallyImportedFile
|
|
|
|
{
|
|
|
|
TrackedDownload = trackedDownload,
|
|
|
|
ImportResult = importResult
|
|
|
|
});
|
|
|
|
}
|
2017-09-13 03:28:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_logger.ProgressTrace("Manually imported {0} files", imported.Count);
|
|
|
|
|
|
|
|
foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
|
|
|
|
{
|
|
|
|
var trackedDownload = groupedTrackedDownload.First().TrackedDownload;
|
|
|
|
|
|
|
|
if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
|
|
|
|
{
|
2018-11-19 03:16:55 +00:00
|
|
|
if (_downloadedTracksImportService.ShouldDeleteFolder(
|
2017-09-13 03:28:45 +00:00
|
|
|
new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath),
|
2017-09-26 02:31:52 +00:00
|
|
|
trackedDownload.RemoteAlbum.Artist) && trackedDownload.DownloadItem.CanMoveFiles)
|
2017-09-13 03:28:45 +00:00
|
|
|
{
|
|
|
|
_diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteAlbum.Albums.Count))
|
|
|
|
{
|
|
|
|
trackedDownload.State = TrackedDownloadStage.Imported;
|
|
|
|
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
|
|
|
|
}
|
|
|
|
}
|
2015-03-04 00:42:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|