mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 21:13:28 -07:00
Refactored most code for track parsing.
This commit is contained in:
parent
d1eb9ff16c
commit
76db95947c
18 changed files with 430 additions and 177 deletions
|
@ -4,6 +4,7 @@ using System.Text.RegularExpressions;
|
||||||
using NzbDrone.Common.EnsureThat;
|
using NzbDrone.Common.EnsureThat;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.IndexerSearch.Definitions
|
namespace NzbDrone.Core.IndexerSearch.Definitions
|
||||||
{
|
{
|
||||||
|
@ -19,6 +20,9 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
|
||||||
public virtual bool MonitoredEpisodesOnly { get; set; }
|
public virtual bool MonitoredEpisodesOnly { get; set; }
|
||||||
public virtual bool UserInvokedSearch { get; set; }
|
public virtual bool UserInvokedSearch { get; set; }
|
||||||
|
|
||||||
|
public Artist Artist { get; set; }
|
||||||
|
public List<Track> Tracks { get; set; }
|
||||||
|
|
||||||
public List<string> QueryTitles => SceneTitles.Select(GetQueryTitle).ToList();
|
public List<string> QueryTitles => SceneTitles.Select(GetQueryTitle).ToList();
|
||||||
|
|
||||||
public static string GetQueryTitle(string title)
|
public static string GetQueryTitle(string title)
|
||||||
|
|
26
src/NzbDrone.Core/MediaFiles/Commands/RescanArtistCommand.cs
Normal file
26
src/NzbDrone.Core/MediaFiles/Commands/RescanArtistCommand.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles.Events
|
||||||
|
{
|
||||||
|
public class RescanArtistCommand : Command
|
||||||
|
{
|
||||||
|
|
||||||
|
public string ArtistId { get; set; }
|
||||||
|
|
||||||
|
public override bool SendUpdatesToClient => true;
|
||||||
|
|
||||||
|
public RescanArtistCommand()
|
||||||
|
{
|
||||||
|
ArtistId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public RescanArtistCommand(string artistId)
|
||||||
|
{
|
||||||
|
ArtistId = artistId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,12 +16,15 @@ using NzbDrone.Core.Messaging.Commands;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.Tv.Events;
|
using NzbDrone.Core.Tv.Events;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using NzbDrone.Core.Music.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
public interface IDiskScanService
|
public interface IDiskScanService
|
||||||
{
|
{
|
||||||
void Scan(Series series);
|
void Scan(Series series);
|
||||||
|
void Scan(Artist artist);
|
||||||
string[] GetVideoFiles(string path, bool allDirectories = true);
|
string[] GetVideoFiles(string path, bool allDirectories = true);
|
||||||
string[] GetNonVideoFiles(string path, bool allDirectories = true);
|
string[] GetNonVideoFiles(string path, bool allDirectories = true);
|
||||||
List<string> FilterFiles(Series series, IEnumerable<string> files);
|
List<string> FilterFiles(Series series, IEnumerable<string> files);
|
||||||
|
@ -30,13 +33,16 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
public class DiskScanService :
|
public class DiskScanService :
|
||||||
IDiskScanService,
|
IDiskScanService,
|
||||||
IHandle<SeriesUpdatedEvent>,
|
IHandle<SeriesUpdatedEvent>,
|
||||||
IExecute<RescanSeriesCommand>
|
IExecute<RescanSeriesCommand>,
|
||||||
|
IHandle<ArtistUpdatedEvent>,
|
||||||
|
IExecute<RescanArtistCommand>
|
||||||
{
|
{
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IMakeImportDecision _importDecisionMaker;
|
private readonly IMakeImportDecision _importDecisionMaker;
|
||||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
private readonly ISeriesService _seriesService;
|
private readonly ISeriesService _seriesService;
|
||||||
|
private readonly IArtistService _artistService;
|
||||||
private readonly IMediaFileTableCleanupService _mediaFileTableCleanupService;
|
private readonly IMediaFileTableCleanupService _mediaFileTableCleanupService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
@ -46,6 +52,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
IImportApprovedEpisodes importApprovedEpisodes,
|
IImportApprovedEpisodes importApprovedEpisodes,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
ISeriesService seriesService,
|
ISeriesService seriesService,
|
||||||
|
IArtistService artistService,
|
||||||
IMediaFileTableCleanupService mediaFileTableCleanupService,
|
IMediaFileTableCleanupService mediaFileTableCleanupService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
|
@ -55,6 +62,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
_importApprovedEpisodes = importApprovedEpisodes;
|
_importApprovedEpisodes = importApprovedEpisodes;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_seriesService = seriesService;
|
_seriesService = seriesService;
|
||||||
|
_artistService = artistService;
|
||||||
_mediaFileTableCleanupService = mediaFileTableCleanupService;
|
_mediaFileTableCleanupService = mediaFileTableCleanupService;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
@ -63,6 +71,58 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
private static readonly Regex ExcludedSubFoldersRegex = new Regex(@"(?:\\|\/|^)(extras|@eadir|extrafanart|plex\sversions|\..+)(?:\\|\/)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static readonly Regex ExcludedSubFoldersRegex = new Regex(@"(?:\\|\/|^)(extras|@eadir|extrafanart|plex\sversions|\..+)(?:\\|\/)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
private static readonly Regex ExcludedFilesRegex = new Regex(@"^\._|Thumbs\.db", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
private static readonly Regex ExcludedFilesRegex = new Regex(@"^\._|Thumbs\.db", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
public void Scan(Artist artist)
|
||||||
|
{
|
||||||
|
var rootFolder = _diskProvider.GetParentFolder(artist.Path);
|
||||||
|
|
||||||
|
if (!_diskProvider.FolderExists(rootFolder))
|
||||||
|
{
|
||||||
|
_logger.Warn("Artist' root folder ({0}) doesn't exist.", rootFolder);
|
||||||
|
_eventAggregator.PublishEvent(new ArtistScanSkippedEvent(artist, ArtistScanSkippedReason.RootFolderDoesNotExist));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_diskProvider.GetDirectories(rootFolder).Empty())
|
||||||
|
{
|
||||||
|
_logger.Warn("Artist' root folder ({0}) is empty.", rootFolder);
|
||||||
|
_eventAggregator.PublishEvent(new ArtistScanSkippedEvent(artist, ArtistScanSkippedReason.RootFolderIsEmpty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.ProgressInfo("Scanning disk for {0}", artist.ArtistName);
|
||||||
|
|
||||||
|
if (!_diskProvider.FolderExists(artist.Path))
|
||||||
|
{
|
||||||
|
if (_configService.CreateEmptySeriesFolders)
|
||||||
|
{
|
||||||
|
_logger.Debug("Creating missing artist folder: {0}", artist.Path);
|
||||||
|
_diskProvider.CreateFolder(artist.Path);
|
||||||
|
SetPermissions(artist.Path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Debug("Artist folder doesn't exist: {0}", artist.Path);
|
||||||
|
}
|
||||||
|
CleanMediaFiles(artist, new List<string>());
|
||||||
|
CompletedScanning(artist);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var musicFilesStopwatch = Stopwatch.StartNew();
|
||||||
|
var mediaFileList = FilterFiles(artist, GetMusicFiles(artist.Path)).ToList();
|
||||||
|
musicFilesStopwatch.Stop();
|
||||||
|
_logger.Trace("Finished getting track files for: {0} [{1}]", artist, musicFilesStopwatch.Elapsed);
|
||||||
|
|
||||||
|
CleanMediaFiles(artist, mediaFileList);
|
||||||
|
|
||||||
|
var decisionsStopwatch = Stopwatch.StartNew();
|
||||||
|
var decisions = _importDecisionMaker.GetImportDecisions(mediaFileList, artist);
|
||||||
|
decisionsStopwatch.Stop();
|
||||||
|
_logger.Trace("Import decisions complete for: {0} [{1}]", artist, decisionsStopwatch.Elapsed);
|
||||||
|
_importApprovedTracks.Import(decisions, false);
|
||||||
|
|
||||||
|
CompletedScanning(artist);
|
||||||
|
}
|
||||||
public void Scan(Series series)
|
public void Scan(Series series)
|
||||||
{
|
{
|
||||||
var rootFolder = _diskProvider.GetParentFolder(series.Path);
|
var rootFolder = _diskProvider.GetParentFolder(series.Path);
|
||||||
|
@ -122,12 +182,24 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
_mediaFileTableCleanupService.Clean(series, mediaFileList);
|
_mediaFileTableCleanupService.Clean(series, mediaFileList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CleanMediaFiles(Artist artist, List<string> mediaFileList)
|
||||||
|
{
|
||||||
|
_logger.Debug("{0} Cleaning up media files in DB", artist);
|
||||||
|
_mediaFileTableCleanupService.Clean(artist, mediaFileList);
|
||||||
|
}
|
||||||
|
|
||||||
private void CompletedScanning(Series series)
|
private void CompletedScanning(Series series)
|
||||||
{
|
{
|
||||||
_logger.Info("Completed scanning disk for {0}", series.Title);
|
_logger.Info("Completed scanning disk for {0}", series.Title);
|
||||||
_eventAggregator.PublishEvent(new SeriesScannedEvent(series));
|
_eventAggregator.PublishEvent(new SeriesScannedEvent(series));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CompletedScanning(Artist artist)
|
||||||
|
{
|
||||||
|
_logger.Info("Completed scanning disk for {0}", artist.ArtistName);
|
||||||
|
_eventAggregator.PublishEvent(new ArtistScannedEvent(artist));
|
||||||
|
}
|
||||||
|
|
||||||
public string[] GetVideoFiles(string path, bool allDirectories = true)
|
public string[] GetVideoFiles(string path, bool allDirectories = true)
|
||||||
{
|
{
|
||||||
_logger.Debug("Scanning '{0}' for video files", path);
|
_logger.Debug("Scanning '{0}' for video files", path);
|
||||||
|
@ -143,9 +215,24 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
return mediaFileList.ToArray();
|
return mediaFileList.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string[] GetMusicFiles(string path, bool allDirectories = true)
|
||||||
|
{
|
||||||
|
_logger.Debug("Scanning '{0}' for music files", path);
|
||||||
|
|
||||||
|
var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||||
|
var filesOnDisk = _diskProvider.GetFiles(path, searchOption).ToList();
|
||||||
|
|
||||||
|
var mediaFileList = filesOnDisk.Where(file => MediaFileExtensions.Extensions.Contains(Path.GetExtension(file).ToLower()))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
_logger.Trace("{0} files were found in {1}", filesOnDisk.Count, path);
|
||||||
|
_logger.Debug("{0} video files were found in {1}", mediaFileList.Count, path);
|
||||||
|
return mediaFileList.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
public string[] GetNonVideoFiles(string path, bool allDirectories = true)
|
public string[] GetNonVideoFiles(string path, bool allDirectories = true)
|
||||||
{
|
{
|
||||||
_logger.Debug("Scanning '{0}' for non-video files", path);
|
_logger.Debug("Scanning '{0}' for non-music files", path);
|
||||||
|
|
||||||
var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||||
var filesOnDisk = _diskProvider.GetFiles(path, searchOption).ToList();
|
var filesOnDisk = _diskProvider.GetFiles(path, searchOption).ToList();
|
||||||
|
@ -154,7 +241,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
_logger.Trace("{0} files were found in {1}", filesOnDisk.Count, path);
|
_logger.Trace("{0} files were found in {1}", filesOnDisk.Count, path);
|
||||||
_logger.Debug("{0} non-video files were found in {1}", mediaFileList.Count, path);
|
_logger.Debug("{0} non-music files were found in {1}", mediaFileList.Count, path);
|
||||||
return mediaFileList.ToArray();
|
return mediaFileList.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +252,13 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<string> FilterFiles(Artist artist, IEnumerable<string> files)
|
||||||
|
{
|
||||||
|
return files.Where(file => !ExcludedSubFoldersRegex.IsMatch(artist.Path.GetRelativePath(file)))
|
||||||
|
.Where(file => !ExcludedFilesRegex.IsMatch(Path.GetFileName(file)))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
private void SetPermissions(string path)
|
private void SetPermissions(string path)
|
||||||
{
|
{
|
||||||
if (!_configService.SetPermissionsLinux)
|
if (!_configService.SetPermissionsLinux)
|
||||||
|
@ -191,6 +285,11 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
Scan(message.Series);
|
Scan(message.Series);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Handle(ArtistUpdatedEvent message)
|
||||||
|
{
|
||||||
|
Scan(message.Artist);
|
||||||
|
}
|
||||||
|
|
||||||
public void Execute(RescanSeriesCommand message)
|
public void Execute(RescanSeriesCommand message)
|
||||||
{
|
{
|
||||||
if (message.SeriesId.HasValue)
|
if (message.SeriesId.HasValue)
|
||||||
|
@ -209,5 +308,24 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Execute(RescanArtistCommand message)
|
||||||
|
{
|
||||||
|
if (message.ArtistId.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
var artist = _artistService.FindById(message.ArtistId);
|
||||||
|
Scan(artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var allArtists = _artistService.GetAllArtists();
|
||||||
|
|
||||||
|
foreach (var artist in allArtists)
|
||||||
|
{
|
||||||
|
Scan(artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,14 +11,16 @@ using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||||
{
|
{
|
||||||
public interface IMakeImportDecision
|
public interface IMakeImportDecision
|
||||||
{
|
{
|
||||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series);
|
//List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series);
|
||||||
List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
|
List<ImportDecision> GetImportDecisions(List<string> musicFiles, Artist artist);
|
||||||
|
//List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
|
||||||
|
List<ImportDecision> GetImportDecisions(List<string> musicFiles, Artist artist, ParsedTrackInfo folderInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImportDecisionMaker : IMakeImportDecision
|
public class ImportDecisionMaker : IMakeImportDecision
|
||||||
|
@ -48,65 +50,87 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series)
|
//public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series)
|
||||||
|
//{
|
||||||
|
// return GetImportDecisions(videoFiles, series, null, false);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Artist series, ParsedEpisodeInfo folderInfo, bool sceneSource)
|
||||||
|
//{
|
||||||
|
// var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series);
|
||||||
|
|
||||||
|
// _logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());
|
||||||
|
|
||||||
|
// var shouldUseFolderName = ShouldUseFolderName(videoFiles, series, folderInfo);
|
||||||
|
// var decisions = new List<ImportDecision>();
|
||||||
|
|
||||||
|
// foreach (var file in newFiles)
|
||||||
|
// {
|
||||||
|
// decisions.AddIfNotNull(GetDecision(file, series, folderInfo, sceneSource, shouldUseFolderName));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return decisions;
|
||||||
|
//}
|
||||||
|
|
||||||
|
public List<ImportDecision> GetImportDecisions(List<string> musicFiles, Artist artist)
|
||||||
{
|
{
|
||||||
return GetImportDecisions(videoFiles, series, null, false);
|
return GetImportDecisions(musicFiles, artist, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ImportDecision> GetImportDecisions(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource)
|
public List<ImportDecision> GetImportDecisions(List<string> musicFiles, Artist artist, ParsedTrackInfo folderInfo)
|
||||||
{
|
{
|
||||||
var newFiles = _mediaFileService.FilterExistingFiles(videoFiles.ToList(), series);
|
var newFiles = _mediaFileService.FilterExistingFiles(musicFiles.ToList(), artist);
|
||||||
|
|
||||||
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, videoFiles.Count());
|
_logger.Debug("Analyzing {0}/{1} files.", newFiles.Count, musicFiles.Count());
|
||||||
|
|
||||||
var shouldUseFolderName = ShouldUseFolderName(videoFiles, series, folderInfo);
|
var shouldUseFolderName = ShouldUseFolderName(musicFiles, artist, folderInfo);
|
||||||
var decisions = new List<ImportDecision>();
|
var decisions = new List<ImportDecision>();
|
||||||
|
|
||||||
foreach (var file in newFiles)
|
foreach (var file in newFiles)
|
||||||
{
|
{
|
||||||
decisions.AddIfNotNull(GetDecision(file, series, folderInfo, sceneSource, shouldUseFolderName));
|
decisions.AddIfNotNull(GetDecision(file, artist, folderInfo, shouldUseFolderName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return decisions;
|
return decisions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImportDecision GetDecision(string file, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource, bool shouldUseFolderName)
|
private ImportDecision GetDecision(string file, Artist artist, ParsedTrackInfo folderInfo, bool sceneSource, bool shouldUseFolderName)
|
||||||
{
|
{
|
||||||
ImportDecision decision = null;
|
ImportDecision decision = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var localEpisode = _parsingService.GetLocalEpisode(file, series, shouldUseFolderName ? folderInfo : null, sceneSource);
|
var localTrack = _parsingService.GetLocalTrack(file, artist, shouldUseFolderName ? folderInfo : null);
|
||||||
|
|
||||||
if (localEpisode != null)
|
if (localTrack != null)
|
||||||
{
|
{
|
||||||
localEpisode.Quality = GetQuality(folderInfo, localEpisode.Quality, series);
|
localTrack.Quality = GetQuality(folderInfo, localTrack.Quality, artist);
|
||||||
localEpisode.Size = _diskProvider.GetFileSize(file);
|
localTrack.Size = _diskProvider.GetFileSize(file);
|
||||||
|
|
||||||
_logger.Debug("Size: {0}", localEpisode.Size);
|
_logger.Debug("Size: {0}", localTrack.Size);
|
||||||
|
|
||||||
//TODO: make it so media info doesn't ruin the import process of a new series
|
//TODO: make it so media info doesn't ruin the import process of a new series
|
||||||
if (sceneSource)
|
if (sceneSource)
|
||||||
{
|
{
|
||||||
localEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
|
localTrack.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localEpisode.Episodes.Empty())
|
if (localTrack.Tracks.Empty())
|
||||||
{
|
{
|
||||||
decision = new ImportDecision(localEpisode, new Rejection("Invalid season or episode"));
|
decision = new ImportDecision(localTrack, new Rejection("Invalid album or track"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
decision = GetDecision(localEpisode);
|
decision = GetDecision(localTrack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
localEpisode = new LocalEpisode();
|
localTrack = new LocalTrack();
|
||||||
localEpisode.Path = file;
|
localTrack.Path = file;
|
||||||
|
|
||||||
decision = new ImportDecision(localEpisode, new Rejection("Unable to parse file"));
|
decision = new ImportDecision(localTrack, new Rejection("Unable to parse file"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -150,28 +174,28 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ShouldUseFolderName(List<string> videoFiles, Series series, ParsedEpisodeInfo folderInfo)
|
private bool ShouldUseFolderName(List<string> musicFiles, Artist artist, ParsedTrackInfo folderInfo)
|
||||||
{
|
{
|
||||||
if (folderInfo == null)
|
if (folderInfo == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (folderInfo.FullSeason)
|
//if (folderInfo.FullSeason)
|
||||||
{
|
//{
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
return videoFiles.Count(file =>
|
return musicFiles.Count(file =>
|
||||||
{
|
{
|
||||||
var size = _diskProvider.GetFileSize(file);
|
var size = _diskProvider.GetFileSize(file);
|
||||||
var fileQuality = QualityParser.ParseQuality(file);
|
var fileQuality = QualityParser.ParseQuality(file);
|
||||||
var sample = _detectSample.IsSample(series, GetQuality(folderInfo, fileQuality, series), file, size, folderInfo.IsPossibleSpecialEpisode);
|
//var sample = _detectSample.IsSample(artist, GetQuality(folderInfo, fileQuality, artist), file, size, folderInfo.IsPossibleSpecialEpisode);
|
||||||
|
|
||||||
if (sample)
|
//if (sample)
|
||||||
{
|
//{
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (SceneChecker.IsSceneTitle(Path.GetFileName(file)))
|
if (SceneChecker.IsSceneTitle(Path.GetFileName(file)))
|
||||||
{
|
{
|
||||||
|
@ -182,9 +206,9 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||||
}) == 1;
|
}) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private QualityModel GetQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Series series)
|
private QualityModel GetQuality(ParsedTrackInfo folderInfo, QualityModel fileQuality, Artist artist)
|
||||||
{
|
{
|
||||||
if (UseFolderQuality(folderInfo, fileQuality, series))
|
if (UseFolderQuality(folderInfo, fileQuality, artist))
|
||||||
{
|
{
|
||||||
_logger.Debug("Using quality from folder: {0}", folderInfo.Quality);
|
_logger.Debug("Using quality from folder: {0}", folderInfo.Quality);
|
||||||
return folderInfo.Quality;
|
return folderInfo.Quality;
|
||||||
|
@ -193,7 +217,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||||
return fileQuality;
|
return fileQuality;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UseFolderQuality(ParsedEpisodeInfo folderInfo, QualityModel fileQuality, Series series)
|
private bool UseFolderQuality(ParsedTrackInfo folderInfo, QualityModel fileQuality, Artist artist)
|
||||||
{
|
{
|
||||||
if (folderInfo == null)
|
if (folderInfo == null)
|
||||||
{
|
{
|
||||||
|
@ -210,7 +234,7 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new QualityModelComparer(series.Profile).Compare(folderInfo.Quality, fileQuality) > 0)
|
if (new QualityModelComparer(artist.Profile).Compare(folderInfo.Quality, fileQuality) > 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using NzbDrone.Common.Messaging;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles.Events
|
||||||
|
{
|
||||||
|
public class ArtistScanSkippedEvent : IEvent
|
||||||
|
{
|
||||||
|
public Artist Artist { get; private set; }
|
||||||
|
public ArtistScanSkippedReason Reason { get; private set; }
|
||||||
|
|
||||||
|
public ArtistScanSkippedEvent(Artist artist, ArtistScanSkippedReason reason)
|
||||||
|
{
|
||||||
|
Artist = artist;
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ArtistScanSkippedReason
|
||||||
|
{
|
||||||
|
RootFolderDoesNotExist,
|
||||||
|
RootFolderIsEmpty
|
||||||
|
}
|
||||||
|
}
|
19
src/NzbDrone.Core/MediaFiles/Events/ArtistScannedEvent.cs
Normal file
19
src/NzbDrone.Core/MediaFiles/Events/ArtistScannedEvent.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using NzbDrone.Common.Messaging;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MediaFiles.Events
|
||||||
|
{
|
||||||
|
public class ArtistScannedEvent : IEvent
|
||||||
|
{
|
||||||
|
public Artist Artist { get; private set; }
|
||||||
|
|
||||||
|
public ArtistScannedEvent(Artist artist)
|
||||||
|
{
|
||||||
|
Artist = artist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
@ -5,36 +6,28 @@ using NzbDrone.Core.Messaging.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
public interface IMediaFileRepository : IBasicRepository<EpisodeFile>
|
public interface IMediaFileRepository : IBasicRepository<TrackFile>
|
||||||
{
|
{
|
||||||
List<EpisodeFile> GetFilesBySeries(int seriesId);
|
List<TrackFile> GetFilesByArtist(string artistId);
|
||||||
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
|
List<TrackFile> GetFilesWithoutMediaInfo();
|
||||||
List<EpisodeFile> GetFilesWithoutMediaInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class MediaFileRepository : BasicRepository<EpisodeFile>, IMediaFileRepository
|
public class MediaFileRepository : BasicRepository<TrackFile>, IMediaFileRepository
|
||||||
{
|
{
|
||||||
public MediaFileRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
public MediaFileRepository(IMainDatabase database, IEventAggregator eventAggregator)
|
||||||
: base(database, eventAggregator)
|
: base(database, eventAggregator)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EpisodeFile> GetFilesBySeries(int seriesId)
|
public List<TrackFile> GetFilesWithoutMediaInfo()
|
||||||
{
|
|
||||||
return Query.Where(c => c.SeriesId == seriesId).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber)
|
|
||||||
{
|
|
||||||
return Query.Where(c => c.SeriesId == seriesId)
|
|
||||||
.AndWhere(c => c.SeasonNumber == seasonNumber)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EpisodeFile> GetFilesWithoutMediaInfo()
|
|
||||||
{
|
{
|
||||||
return Query.Where(c => c.MediaInfo == null).ToList();
|
return Query.Where(c => c.MediaInfo == null).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TrackFile> GetFilesByArtist(string artistId)
|
||||||
|
{
|
||||||
|
return Query.Where(c => c.SpotifyTrackId == artistId).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,24 +7,26 @@ using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.Tv.Events;
|
using NzbDrone.Core.Tv.Events;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using System;
|
||||||
|
using NzbDrone.Core.Music.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles
|
namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
public interface IMediaFileService
|
public interface IMediaFileService
|
||||||
{
|
{
|
||||||
EpisodeFile Add(EpisodeFile episodeFile);
|
TrackFile Add(TrackFile trackFile);
|
||||||
void Update(EpisodeFile episodeFile);
|
void Update(TrackFile trackFile);
|
||||||
void Delete(EpisodeFile episodeFile, DeleteMediaFileReason reason);
|
void Delete(TrackFile trackFile, DeleteMediaFileReason reason);
|
||||||
List<EpisodeFile> GetFilesBySeries(int seriesId);
|
List<TrackFile> GetFilesByArtist(string artistId);
|
||||||
List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber);
|
List<TrackFile> GetFilesWithoutMediaInfo();
|
||||||
List<EpisodeFile> GetFilesWithoutMediaInfo();
|
List<string> FilterExistingFiles(List<string> files, Artist artist);
|
||||||
List<string> FilterExistingFiles(List<string> files, Series series);
|
TrackFile Get(int id);
|
||||||
EpisodeFile Get(int id);
|
List<TrackFile> Get(IEnumerable<int> ids);
|
||||||
List<EpisodeFile> Get(IEnumerable<int> ids);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MediaFileService : IMediaFileService, IHandleAsync<SeriesDeletedEvent>
|
public class MediaFileService : IMediaFileService, IHandleAsync<ArtistDeletedEvent>
|
||||||
{
|
{
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly IMediaFileRepository _mediaFileRepository;
|
private readonly IMediaFileRepository _mediaFileRepository;
|
||||||
|
@ -37,66 +39,63 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EpisodeFile Add(EpisodeFile episodeFile)
|
public TrackFile Add(TrackFile trackFile)
|
||||||
{
|
{
|
||||||
var addedFile = _mediaFileRepository.Insert(episodeFile);
|
var addedFile = _mediaFileRepository.Insert(trackFile);
|
||||||
_eventAggregator.PublishEvent(new EpisodeFileAddedEvent(addedFile));
|
_eventAggregator.PublishEvent(new TrackFileAddedEvent(addedFile));
|
||||||
return addedFile;
|
return addedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(EpisodeFile episodeFile)
|
public void Update(TrackFile trackFile)
|
||||||
{
|
{
|
||||||
_mediaFileRepository.Update(episodeFile);
|
_mediaFileRepository.Update(trackFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(EpisodeFile episodeFile, DeleteMediaFileReason reason)
|
public void Delete(TrackFile trackFile, DeleteMediaFileReason reason)
|
||||||
{
|
{
|
||||||
//Little hack so we have the episodes and series attached for the event consumers
|
//Little hack so we have the tracks and artist attached for the event consumers
|
||||||
episodeFile.Episodes.LazyLoad();
|
trackFile.Episodes.LazyLoad();
|
||||||
episodeFile.Path = Path.Combine(episodeFile.Series.Value.Path, episodeFile.RelativePath);
|
trackFile.Path = Path.Combine(trackFile.Artist.Value.Path, trackFile.RelativePath);
|
||||||
|
|
||||||
_mediaFileRepository.Delete(episodeFile);
|
_mediaFileRepository.Delete(trackFile);
|
||||||
_eventAggregator.PublishEvent(new EpisodeFileDeletedEvent(episodeFile, reason));
|
_eventAggregator.PublishEvent(new TrackFileDeletedEvent(trackFile, reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EpisodeFile> GetFilesBySeries(int seriesId)
|
|
||||||
{
|
|
||||||
return _mediaFileRepository.GetFilesBySeries(seriesId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EpisodeFile> GetFilesBySeason(int seriesId, int seasonNumber)
|
public List<TrackFile> GetFilesWithoutMediaInfo()
|
||||||
{
|
|
||||||
return _mediaFileRepository.GetFilesBySeason(seriesId, seasonNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<EpisodeFile> GetFilesWithoutMediaInfo()
|
|
||||||
{
|
{
|
||||||
return _mediaFileRepository.GetFilesWithoutMediaInfo();
|
return _mediaFileRepository.GetFilesWithoutMediaInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> FilterExistingFiles(List<string> files, Series series)
|
public List<string> FilterExistingFiles(List<string> files, Artist artist)
|
||||||
{
|
{
|
||||||
var seriesFiles = GetFilesBySeries(series.Id).Select(f => Path.Combine(series.Path, f.RelativePath)).ToList();
|
var artistFiles = GetFilesByArtist(artist.SpotifyId).Select(f => Path.Combine(artist.Path, f.RelativePath)).ToList();
|
||||||
|
|
||||||
if (!seriesFiles.Any()) return files;
|
if (!artistFiles.Any()) return files;
|
||||||
|
|
||||||
return files.Except(seriesFiles, PathEqualityComparer.Instance).ToList();
|
return files.Except(artistFiles, PathEqualityComparer.Instance).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EpisodeFile Get(int id)
|
public TrackFile Get(int id)
|
||||||
{
|
{
|
||||||
|
// TODO: Should this be spotifyID or DB Id?
|
||||||
return _mediaFileRepository.Get(id);
|
return _mediaFileRepository.Get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EpisodeFile> Get(IEnumerable<int> ids)
|
public List<TrackFile> Get(IEnumerable<int> ids)
|
||||||
{
|
{
|
||||||
return _mediaFileRepository.Get(ids).ToList();
|
return _mediaFileRepository.Get(ids).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleAsync(SeriesDeletedEvent message)
|
public void HandleAsync(ArtistDeletedEvent message)
|
||||||
{
|
{
|
||||||
var files = GetFilesBySeries(message.Series.Id);
|
var files = GetFilesByArtist(message.Artist.SpotifyId);
|
||||||
_mediaFileRepository.DeleteMany(files);
|
_mediaFileRepository.DeleteMany(files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TrackFile> GetFilesByArtist(string artistId)
|
||||||
|
{
|
||||||
|
return _mediaFileRepository.GetFilesByArtist(artistId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
public class TrackFile : ModelBase
|
public class TrackFile : ModelBase
|
||||||
{
|
{
|
||||||
public int ItunesTrackId { get; set; }
|
public string SpotifyTrackId { get; set; }
|
||||||
public int AlbumId { get; set; }
|
public int AlbumId { get; set; }
|
||||||
public string RelativePath { get; set; }
|
public string RelativePath { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
|
|
@ -89,11 +89,12 @@ namespace NzbDrone.Core.Music
|
||||||
return _artistRepository.All().ToList();
|
return _artistRepository.All().ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Artist GetArtist(int artistId)
|
public Artist GetArtist(int artistDBId)
|
||||||
{
|
{
|
||||||
return _artistRepository.Get(artistId);
|
return _artistRepository.Get(artistDBId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Artist> GetArtists(IEnumerable<int> artistIds)
|
public List<Artist> GetArtists(IEnumerable<int> artistIds)
|
||||||
{
|
{
|
||||||
return _artistRepository.Get(artistIds).ToList();
|
return _artistRepository.Get(artistIds).ToList();
|
||||||
|
|
|
@ -157,7 +157,7 @@ namespace NzbDrone.Core.Music
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Info("Skipping refresh of artist: {0}", artist.ArtistName);
|
_logger.Info("Skipping refresh of artist: {0}", artist.ArtistName);
|
||||||
//TODO: _diskScanService.Scan(artist);
|
_diskScanService.Scan(artist);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
14
src/NzbDrone.Core/Parser/Model/ArtistTitleInfo.cs
Normal file
14
src/NzbDrone.Core/Parser/Model/ArtistTitleInfo.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Parser.Model
|
||||||
|
{
|
||||||
|
public class ArtistTitleInfo
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string TitleWithoutYear { get; set; }
|
||||||
|
public int Year { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,87 +9,34 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
{
|
{
|
||||||
public class ParsedTrackInfo
|
public class ParsedTrackInfo
|
||||||
{
|
{
|
||||||
// [TODO]: Properly fill this out
|
|
||||||
public string ArtistTitle { get; set; }
|
public string ArtistTitle { get; set; }
|
||||||
public string AlbumTitle { get; set; }
|
public string AlbumTitle { get; set; }
|
||||||
public SeriesTitleInfo SeriesTitleInfo { get; set; }
|
public ArtistTitleInfo ArtistTitleInfo { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public int SeasonNumber { get; set; }
|
public string AlbumId { get; set; } // maybe
|
||||||
public int[] EpisodeNumbers { get; set; }
|
public int[] TrackNumbers { get; set; }
|
||||||
public int[] AbsoluteEpisodeNumbers { get; set; }
|
//public Language Language { get; set; }
|
||||||
public string AirDate { get; set; }
|
|
||||||
public Language Language { get; set; }
|
|
||||||
public bool FullSeason { get; set; }
|
|
||||||
public bool Special { get; set; }
|
|
||||||
public string ReleaseGroup { get; set; }
|
public string ReleaseGroup { get; set; }
|
||||||
public string ReleaseHash { get; set; }
|
public string ReleaseHash { get; set; }
|
||||||
|
|
||||||
public ParsedTrackInfo()
|
public ParsedTrackInfo()
|
||||||
{
|
{
|
||||||
EpisodeNumbers = new int[0];
|
TrackNumbers = new int[0];
|
||||||
AbsoluteEpisodeNumbers = new int[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDaily
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return !string.IsNullOrWhiteSpace(AirDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
//This prevents manually downloading a release from blowing up in mono
|
|
||||||
//TODO: Is there a better way?
|
|
||||||
private set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsAbsoluteNumbering
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return AbsoluteEpisodeNumbers.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
//This prevents manually downloading a release from blowing up in mono
|
|
||||||
//TODO: Is there a better way?
|
|
||||||
private set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPossibleSpecialEpisode
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
// if we don't have eny episode numbers we are likely a special episode and need to do a search by episode title
|
|
||||||
return (AirDate.IsNullOrWhiteSpace() &&
|
|
||||||
ArtistTitle.IsNullOrWhiteSpace() &&
|
|
||||||
(EpisodeNumbers.Length == 0 || SeasonNumber == 0) ||
|
|
||||||
!ArtistTitle.IsNullOrWhiteSpace() && Special);
|
|
||||||
}
|
|
||||||
|
|
||||||
//This prevents manually downloading a release from blowing up in mono
|
|
||||||
//TODO: Is there a better way?
|
|
||||||
private set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
string episodeString = "[Unknown Episode]";
|
string episodeString = "[Unknown Track]";
|
||||||
|
|
||||||
if (IsDaily && EpisodeNumbers.Empty())
|
|
||||||
|
if (TrackNumbers != null && TrackNumbers.Any())
|
||||||
{
|
{
|
||||||
episodeString = string.Format("{0}", AirDate);
|
episodeString = string.Format("T{1}", string.Join("-", TrackNumbers.Select(c => c.ToString("00"))));
|
||||||
}
|
|
||||||
else if (FullSeason)
|
|
||||||
{
|
|
||||||
episodeString = string.Format("Season {0:00}", SeasonNumber);
|
|
||||||
}
|
|
||||||
else if (EpisodeNumbers != null && EpisodeNumbers.Any())
|
|
||||||
{
|
|
||||||
episodeString = string.Format("S{0:00}E{1}", SeasonNumber, string.Join("-", EpisodeNumbers.Select(c => c.ToString("00"))));
|
|
||||||
}
|
|
||||||
else if (AbsoluteEpisodeNumbers != null && AbsoluteEpisodeNumbers.Any())
|
|
||||||
{
|
|
||||||
episodeString = string.Format("{0}", string.Join("-", AbsoluteEpisodeNumbers.Select(c => c.ToString("000"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return string.Format("{0} - {1} {2}", ArtistTitle, episodeString, Quality);
|
return string.Format("{0} - {1} {2}", ArtistTitle, episodeString, Quality);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace NzbDrone.Core.Parser
|
||||||
|
|
||||||
// Music stuff here
|
// Music stuff here
|
||||||
LocalTrack GetLocalTrack(string filename, Artist artist);
|
LocalTrack GetLocalTrack(string filename, Artist artist);
|
||||||
LocalTrack GetLocalTrack(string filename, Artist artist, ParsedTrackInfo folderInfo, bool sceneSource);
|
LocalTrack GetLocalTrack(string filename, Artist artist, ParsedTrackInfo folderInfo);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,12 +40,14 @@ namespace NzbDrone.Core.Parser
|
||||||
|
|
||||||
public ParsingService(IEpisodeService episodeService,
|
public ParsingService(IEpisodeService episodeService,
|
||||||
ISeriesService seriesService,
|
ISeriesService seriesService,
|
||||||
|
ITrackService trackService,
|
||||||
// ISceneMappingService sceneMappingService,
|
// ISceneMappingService sceneMappingService,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_episodeService = episodeService;
|
_episodeService = episodeService;
|
||||||
_seriesService = seriesService;
|
_seriesService = seriesService;
|
||||||
// _sceneMappingService = sceneMappingService;
|
// _sceneMappingService = sceneMappingService;
|
||||||
|
_trackService = trackService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,6 +409,73 @@ namespace NzbDrone.Core.Parser
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Track> GetStandardTracks(Artist artist, ParsedTrackInfo parsedTrackInfo, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
var result = new List<Track>();
|
||||||
|
|
||||||
|
if (parsedTrackInfo.TrackNumbers == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var trackNumber in parsedTrackInfo.TrackNumbers)
|
||||||
|
{
|
||||||
|
//if (series.UseSceneNumbering && sceneSource)
|
||||||
|
//{
|
||||||
|
// List<Episode> episodes = new List<Episode>();
|
||||||
|
|
||||||
|
// if (searchCriteria != null)
|
||||||
|
// {
|
||||||
|
// episodes = searchCriteria.Episodes.Where(e => e.SceneSeasonNumber == parsedTrackInfo.SeasonNumber &&
|
||||||
|
// e.SceneEpisodeNumber == trackNumber).ToList();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!episodes.Any())
|
||||||
|
// {
|
||||||
|
// episodes = _episodeService.FindEpisodesBySceneNumbering(series.Id, seasonNumber, trackNumber);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (episodes != null && episodes.Any())
|
||||||
|
// {
|
||||||
|
// _logger.Debug("Using Scene to TVDB Mapping for: {0} - Scene: {1}x{2:00} - TVDB: {3}",
|
||||||
|
// series.Title,
|
||||||
|
// episodes.First().SceneSeasonNumber,
|
||||||
|
// episodes.First().SceneEpisodeNumber,
|
||||||
|
// string.Join(", ", episodes.Select(e => string.Format("{0}x{1:00}", e.SeasonNumber, e.EpisodeNumber))));
|
||||||
|
|
||||||
|
// result.AddRange(episodes);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
Track trackInfo = null;
|
||||||
|
|
||||||
|
if (searchCriteria != null)
|
||||||
|
{
|
||||||
|
trackInfo = searchCriteria.Tracks.SingleOrDefault(e => e.TrackNumber == trackNumber); //e => e.SeasonNumber == seasonNumber && e.TrackNumber == trackNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackInfo == null)
|
||||||
|
{
|
||||||
|
trackInfo = _trackService.FindTrack(artist.SpotifyId, trackNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackInfo != null)
|
||||||
|
{
|
||||||
|
result.Add(trackInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Debug("Unable to find {0}", parsedEpisodeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private List<Episode> GetStandardEpisodes(Series series, ParsedEpisodeInfo parsedEpisodeInfo, bool sceneSource, SearchCriteriaBase searchCriteria)
|
private List<Episode> GetStandardEpisodes(Series series, ParsedEpisodeInfo parsedEpisodeInfo, bool sceneSource, SearchCriteriaBase searchCriteria)
|
||||||
{
|
{
|
||||||
var result = new List<Episode>();
|
var result = new List<Episode>();
|
||||||
|
@ -486,10 +555,10 @@ namespace NzbDrone.Core.Parser
|
||||||
|
|
||||||
public LocalTrack GetLocalTrack(string filename, Artist artist)
|
public LocalTrack GetLocalTrack(string filename, Artist artist)
|
||||||
{
|
{
|
||||||
return GetLocalTrack(filename, artist, null, false);
|
return GetLocalTrack(filename, artist, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalTrack GetLocalTrack(string filename, Artist artist, ParsedTrackInfo folderInfo, bool sceneSource)
|
public LocalTrack GetLocalTrack(string filename, Artist artist, ParsedTrackInfo folderInfo)
|
||||||
{
|
{
|
||||||
ParsedTrackInfo parsedTrackInfo;
|
ParsedTrackInfo parsedTrackInfo;
|
||||||
|
|
||||||
|
@ -504,7 +573,7 @@ namespace NzbDrone.Core.Parser
|
||||||
parsedTrackInfo = Parser.ParseMusicPath(filename);
|
parsedTrackInfo = Parser.ParseMusicPath(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parsedTrackInfo == null || parsedTrackInfo.IsPossibleSpecialEpisode)
|
if (parsedTrackInfo == null)
|
||||||
{
|
{
|
||||||
var title = Path.GetFileNameWithoutExtension(filename);
|
var title = Path.GetFileNameWithoutExtension(filename);
|
||||||
//var specialEpisodeInfo = ParseSpecialEpisodeTitle(title, series);
|
//var specialEpisodeInfo = ParseSpecialEpisodeTitle(title, series);
|
||||||
|
@ -525,7 +594,7 @@ namespace NzbDrone.Core.Parser
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tracks = GetTracks(parsedTrackInfo, artist, sceneSource);
|
var tracks = GetTracks(parsedTrackInfo, artist);
|
||||||
|
|
||||||
return new LocalTrack
|
return new LocalTrack
|
||||||
{
|
{
|
||||||
|
@ -538,10 +607,10 @@ namespace NzbDrone.Core.Parser
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Track> GetTracks(ParsedTrackInfo parsedTrackInfo, Artist artist, bool sceneSource)
|
private List<Track> GetTracks(ParsedTrackInfo parsedTrackInfo, Artist artist)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
|
||||||
|
|
||||||
|
// TODO: Ensure GetTracks(parsedTrackInfo, artist) doesn't need any checks
|
||||||
/*if (parsedTrackInfo.FullSeason) // IF Album
|
/*if (parsedTrackInfo.FullSeason) // IF Album
|
||||||
{
|
{
|
||||||
return _trackService.GetTracksByAlbumTitle(artist.Id, parsedTrackInfo.AlbumTitle);
|
return _trackService.GetTracksByAlbumTitle(artist.Id, parsedTrackInfo.AlbumTitle);
|
||||||
|
@ -566,6 +635,7 @@ namespace NzbDrone.Core.Parser
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetStandardEpisodes(artist, parsedTrackInfo, sceneSource, searchCriteria);*/
|
return GetStandardEpisodes(artist, parsedTrackInfo, sceneSource, searchCriteria);*/
|
||||||
|
return GetStandardTracks(artist, parsedTrackInfo, searchCriteria);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,7 @@ var ReleaseLayout = require('./Release/ReleaseLayout');
|
||||||
var SystemLayout = require('./System/SystemLayout');
|
var SystemLayout = require('./System/SystemLayout');
|
||||||
var SeasonPassLayout = require('./SeasonPass/SeasonPassLayout');
|
var SeasonPassLayout = require('./SeasonPass/SeasonPassLayout');
|
||||||
var SeriesEditorLayout = require('./Series/Editor/SeriesEditorLayout');
|
var SeriesEditorLayout = require('./Series/Editor/SeriesEditorLayout');
|
||||||
|
var SeriesDetailsLayout = require('./Series/Details/SeriesDetailsLayout');
|
||||||
|
|
||||||
module.exports = NzbDroneController.extend({
|
module.exports = NzbDroneController.extend({
|
||||||
addSeries : function(action) {
|
addSeries : function(action) {
|
||||||
|
@ -17,6 +18,11 @@ module.exports = NzbDroneController.extend({
|
||||||
this.showMainRegion(new AddSeriesLayout({ action : action }));
|
this.showMainRegion(new AddSeriesLayout({ action : action }));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
artistDetails: function(query) {
|
||||||
|
this.setTitle('Artist Detail');
|
||||||
|
this.showMainRegion(new SeriesDetailsLayout());
|
||||||
|
},
|
||||||
|
|
||||||
calendar : function() {
|
calendar : function() {
|
||||||
this.setTitle('Calendar');
|
this.setTitle('Calendar');
|
||||||
this.showMainRegion(new CalendarLayout());
|
this.showMainRegion(new CalendarLayout());
|
||||||
|
|
|
@ -18,6 +18,7 @@ module.exports = Marionette.AppRouter.extend({
|
||||||
'rss' : 'rss',
|
'rss' : 'rss',
|
||||||
'system' : 'system',
|
'system' : 'system',
|
||||||
'system/:action' : 'system',
|
'system/:action' : 'system',
|
||||||
|
'artist/:query' : 'artistDetails',
|
||||||
'seasonpass' : 'seasonPass',
|
'seasonpass' : 'seasonPass',
|
||||||
'serieseditor' : 'seriesEditor',
|
'serieseditor' : 'seriesEditor',
|
||||||
':whatever' : 'showNotFound'
|
':whatever' : 'showNotFound'
|
||||||
|
|
|
@ -48,9 +48,10 @@ module.exports = Marionette.Layout.extend({
|
||||||
this.seriesCollection = ArtistCollection.clone();
|
this.seriesCollection = ArtistCollection.clone();
|
||||||
this.seriesCollection.shadowCollection.bindSignalR();
|
this.seriesCollection.shadowCollection.bindSignalR();
|
||||||
|
|
||||||
|
|
||||||
this.listenTo(this.model, 'change:monitored', this._setMonitoredState);
|
this.listenTo(this.model, 'change:monitored', this._setMonitoredState);
|
||||||
this.listenTo(this.model, 'remove', this._seriesRemoved);
|
this.listenTo(this.model, 'remove', this._seriesRemoved);
|
||||||
this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
|
//this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
|
||||||
|
|
||||||
this.listenTo(this.model, 'change', function(model, options) {
|
this.listenTo(this.model, 'change', function(model, options) {
|
||||||
if (options && options.changeSource === 'signalr') {
|
if (options && options.changeSource === 'signalr') {
|
||||||
|
@ -59,6 +60,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
this.listenTo(this.model, 'change:images', this._updateImages);
|
this.listenTo(this.model, 'change:images', this._updateImages);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow : function() {
|
onShow : function() {
|
||||||
|
@ -81,15 +83,16 @@ module.exports = Marionette.Layout.extend({
|
||||||
name : 'seriesSearch'
|
name : 'seriesSearch'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log(this.model);
|
||||||
|
|
||||||
CommandController.bindToCommand({
|
/*CommandController.bindToCommand({
|
||||||
element : this.ui.rename,
|
element : this.ui.rename,
|
||||||
command : {
|
command : {
|
||||||
name : 'renameFiles',
|
name : 'renameFiles',
|
||||||
seriesId : this.model.id,
|
seriesId : this.model.spotifyId,
|
||||||
seasonNumber : -1
|
seasonNumber : -1
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
},
|
},
|
||||||
|
|
||||||
onClose : function() {
|
onClose : function() {
|
||||||
|
@ -164,6 +167,7 @@ module.exports = Marionette.Layout.extend({
|
||||||
|
|
||||||
_showSeasons : function() {
|
_showSeasons : function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
return;
|
||||||
|
|
||||||
this.seasons.show(new LoadingView());
|
this.seasons.show(new LoadingView());
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="col-md-10 col-xs-9">
|
<div class="col-md-10 col-xs-9">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-10 col-xs-10">
|
<div class="col-md-10 col-xs-10">
|
||||||
<a href="artist/{{artistNameSlug}}" target="_blank">
|
<a href="artist/{{artistSlug}}" target="_blank">
|
||||||
<h2>{{artistName}}</h2>
|
<h2>{{artistName}}</h2>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue