Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/episode groups #20

Merged
merged 12 commits into from
Dec 1, 2024
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,6 @@ MigrationBackup/

# Fody - auto-generated XML schema
FodyWeavers.xsd

# Ignore JetBrains config files
.idea/
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"args": ["/home/james/Desktop/test", "--extended-tagging"],
"cwd": "${workspaceFolder}/AutoTag.CLI",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"console": "externalTerminal",
"stopAtEntry": false
},
{
Expand Down
6 changes: 4 additions & 2 deletions AutoTag.CLI/AutoTag.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<PropertyGroup>
<AssemblyName>autotag</AssemblyName>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Version>3.5.5</Version>
<TargetFramework>net8.0</TargetFramework>
<Version>3.6.0</Version>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand All @@ -13,6 +13,8 @@
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<SelfContained>true</SelfContained>
<PublishTrimmed>true</PublishTrimmed>
<!-- needed to serialze config. see https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/#disabling-reflection-defaults -->
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
<TrimMode>partial</TrimMode>
</PropertyGroup>

Expand Down
10 changes: 10 additions & 0 deletions AutoTag.CLI/Options/TaggingOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public class TaggingOptions : OptionsBase<TaggingOptions>, IOptionsBase<TaggingO

[CommandLineOption<string>("--language", "-l", "Metadata language")]
public string? Language { get; set; }

[CommandLineOption<bool?>("--episode-group", "-g", "Manually choose the Episode Group for a TV episode. Enables manual mode.")]
public bool? EpisodeGroup { get; set; }

public static IEnumerable<Option> GetOptions()
{
Expand All @@ -34,6 +37,7 @@ public static IEnumerable<Option> GetOptions()
yield return GetOption(o => o.ExtendedTagging);
yield return GetOption(o => o.AppleTagging);
yield return GetOption(o => o.Language);
yield return GetOption(o => o.EpisodeGroup);
}

public static TaggingOptions GetBoundValues(BindingContext context) =>
Expand All @@ -47,6 +51,7 @@ public static TaggingOptions GetBoundValues(BindingContext context) =>
ExtendedTagging = GetValueForProperty(o => o.ExtendedTagging, context),
AppleTagging = GetValueForProperty(o => o.AppleTagging, context),
Language = GetValueForProperty(o => o.Language, context),
EpisodeGroup = GetValueForProperty(o => o.EpisodeGroup, context),
};

public void UpdateConfig(AutoTagConfig config)
Expand Down Expand Up @@ -90,5 +95,10 @@ public void UpdateConfig(AutoTagConfig config)
{
config.Language = Language;
}

if (EpisodeGroup.HasValue)
{
config.EpisodeGroup = EpisodeGroup.Value;
}
}
}
8 changes: 4 additions & 4 deletions AutoTag.Core/AutoTag.Core.csproj
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="TMDbLib" Version="2.0.0" />
<PackageReference Include="TMDbLib" Version="2.2.0" />
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion AutoTag.Core/AutoTagConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace AutoTag.Core;
public class AutoTagConfig
{
public enum Modes { TV, Movie };
public const int CurrentVer = 9;
public const int CurrentVer = 10;
public int ConfigVer { get; set; } = CurrentVer;
public Modes Mode { get; set; } = Modes.TV;
public bool ManualMode { get; set; } = false;
Expand All @@ -18,6 +18,7 @@ public enum Modes { TV, Movie };
public bool AppleTagging { get; set; } = false;
public bool RenameSubtitles { get; set; } = false;
public string Language { get; set; } = "en";
public bool EpisodeGroup { get; set; }
public IEnumerable<FileNameReplace> FileNameReplaces { get; set; } = Enumerable.Empty<FileNameReplace>();

public bool IsTVMode() => Mode == Modes.TV;
Expand Down
10 changes: 2 additions & 8 deletions AutoTag.Core/AutoTagSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ public AutoTagSettings(string configPath)
}
finally
{
if (Config == null)
{
Config = new AutoTagConfig();
}
Config ??= new AutoTagConfig();
}

if (Config.ConfigVer != AutoTagConfig.CurrentVer)
Expand Down Expand Up @@ -98,10 +95,7 @@ public AutoTagSettings(string configPath)
}
}

if (Config == null)
{
Config = new AutoTagConfig();
}
Config ??= new AutoTagConfig();
}

public void Save()
Expand Down
4 changes: 2 additions & 2 deletions AutoTag.Core/FileMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public virtual void WriteToFile(TagLib.File file, AutoTagConfig config, Action<s

public abstract string GetFileName(AutoTagConfig config);

protected readonly static Regex _renameRegex = new Regex(@"%(?<num>\d+)(?:\:(?<format>[0#]+))?");
protected static readonly Regex _renameRegex = new Regex(@"%(?<num>\d+)(?:\:(?<format>[0#]+))?");

protected static string FormatRenameNumber(Match match, int value)
{
Expand All @@ -55,5 +55,5 @@ protected static string FormatRenameNumber(Match match, int value)
}
}

public override abstract string ToString();
public abstract override string ToString();
}
6 changes: 3 additions & 3 deletions AutoTag.Core/FileWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace AutoTag.Core;
public class FileWriter : IDisposable
{
private readonly static HttpClient _client = new HttpClient();
private static readonly HttpClient _client = new HttpClient();
private static Dictionary<string, byte[]> _images = new Dictionary<string, byte[]>();
private readonly IMemoryCache _cache;

Expand Down Expand Up @@ -176,9 +176,9 @@ private static string GetFileName(string fileName, string oldFileName, Action<st

private static char[]? invalidFilenameChars { get; set; }

private readonly static char[] invalidNtfsChars = { '<', '>', ':', '"', '/', '\\', '|', '?', '*' };
private static readonly char[] invalidNtfsChars = { '<', '>', ':', '"', '/', '\\', '|', '?', '*' };

private readonly static string[] subtitleFileExtensions = { ".srt", ".vtt", ".sub", ".ssa" };
private static readonly string[] subtitleFileExtensions = { ".srt", ".vtt", ".sub", ".ssa" };

public void Dispose()
{
Expand Down
120 changes: 120 additions & 0 deletions AutoTag.Core/TV/ShowResults.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System.Text.RegularExpressions;
using TMDbLib.Objects.Search;
using TMDbLib.Objects.TvShows;

namespace AutoTag.Core.TV;

/// <summary>
/// Container class to hold episode as well as related episode group search results
/// </summary>
public partial class ShowResults
{
/* settings */
[GeneratedRegex(@"^\S+\s+(?<episode>\d+)$")]
private static partial Regex EpisodeRegex();

/* vars */
public SearchTv TvSearchResult { get; }
private TvGroupCollection? GroupCollection { get; set; }
public IReadOnlyDictionary<(int season, int episode), (int season, int episode)>? EpisodeGroupMappingTable => _episodeGroupMappingTable;
private Dictionary<(int season, int episode), (int season, int episode)>? _episodeGroupMappingTable;

/// <summary>
/// Create simple ShowResult container with <see cref="SearchTv"/> base
/// </summary>
/// <param name="tvSearchResult"></param>
public ShowResults(SearchTv tvSearchResult)
{
TvSearchResult = tvSearchResult;
}

public static implicit operator ShowResults(SearchTv tv) => new(tv);

/// <summary>
/// Generates <see cref="ShowResults"/> list from any <see cref="SearchTv"/> enumerable
/// </summary>
/// <param name="results">Result from tmdb client</param>
public static List<ShowResults> FromSearchResults(IEnumerable<SearchTv> results)
{
return results.Select(result => (ShowResults)result).ToList();
}


/// <summary>
/// Add optional episode group to tv result
/// </summary>
/// <param name="episodeGroup">Episode group fetched from tmdb api</param>
public bool AddEpisodeGroup(TvGroupCollection episodeGroup)
{
if (!TryGenerateMappingTable(episodeGroup, out var mappingTable)) return false;

_episodeGroupMappingTable = mappingTable;
GroupCollection = episodeGroup;
return true;
}

/// <summary>
/// Try to retrieve episode mapping for episode group order
/// </summary>
/// <param name="seasonNumber">Season number as defined in episode group</param>
/// <param name="episodeNumber">Episode number as defined in episode group</param>
/// <param name="numbering">Matching episode number and season of "standard" order</param>
/// <returns>True if mapping exists, false if not</returns>
public bool TryGetMapping(int seasonNumber, int episodeNumber, out (int season, int episode)? numbering)
{
numbering = null;
if (_episodeGroupMappingTable?.TryGetValue((seasonNumber, episodeNumber), out var result) ?? false)
{
numbering = result;
return true;
}

return false;
}

/// <summary>
/// Tries to generate mapping table between TMDB standard sorting of show
/// and the given Episode Group
/// </summary>
/// <param name="collection">Episode Group from TMDB</param>
/// <param name="parsedTable">Filled parsing table. Only filled when method returns true</param>
/// <returns>True if successful, false if not</returns>
private static bool TryGenerateMappingTable(TvGroupCollection collection,
out Dictionary<(int season, int episode), (int season, int episode)>? parsedTable)
{
parsedTable = new Dictionary<(int season, int episode), (int season, int episode)>();

foreach (var tvGroup in collection.Groups)
{
// determine season number
var sanitizedGroupName = tvGroup.Name.ToLower().Trim();
int? seasonNumber = null;

var seasonMatch = EpisodeRegex().Match(sanitizedGroupName);
if (seasonMatch.Success)
{
seasonNumber = int.Parse(seasonMatch.Groups["episode"].Value);
}
else if (sanitizedGroupName.StartsWith("special"))
{
seasonNumber = 0;
}

if (!seasonNumber.HasValue) return false;

// create mapping
foreach (var episode in tvGroup.Episodes)
{
var mappingIsUnique = parsedTable.TryAdd(
(seasonNumber.Value, episode.Order + 1), // order starts at 0, episodes at 1
(episode.SeasonNumber, episode.EpisodeNumber));
if (!mappingIsUnique)
{
return false;
}
}
}

return true;
}
}
Loading