sonarr-repo-only/NzbDrone.Core/Organizer/FileNameBuilder.cs

267 lines
12 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Organizer
{
public interface IBuildFileNames
{
2013-03-06 21:35:39 +00:00
string BuildFilename(IList<Episode> episodes, Series series, EpisodeFile episodeFile);
2013-03-06 22:20:34 +00:00
string BuildFilePath(Series series, int seasonNumber, string fileName, string extension);
}
2013-04-25 04:27:49 +00:00
public interface INamingConfigService
{
2013-04-25 04:27:49 +00:00
NamingConfig GetConfig();
NamingConfig Save(NamingConfig namingConfig);
}
2013-04-25 04:27:49 +00:00
public class NamingConfigService : INamingConfigService
{
private readonly IBasicRepository<NamingConfig> _repository;
public NamingConfigService(IBasicRepository<NamingConfig> repository)
{
2013-04-25 04:27:49 +00:00
_repository = repository;
}
public NamingConfig GetConfig()
{
var config = _repository.SingleOrDefault();
if (config == null)
{
_repository.Insert(NamingConfig.Default);
config = _repository.Single();
}
return config;
}
public NamingConfig Save(NamingConfig namingConfig)
{
return _repository.Upsert(namingConfig);
}
2013-04-25 04:27:49 +00:00
}
2013-04-25 04:27:49 +00:00
public class FileNameBuilder : IBuildFileNames
{
private readonly INamingConfigService _namingConfigService;
private readonly Logger _logger;
public FileNameBuilder(INamingConfigService namingConfigService, Logger logger)
{
2013-04-25 04:27:49 +00:00
_namingConfigService = namingConfigService;
_logger = logger;
}
2013-03-06 21:35:39 +00:00
2013-04-25 04:27:49 +00:00
2013-03-06 21:35:39 +00:00
public string BuildFilename(IList<Episode> episodes, Series series, EpisodeFile episodeFile)
{
2013-04-25 04:27:49 +00:00
var nameSpec = _namingConfigService.GetConfig();
2013-03-06 21:35:39 +00:00
if (nameSpec.UseSceneName)
{
if (String.IsNullOrWhiteSpace(episodeFile.SceneName))
{
2013-03-06 22:20:34 +00:00
return Path.GetFileNameWithoutExtension(episodeFile.Path);
}
return episodeFile.SceneName;
}
var sortedEpisodes = episodes.OrderBy(e => e.EpisodeNumber);
2013-03-06 22:20:34 +00:00
var numberStyle = GetNumberStyle(nameSpec.NumberStyle);
2013-03-06 22:20:34 +00:00
var episodeNames = new List<string>
{
Parser.Parser.CleanupEpisodeTitle(sortedEpisodes.First().Title)
2013-03-06 22:20:34 +00:00
};
2013-03-06 22:20:34 +00:00
var result = String.Empty;
2013-04-25 04:27:49 +00:00
if (nameSpec.IncludeSeriesTitle)
{
2013-03-06 22:20:34 +00:00
result += series.Title + nameSpec.Separator;
}
2013-03-24 04:16:00 +00:00
if (series.SeriesType == SeriesTypes.Standard)
{
result += numberStyle.Pattern.Replace("%0e",
String.Format("{0:00}", sortedEpisodes.First().EpisodeNumber));
if (episodes.Count > 1)
{
var multiEpisodeStyle =
2013-03-06 22:20:34 +00:00
GetMultiEpisodeStyle(nameSpec.MultiEpisodeStyle);
foreach (var episode in sortedEpisodes.Skip(1))
{
if (multiEpisodeStyle.Name == "Duplicate")
{
2013-03-06 22:20:34 +00:00
result += nameSpec.Separator + numberStyle.Pattern;
}
else
{
result += multiEpisodeStyle.Pattern;
}
result = result.Replace("%0e", String.Format("{0:00}", episode.EpisodeNumber));
episodeNames.Add(Parser.Parser.CleanupEpisodeTitle(episode.Title));
}
}
result = result
.Replace("%s", String.Format("{0}", episodes.First().SeasonNumber))
.Replace("%0s", String.Format("{0:00}", episodes.First().SeasonNumber))
.Replace("%x", numberStyle.EpisodeSeparator)
2013-03-06 22:20:34 +00:00
.Replace("%p", nameSpec.Separator);
}
else
{
if (episodes.First().AirDate.HasValue)
result += episodes.First().AirDate.Value.ToString("yyyy-MM-dd");
else
result += "Unknown";
}
2013-03-06 21:35:39 +00:00
if (nameSpec.IncludeEpisodeTitle)
{
if (episodeNames.Distinct().Count() == 1)
2013-03-06 22:20:34 +00:00
result += nameSpec.Separator + episodeNames.First();
else
2013-03-06 22:20:34 +00:00
result += nameSpec.Separator + String.Join(" + ", episodeNames.Distinct());
}
2013-04-25 04:27:49 +00:00
if (nameSpec.IncludeQuality)
{
2013-04-11 05:08:55 +00:00
result += String.Format(" [{0}]", episodeFile.Quality.Quality);
2013-04-01 06:22:16 +00:00
if (episodeFile.Quality.Proper)
result += " [Proper]";
}
2013-03-06 21:35:39 +00:00
if (nameSpec.ReplaceSpaces)
result = result.Replace(' ', '.');
_logger.Trace("New File Name is: [{0}]", result.Trim());
return CleanFilename(result.Trim());
}
2013-03-06 22:20:34 +00:00
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
{
2013-04-25 04:27:49 +00:00
var nameSpec = _namingConfigService.GetConfig();
string path = series.Path;
if (series.SeasonFolder)
{
2013-03-06 21:35:39 +00:00
var seasonFolder = nameSpec.SeasonFolderFormat
.Replace("%0s", seasonNumber.ToString("00"))
.Replace("%s", seasonNumber.ToString());
path = Path.Combine(path, seasonFolder);
}
2013-03-06 22:20:34 +00:00
return Path.Combine(path, fileName + extension);
}
public static string CleanFilename(string name)
{
string result = name;
string[] badCharacters = { "\\", "/", "<", ">", "?", "*", ":", "|", "\"" };
string[] goodCharacters = { "+", "+", "{", "}", "!", "@", "-", "#", "`" };
for (int i = 0; i < badCharacters.Length; i++)
result = result.Replace(badCharacters[i], goodCharacters[i]);
return result.Trim();
}
2013-03-06 22:20:34 +00:00
private static readonly List<EpisodeSortingType> NumberStyles = new List<EpisodeSortingType>
{
new EpisodeSortingType
{
Id = 0,
Name = "1x05",
Pattern = "%sx%0e",
EpisodeSeparator = "x"
},
new EpisodeSortingType
{
Id = 1,
Name = "01x05",
Pattern = "%0sx%0e",
EpisodeSeparator = "x"
},
new EpisodeSortingType
{
Id = 2,
Name = "S01E05",
Pattern = "S%0sE%0e",
EpisodeSeparator = "E"
},
new EpisodeSortingType
{
Id = 3,
Name = "s01e05",
Pattern = "s%0se%0e",
EpisodeSeparator = "e"
}
};
private static readonly List<EpisodeSortingType> MultiEpisodeStyles = new List<EpisodeSortingType>
{
new EpisodeSortingType
{
Id = 0,
Name = "Extend",
Pattern = "-%0e"
},
new EpisodeSortingType
{
Id = 1,
Name = "Duplicate",
Pattern = "%p%0s%x%0e"
},
new EpisodeSortingType
{
Id = 2,
Name = "Repeat",
Pattern = "%x%0e"
},
new EpisodeSortingType
{
Id = 3,
Name = "Scene",
Pattern = "-%x%0e"
}
};
private static EpisodeSortingType GetNumberStyle(int id)
{
return NumberStyles.Single(s => s.Id == id);
}
private static EpisodeSortingType GetMultiEpisodeStyle(int id)
{
return MultiEpisodeStyles.Single(s => s.Id == id);
}
}
}