FF-1659: Improved tv show lookups

This commit is contained in:
John Andrews
2024-06-28 10:39:35 +12:00
parent 14466591da
commit 8472be2c4b
55 changed files with 825 additions and 238 deletions
-1
View File
@@ -8,7 +8,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Apprise</Product>
-3
View File
@@ -128,10 +128,7 @@ File shrunk in size by: {{ difference | file_size }} / {{ percent }}%
type = this.MessageType?.EmptyAsNull() ?? "info"
};
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
var json = JsonSerializer.Serialize(data);
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
var content = new StringContent(json, Encoding.UTF8, "application/json");
using var httpClient = new HttpClient();
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -7,7 +7,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Audio</Product>
-2
View File
@@ -25,13 +25,11 @@ namespace FileFlows.AudioNodes.Tests
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -6,7 +6,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Basic</Product>
+3
View File
@@ -7,6 +7,9 @@ using FileHelper = FileFlows.Plugin.Helpers.FileHelper;
namespace BasicNodes.Tests;
/// <summary>
/// Local file service
/// </summary>
public class LocalFileService : IFileService
{
/// <summary>
-2
View File
@@ -57,13 +57,11 @@ public class TestLogger : ILogger
/// <param name="args">the arguments of the message</param>
private void Log(LogType type, params object[] args)
{
#pragma warning disable IL2026
string message = type + " -> " + string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive ? x.ToString() :
x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Writer?.Invoke(message);
Messages.Add(message);
}
-1
View File
@@ -7,7 +7,6 @@
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<FileVersion>1.0.4.189</FileVersion>
<ProductVersion>1.0.4.189</ProductVersion>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Checksum</Product>
-2
View File
@@ -23,13 +23,11 @@ namespace ChecksumNodes.Tests
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
public bool Contains(string message)
-1
View File
@@ -7,7 +7,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Comic</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -8,7 +8,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Discord</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-2
View File
@@ -6,11 +6,9 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Email</Product>
-2
View File
@@ -24,13 +24,11 @@ namespace EmailNodes.Tests
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -8,7 +8,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Emby</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
Binary file not shown.
Binary file not shown.
-1
View File
@@ -7,7 +7,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Gotify</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -7,7 +7,6 @@
<FileVersion>1.0.4.189</FileVersion>
<ProductVersion>1.0.4.189</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Image</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
+31 -9
View File
@@ -1,11 +1,33 @@
namespace MetaNodes
namespace MetaNodes;
/// <summary>
/// The Global variables used by this plugin
/// </summary>
internal class Globals
{
internal class Globals
{
public static string MOVIE_INFO = "MovieInfo";
public static string MOVIE = "Movie";
public static string MOVIE_CREDITS = "MovieCredits";
public static string TV_SHOW_INFO = "TVShowInfo";
public static string TV_EPISODE_INFO = "TVEpisodeInfo";
}
/// <summary>
/// The name fo the Movie Info variable
/// </summary>
public static string MOVIE_INFO = "MovieInfo";
/// <summary>
/// The name fo the Movie variable
/// </summary>
public static string MOVIE = "Movie";
/// <summary>
/// The name fo the Movie Credits variable
/// </summary>
public static string MOVIE_CREDITS = "MovieCredits";
/// <summary>
/// The name fo the TV Show Info variable
/// </summary>
public static string TV_SHOW_INFO = "TVShowInfo";
/// <summary>
/// The name fo the TV Episode Info variable
/// </summary>
public static string TV_EPISODE_INFO = "TVEpisodeInfo";
/// <summary>
/// The token used to query the MovieDB
/// </summary>
internal const string MovieDbBearerToken = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxZjVlNTAyNmJkMDM4YmZjZmU2MjI2MWU2ZGEwNjM0ZiIsInN1YiI6IjRiYzg4OTJjMDE3YTNjMGY5MjAwMDIyZCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.yMwyT8DEK1rF1gQMKJ-ZSy-dUGxFs5T345XwBLrvrWE";
}
-1
View File
@@ -8,7 +8,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Meta</Product>
+81 -51
View File
@@ -1,60 +1,90 @@
#if(DEBUG)
namespace MetaNodes.Tests
using FileFlows.Plugin;
namespace MetaNodes.Tests;
/// <summary>
/// A logger for tests that stores the logs in memory
/// </summary>
public class TestLogger : ILogger
{
using FileFlows.Plugin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
private readonly List<string> Messages = new();
internal class TestLogger : ILogger
/// <summary>
/// Writes an information log message
/// </summary>
/// <param name="args">the log parameters</param>
public void ILog(params object[] args)
=> Log(LogType.Info, args);
/// <summary>
/// Writes an debug log message
/// </summary>
/// <param name="args">the log parameters</param>
public void DLog(params object[] args)
=> Log(LogType.Debug, args);
/// <summary>
/// Writes an warning log message
/// </summary>
/// <param name="args">the log parameters</param>
public void WLog(params object[] args)
=> Log(LogType.Warning, args);
/// <summary>
/// Writes an error log message
/// </summary>
/// <param name="args">the log parameters</param>
public void ELog(params object[] args)
=> Log(LogType.Error, args);
/// <summary>
/// Gets the tail of the log
/// </summary>
/// <param name="length">the number of messages to get</param>
public string GetTail(int length = 50)
{
private List<string> Messages = new List<string>();
public void DLog(params object[] args) => Log("DBUG", args);
public void ELog(params object[] args) => Log("ERRR", args);
public void ILog(params object[] args) => Log("INFO", args);
public void WLog(params object[] args) => Log("WARN", args);
private void Log(string type, object[] args)
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
public bool Contains(string message)
{
if (string.IsNullOrWhiteSpace(message))
return false;
string log = string.Join(Environment.NewLine, Messages);
return log.Contains(message);
}
public string GetTail(int length = 50)
{
if(length <= 0)
length = 50;
if (Messages.Count <= length)
return string.Join(Environment.NewLine, Messages);
return string.Join(Environment.NewLine, Messages.TakeLast(length));
}
public override string ToString()
=> string.Join(Environment.NewLine, Messages);
if (Messages.Count <= length)
return string.Join(Environment.NewLine, Messages);
return string.Join(Environment.NewLine, Messages.TakeLast(50));
}
/// <summary>
/// Logs a message
/// </summary>
/// <param name="type">the type of log to record</param>
/// <param name="args">the arguments of the message</param>
private void Log(LogType type, params object[] args)
{
string message = type + " -> " + string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive ? x.ToString() :
x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
Writer?.Invoke(message);
Messages.Add(message);
}
/// <summary>
/// Gets or sets an optional writer
/// </summary>
public Action<string> Writer { get; set; }
/// <summary>
/// Returns the entire log as a string
/// </summary>
/// <returns>the entire log</returns>
public override string ToString()
=> string.Join(Environment.NewLine, Messages);
/// <summary>
/// Checks if the log contains the text
/// </summary>
/// <param name="text">the text to check for</param>
/// <returns>true if it contains it, otherwise false</returns>
public bool Contains(string text)
=> Messages.Any(x => x.Contains(text));
}
#endif
+17 -21
View File
@@ -1,5 +1,6 @@
#if(DEBUG)
using System.Diagnostics.CodeAnalysis;
using DM.MovieApi;
using DM.MovieApi.MovieDb.Movies;
using FileFlows.Plugin;
@@ -9,12 +10,12 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MetaNodes.Tests.TheMovieDb;
[TestClass]
public class MovieLookupTests
public class MovieLookupTests : TestBase
{
[TestMethod]
public void MovieLookupTests_File_Ghostbusters()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/Ghostbusters 1984.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Ghostbusters 1984.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -33,7 +34,7 @@ public class MovieLookupTests
[TestMethod]
public void MovieLookupTests_File_Ghostbusters2()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/Ghostbusters 2.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Ghostbusters 2.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -52,7 +53,7 @@ public class MovieLookupTests
[TestMethod]
public void MovieLookupTests_File_WithDots()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/Back.To.The.Future.2.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Back.To.The.Future.2.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -71,7 +72,7 @@ public class MovieLookupTests
[TestMethod]
public void MovieLookupTests_File_WithYear()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/Back.To.The.Future.1989.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Back.To.The.Future.1989.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -83,14 +84,14 @@ public class MovieLookupTests
var mi = args.Parameters[Globals.MOVIE_INFO] as MovieInfo;
Assert.IsNotNull(mi);
Assert.AreEqual("Back to the Future Part II", mi.Title);
Assert.AreEqual("Back to the Future Part II", mi.Title.Replace("(", "").Replace(")", ""));
Assert.AreEqual(1989, mi.ReleaseDate.Year);
}
[TestMethod]
public void MovieLookupTests_Folder_WithYear()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/Back To The Future (1989)/Jaws.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Back To The Future (1989)/Jaws.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = true;
@@ -109,7 +110,7 @@ public class MovieLookupTests
[TestMethod]
public void MovieLookupTests_VariablesSet()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/Back To The Future (1989)/Jaws.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Back To The Future (1989)/Jaws.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = true;
@@ -125,7 +126,7 @@ public class MovieLookupTests
[TestMethod]
public void MovieLookupTests_NoMatchNoVariables()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/sdfsdfdsvfdcxdsf.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("sdfsdfdsvfdcxdsf.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -139,10 +140,8 @@ public class MovieLookupTests
[TestMethod]
public void MovieLookupTests_ComplexFile()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters(@"/test/Constantine.2005.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}/Constantine.2005.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}.mkv", logger, false, string.Empty, null);
string log = logger.ToString();
{
var args = GetNodeParameters("Constantine.2005.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}/Constantine.2005.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -161,7 +160,7 @@ public class MovieLookupTests
[TestMethod]
public void MovieLookupTests_WonderWoman()
{
var args = new FileFlows.Plugin.NodeParameters(@"/test/Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}/Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}/Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -178,28 +177,25 @@ public class MovieLookupTests
}
[TestMethod]
[RequiresUnreferencedCode("")]
public void MovieLookupTests_File_TheBatman_Metadata()
{
MovieDbFactory.RegisterSettings(MovieLookup.MovieDbBearerToken);
MovieDbFactory.RegisterSettings(Globals.MovieDbBearerToken);
var movieApi = MovieDbFactory.Create<IApiMovieRequest>().Value;
var args = new FileFlows.Plugin.NodeParameters(@"/test/Ghostbusters 1984.mkv", new TestLogger(), false, string.Empty, null);;
var args = GetNodeParameters("Ghostbusters 1984.mkv");
var md = MovieLookup.GetVideoMetadata(args, movieApi, 414906, @"D:\videos\temp");
Assert.IsNotNull(md);
#pragma warning disable IL2026
string json = System.Text.Json.JsonSerializer.Serialize(md, new System.Text.Json.JsonSerializerOptions
{
WriteIndented = true
});
#pragma warning restore IL2026
File.WriteAllText(@"D:\videos\metadata.json", json);
}
[TestMethod]
public void MovieLookupTests_WonderWoman_Nfo()
{
var logger = new TestLogger();
var args = new NodeParameters(@"/test/Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}/Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}.mkv",
logger, false, string.Empty, null);;
var args = GetNodeParameters(@"Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}/Wonder.Woman.1984.2020.German.DL.AC3.1080p.BluRay.x265-Fun{{fdg$ERGESDG32fesdfgds}}.mkv");
MovieLookup ml = new MovieLookup();
ml.UseFolderName = false;
@@ -3,33 +3,20 @@
using System.Diagnostics.CodeAnalysis;
using DM.MovieApi.MovieDb.Movies;
using DM.MovieApi.MovieDb.TV;
using FileFlows.Plugin;
using MetaNodes.TheMovieDb;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MetaNodes.Tests.TheMovieDb;
[TestClass]
public class TVEpisodeLookupTests
public class TVEpisodeLookupTests : TestBase
{
/// <summary>
/// The test context instance
/// </summary>
private TestContext testContextInstance;
/// <summary>
/// Gets or sets the test context
/// </summary>
public TestContext TestContext
{
get { return testContextInstance; }
set { testContextInstance = value; }
}
[TestMethod]
public void TheBatman_s02e01()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 2/The Batman.s02e01.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 2/The Batman.s02e01.mkv");
var element = new TVEpisodeLookup();
@@ -46,8 +33,7 @@ public class TVEpisodeLookupTests
[TestMethod]
public void TheBatman_2x03()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 2/The Batman - 2x03.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 2/The Batman - 2x03.mkv");
var element = new TVEpisodeLookup();
@@ -64,13 +50,11 @@ public class TVEpisodeLookupTests
[TestMethod]
public void WithYear()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/Paradise PD (2018) - S04E04 - Good Jeans (1080p NF WEB-DL x265 t3nzin).mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("Paradise PD (2018) - S04E04 - Good Jeans (1080p NF WEB-DL x265 t3nzin).mkv");
var element = new TVEpisodeLookup();
var result = element.Execute(args);
TestContext.WriteLine(logger.ToString());
Assert.AreEqual(1, result);
@@ -86,8 +70,7 @@ public class TVEpisodeLookupTests
[TestMethod]
public void TheBatman_3x01_2()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 3/The Batman - 3x01-2.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 3/The Batman - 3x01-2.mkv");
var element = new TVEpisodeLookup();
@@ -105,8 +88,7 @@ public class TVEpisodeLookupTests
[TestMethod]
public void TheBatman_s4e12_13()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 4/The Batman - s4e12-13.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 4/The Batman - s4e12-13.mkv");
var element = new TVEpisodeLookup();
@@ -124,8 +106,7 @@ public class TVEpisodeLookupTests
[TestMethod]
public void TheBatman_s5e1_2_3()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 5/The Batman - s5e1-3.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 5/The Batman - s5e1-3.mkv");
var element = new TVEpisodeLookup();
@@ -145,8 +126,7 @@ public class TVEpisodeLookupTests
[TestMethod]
public void TheBatman_2x03_nfo()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 2/The Batman - 2x03.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 2/The Batman - 2x03.mkv");
var element = new TVEpisodeLookup();
@@ -166,8 +146,7 @@ public class TVEpisodeLookupTests
[TestMethod]
public void TheBatman_s5e1_2_3_Nfo()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 5/The Batman - s5e1-3.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 5/The Batman - s5e1-3.mkv");
var element = new TVEpisodeLookup();
@@ -184,7 +163,6 @@ public class TVEpisodeLookupTests
[TestMethod]
public void VariousTests()
{
var logger = new TestLogger();
foreach (var test in new TVLookupTestData[]
{
new("See",
@@ -204,7 +182,7 @@ public class TVEpisodeLookupTests
(string lookupName, string year) = TVShowLookup.GetLookupName(test.Path, true);
Assert.AreEqual(test.Show, lookupName);
//
// var args = new FileFlows.Plugin.NodeParameters(test.Path, logger, false, string.Empty, null);
// var args = GetNodeParameters(test.Path);
//
// var element = new TVEpisodeLookup();
//
+27 -11
View File
@@ -8,13 +8,12 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MetaNodes.Tests.TheMovieDb;
[TestClass]
public class TVShowLookupTests
public class TVShowLookupTests : TestBase
{
[TestMethod]
public void TheBatman_Filename()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 2/The Batman.s02e01.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 2/The Batman.s02e01.mkv");
var element = new TVShowLookup();
element.UseFolderName = false;
@@ -33,12 +32,32 @@ public class TVShowLookupTests
Assert.AreEqual(2004, args.Variables["tvshow.Year"]);
}
[TestMethod]
public void YearInFilename()
{
var args = GetNodeParameters("TestFolder/Eric.2024.S01.01.mkv");
var element = new TVShowLookup();
element.UseFolderName = false;
var result = element.Execute(args);
Assert.AreEqual(1, result);
Assert.IsTrue(args.Parameters.ContainsKey(Globals.TV_SHOW_INFO));
var info = args.Parameters[Globals.TV_SHOW_INFO] as TVShowInfo;
Assert.IsNotNull(info);
Assert.AreEqual("Eric", info.Name);
Assert.AreEqual(2024, info.FirstAirDate.Year);
Assert.AreEqual("en", info.OriginalLanguage);
Assert.AreEqual("Eric", args.Variables["tvshow.Title"]);
Assert.AreEqual(2024, args.Variables["tvshow.Year"]);
}
[TestMethod]
public void TvdbID_Test()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/media/TV/The Walking Dead (2010) [tvdbid-153021]/Season 07/S07E03 - The Cell [HDTV-1080p][AAC 5.1][h265].mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Walking Dead (2010) [tvdbid-153021]/Season 07/S07E03 - The Cell [HDTV-1080p][AAC 5.1][h265].mkv");
var element = new TVShowLookup();
element.UseFolderName = true;
@@ -60,8 +79,7 @@ public class TVShowLookupTests
[TestMethod]
public void TheBatman_Folder()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/The Batman/Season 2/The Batman.s02e01.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("The Batman/Season 2/The Batman.s02e01.mkv");
var element = new TVShowLookup();
element.UseFolderName = true;
@@ -83,8 +101,7 @@ public class TVShowLookupTests
[TestMethod]
public void SquidGame_Filename()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/Squid Game/Season 1/Squid.Game.1x01-02.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("Squid Game/Season 1/Squid.Game.1x01-02.mkv");
var element = new TVShowLookup();
element.UseFolderName = false;
@@ -106,8 +123,7 @@ public class TVShowLookupTests
[TestMethod]
public void SquidGame_Folder()
{
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters("/test/tv/Squid Game/Season 1/Squid.Game.1x01-02.mkv", logger, false, string.Empty, null);
var args = GetNodeParameters("Squid Game/Season 1/Squid.Game.1x01-02.mkv");
var element = new TVShowLookup();
element.UseFolderName = true;
+473
View File
@@ -0,0 +1,473 @@
#if(DEBUG)
using FileFlows.Plugin;
using FileFlows.Plugin.Models;
using FileFlows.Plugin.Services;
using System.IO;
using FileHelper = FileFlows.Plugin.Helpers.FileHelper;
namespace MetaNodes.Tests;
/// <summary>
/// Local file service
/// </summary>
public class LocalFileService : IFileService
{
/// <summary>
/// Gets or sets the path separator for the file system
/// </summary>
public char PathSeparator { get; init; } = Path.DirectorySeparatorChar;
/// <summary>
/// Gets or sets the allowed paths the file service can access
/// </summary>
public string[] AllowedPaths { get; init; }
/// <summary>
/// Gets or sets a function for replacing variables in a string.
/// </summary>
/// <remarks>
/// The function takes a string input, a boolean indicating whether to strip missing variables,
/// and a boolean indicating whether to clean special characters.
/// </remarks>
public ReplaceVariablesDelegate ReplaceVariables { get; set; }
/// <summary>
/// Gets or sets the permissions to use for files
/// </summary>
public int? Permissions { get; set; }
/// <summary>
/// Gets or sets the owner:group to use for files
/// </summary>
public string OwnerGroup { get; set; }
/// <summary>
/// Gets or sets the logger used for logging
/// </summary>
public ILogger? Logger { get; set; }
public Result<string[]> GetFiles(string path, string searchPattern = "", bool recursive = false)
{
if (IsProtectedPath(ref path))
return Result<string[]>.Fail("Cannot access protected path: " + path);
try
{
return Directory.GetFiles(path, searchPattern ?? string.Empty,
recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
}
catch (Exception)
{
return new string[] { };
}
}
public Result<string[]> GetDirectories(string path)
{
if (IsProtectedPath(ref path))
return Result<string[]>.Fail("Cannot access protected path: " + path);
try
{
return Directory.GetDirectories(path);
}
catch (Exception)
{
return new string[] { };
}
}
public Result<bool> DirectoryExists(string path)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
return Directory.Exists(path);
}
catch (Exception)
{
return false;
}
}
public Result<bool> DirectoryDelete(string path, bool recursive = false)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
Directory.Delete(path, recursive);
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail(ex.Message);
}
}
public Result<bool> DirectoryMove(string path, string destination)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
if (IsProtectedPath(ref destination))
return Result<bool>.Fail("Cannot access protected path: " + destination);
try
{
Directory.Move(path, destination);
SetPermissions(destination);
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail(ex.Message);
}
}
public Result<bool> DirectoryCreate(string path)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
var dirInfo = new DirectoryInfo(path);
if (dirInfo.Exists == false)
dirInfo.Create();
SetPermissions(path);
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail(ex.Message);
}
}
public Result<bool> FileExists(string path)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
return System.IO.File.Exists(path);
}
catch (Exception)
{
return false;
}
}
public Result<FileInformation> FileInfo(string path)
{
if (IsProtectedPath(ref path))
return Result<FileInformation>.Fail("Cannot access protected path: " + path);
try
{
FileInfo fileInfo = new FileInfo(path);
return new FileInformation
{
CreationTime = fileInfo.CreationTime,
CreationTimeUtc = fileInfo.CreationTimeUtc,
LastWriteTime = fileInfo.LastWriteTime,
LastWriteTimeUtc = fileInfo.LastWriteTimeUtc,
Extension = fileInfo.Extension.TrimStart('.'),
Name = fileInfo.Name,
FullName = fileInfo.FullName,
Length = fileInfo.Length,
Directory = fileInfo.DirectoryName
};
}
catch (Exception ex)
{
return Result<FileInformation>.Fail(ex.Message);
}
}
public Result<bool> FileDelete(string path)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
var fileInfo = new FileInfo(path);
if(fileInfo.Exists)
fileInfo.Delete();
return true;
}
catch (Exception)
{
return false;
}
}
public Result<long> FileSize(string path)
{
if (IsProtectedPath(ref path))
return Result<long>.Fail("Cannot access protected path: " + path);
try
{
var fileInfo = new FileInfo(path);
if (fileInfo.Exists == false)
return Result<long>.Fail("File does not exist");
return fileInfo.Length;
}
catch (Exception ex)
{
return Result<long>.Fail(ex.Message);
}
}
public Result<DateTime> FileCreationTimeUtc(string path)
{
if (IsProtectedPath(ref path))
return Result<DateTime>.Fail("Cannot access protected path: " + path);
try
{
var fileInfo = new FileInfo(path);
if (fileInfo.Exists == false)
return Result<DateTime>.Fail("File does not exist");
return fileInfo.CreationTimeUtc;
}
catch (Exception ex)
{
return Result<DateTime>.Fail(ex.Message);
}
}
public Result<DateTime> FileLastWriteTimeUtc(string path)
{
if (IsProtectedPath(ref path))
return Result<DateTime>.Fail("Cannot access protected path: " + path);
try
{
var fileInfo = new FileInfo(path);
if (fileInfo.Exists == false)
return Result<DateTime>.Fail("File does not exist");
return fileInfo.LastWriteTimeUtc;
}
catch (Exception ex)
{
return Result<DateTime>.Fail(ex.Message);
}
}
public Result<bool> FileMove(string path, string destination, bool overwrite = true)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
if (IsProtectedPath(ref destination))
return Result<bool>.Fail("Cannot access protected path: " + destination);
try
{
var fileInfo = new FileInfo(path);
if (fileInfo.Exists == false)
return Result<bool>.Fail("File does not exist");
var destDir = new FileInfo(destination).Directory;
if (destDir.Exists == false)
{
destDir.Create();
SetPermissions(destDir.FullName);
}
fileInfo.MoveTo(destination, overwrite);
SetPermissions(destination);
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail(ex.Message);
}
}
public Result<bool> FileCopy(string path, string destination, bool overwrite = true)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
if (IsProtectedPath(ref destination))
return Result<bool>.Fail("Cannot access protected path: " + destination);
try
{
var fileInfo = new FileInfo(path);
if (fileInfo.Exists == false)
return Result<bool>.Fail("File does not exist");
var destDir = new FileInfo(destination).Directory;
if (destDir.Exists == false)
{
destDir.Create();
SetPermissions(destDir.FullName);
}
fileInfo.CopyTo(destination, overwrite);
SetPermissions(destination);
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail(ex.Message);
}
}
public Result<bool> FileAppendAllText(string path, string text)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
System.IO.File.AppendAllText(path, text);
SetPermissions(path);
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail(ex.Message);
}
}
public bool FileIsLocal(string path) => true;
/// <summary>
/// Gets the local path
/// </summary>
/// <param name="path">the path</param>
/// <returns>the local path to the file</returns>
public Result<string> GetLocalPath(string path)
=> Result<string>.Success(path);
public Result<bool> Touch(string path)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
if (DirectoryExists(path).Is(true))
{
try
{
Directory.SetLastWriteTimeUtc(path, DateTime.UtcNow);
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail("Failed to touch directory: " + ex.Message);
}
}
try
{
if (System.IO.File.Exists(path))
System.IO.File.SetLastWriteTimeUtc(path, DateTime.UtcNow);
else
{
System.IO.File.Create(path);
SetPermissions(path);
}
return true;
}
catch (Exception ex)
{
return Result<bool>.Fail($"Failed to touch file: '{path}' => {ex.Message}");
}
}
public Result<long> DirectorySize(string path)
{
throw new NotImplementedException();
}
public Result<bool> SetCreationTimeUtc(string path, DateTime date)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
if (!System.IO.File.Exists(path))
return Result<bool>.Fail("File not found.");
System.IO.File.SetCreationTimeUtc(path, date);
return Result<bool>.Success(true);
}
catch (Exception ex)
{
return Result<bool>.Fail($"Error setting creation time: {ex.Message}");
}
}
public Result<bool> SetLastWriteTimeUtc(string path, DateTime date)
{
if (IsProtectedPath(ref path))
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
if (!System.IO.File.Exists(path))
return Result<bool>.Fail("File not found.");
System.IO.File.SetLastWriteTimeUtc(path, date);
return Result<bool>.Success(true);
}
catch (Exception ex)
{
return Result<bool>.Fail($"Error setting last write time: {ex.Message}");
}
}
/// <summary>
/// Checks if a path is accessible by the file server
/// </summary>
/// <param name="path">the path to check</param>
/// <returns>true if accessible, otherwise false</returns>
private bool IsProtectedPath(ref string path)
{
if (OperatingSystem.IsWindows())
path = path.Replace("/", "\\");
else
path = path.Replace("\\", "/");
if(ReplaceVariables != null)
path = ReplaceVariables(path, true);
if (FileHelper.IsSystemDirectory(path))
return true; // a system directory, no access
if (AllowedPaths?.Any() != true)
return false; // no allowed paths configured, allow all
if (OperatingSystem.IsWindows())
path = path.ToLowerInvariant();
for(int i=0;i<AllowedPaths.Length;i++)
{
string p = OperatingSystem.IsWindows() ? AllowedPaths[i].ToLowerInvariant().TrimEnd('\\') : AllowedPaths[i].TrimEnd('/');
if (path.StartsWith(p))
return false;
}
return true;
}
public void SetPermissions(string path, int? permissions = null, Action<string> logMethod = null)
{
logMethod ??= (string message) => Logger?.ILog(message);
permissions = permissions != null && permissions > 0 ? permissions : Permissions;
if (permissions == null || permissions < 1)
permissions = 777;
if ((System.IO.File.Exists(path) == false && Directory.Exists(path) == false))
{
logMethod("SetPermissions: File doesnt existing, skipping");
return;
}
//StringLogger stringLogger = new StringLogger();
var logger = new TestLogger();
bool isFile = new FileInfo(path).Exists;
FileHelper.SetPermissions(logger, path, file: isFile, permissions: permissions);
FileHelper.ChangeOwner(logger, path, file: isFile, ownerGroup: OwnerGroup);
logMethod(logger.ToString());
}
}
#endif
+78
View File
@@ -0,0 +1,78 @@
#if(DEBUG)
using System.Runtime.InteropServices;
using FileFlows.Plugin;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MetaNodes.Tests;
/// <summary>
/// Base class for the tests
/// </summary>
[TestClass]
public abstract class TestBase
{
/// <summary>
/// The test context instance
/// </summary>
private TestContext testContextInstance;
internal TestLogger Logger = new();
/// <summary>
/// Gets or sets the test context
/// </summary>
public TestContext TestContext
{
get => testContextInstance;
set => testContextInstance = value;
}
public string TestPath { get; private set; }
public string TempPath { get; private set; }
public readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
[TestInitialize]
public void TestInitialize()
{
Logger.Writer = (msg) => TestContext.WriteLine(msg);
this.TestPath = this.TestPath?.EmptyAsNull() ?? (IsLinux ? "~/src/ff-files/test-files/videos" : @"d:\videos\testfiles");
this.TempPath = this.TempPath?.EmptyAsNull() ?? (IsLinux ? "~/src/ff-files/temp" : @"d:\videos\temp");
this.TestPath = this.TestPath.Replace("~/", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/");
this.TempPath = this.TempPath.Replace("~/", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/");
if (Directory.Exists(this.TempPath) == false)
Directory.CreateDirectory(this.TempPath);
}
[TestCleanup]
public void CleanUp()
{
TestContext.WriteLine(Logger.ToString());
}
protected virtual void TestStarting()
{
}
protected NodeParameters GetNodeParameters(string filename)
{
string tempPath = Path.GetTempPath();
string libPath = Path.Combine(tempPath, "media");
if (Directory.Exists(libPath) == false)
Directory.CreateDirectory(libPath);
return new(filename, Logger, false, libPath, new LocalFileService())
{
LibraryFileName = filename,
TempPath = tempPath
};
}
}
#endif
+13 -15
View File
@@ -1,9 +1,9 @@
using System.Text.RegularExpressions;
using DM.MovieApi;
using DM.MovieApi.ApiResponse;
using DM.MovieApi.MovieDb.Movies;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
using FileHelper = FileFlows.Plugin.Helpers.FileHelper;
namespace MetaNodes.TheMovieDb;
@@ -51,9 +51,6 @@ public class MovieLookup : Node
{ "movie.Year", 2005 }
};
}
internal const string MovieDbBearerToken = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxZjVlNTAyNmJkMDM4YmZjZmU2MjI2MWU2ZGEwNjM0ZiIsInN1YiI6IjRiYzg4OTJjMDE3YTNjMGY5MjAwMDIyZCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.yMwyT8DEK1rF1gQMKJ-ZSy-dUGxFs5T345XwBLrvrWE";
/// <summary>
/// Gets or sets if the folder name should be used
/// </summary>
@@ -67,8 +64,7 @@ public class MovieLookup : Node
/// <returns>the output to call next</returns>
public override int Execute(NodeParameters args)
{
var fileInfo = new FileInfo(args.FileName);
string lookupName = UseFolderName ? fileInfo.Directory.Name : fileInfo.Name.Substring(0, fileInfo.Name.LastIndexOf(fileInfo.Extension));
string lookupName = UseFolderName ? FileHelper.GetDirectoryName(args.LibraryFileName) : FileHelper.GetShortFileNameWithoutExtension(args.LibraryFileName);
lookupName = lookupName.Replace(".", " ").Replace("_", " ");
// look for year
@@ -77,25 +73,27 @@ public class MovieLookup : Node
if (match != null)
{
year = match.Value;
lookupName = lookupName.Substring(0, lookupName.IndexOf(year)).Trim();
lookupName = lookupName[..lookupName.IndexOf(year, StringComparison.Ordinal)].TrimEnd('(');
}
// remove double spaces in case they were added when removing the year
while (lookupName.IndexOf(" ", StringComparison.Ordinal) > 0)
lookupName = lookupName.Replace(" ", " ");
lookupName = lookupName.TrimEnd('(', '-');
args.Logger?.ILog("Lookup name: " + lookupName);
// RegisterSettings only needs to be called one time when your application starts-up.
MovieDbFactory.RegisterSettings(MovieDbBearerToken);
MovieDbFactory.RegisterSettings(Globals.MovieDbBearerToken);
var movieApi = MovieDbFactory.Create<IApiMovieRequest>().Value;
ApiSearchResponse<MovieInfo> response = movieApi.SearchByTitleAsync(lookupName).Result;
var response = movieApi.SearchByTitleAsync(lookupName).Result;
// try find an exact match
var result = response.Results.OrderBy(x =>
var results = response.Results.OrderBy(x =>
{
if (string.IsNullOrEmpty(year) == false)
{
@@ -128,7 +126,7 @@ public class MovieLookup : Node
13 => "xiii",
_ => string.Empty
};
string ln = lookupName.Substring(0, lookupName.LastIndexOf(number.ToString())).ToLower().Trim().Replace(" ", "");
string ln = lookupName[..lookupName.LastIndexOf(number.ToString(), StringComparison.Ordinal)].ToLower().Trim().Replace(" ", "");
string softTitle = x.Title.ToLower().Replace(" ", "").Trim();
if (softTitle == ln + roman)
return 0;
@@ -137,8 +135,9 @@ public class MovieLookup : Node
return 1;
})
.ThenBy(x => lookupName.ToLower().Trim().Replace(" ", "").StartsWith(x.Title.ToLower().Trim().Replace(" ", "")) ? 0 : 1)
.ThenBy(x => x.Title)
.FirstOrDefault();
// .ThenBy(x => x.Title)
.ToList();
var result = results.FirstOrDefault();
if (result == null)
return 2; // no match
@@ -165,7 +164,6 @@ public class MovieLookup : Node
args.UpdateVariables(Variables);
return 1;
}
@@ -205,7 +203,7 @@ public class MovieLookup : Node
}
catch (Exception)
{
// Ignored
}
}
+1
View File
@@ -126,6 +126,7 @@ public class NfoFileCreator : Node
if (args.FileService.FileMove(tempFile, output).Failed(out string error))
{
args.FailureReason = error;
args.Logger?.ELog(error);
return -1;
}
args.Logger.ILog("NFO File Created at: " + output);
+6 -8
View File
@@ -2,6 +2,8 @@
using DM.MovieApi.MovieDb.TV;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers;
using FileHelper = FileFlows.Plugin.Helpers.FileHelper;
namespace MetaNodes.TheMovieDb;
@@ -61,9 +63,7 @@ public class TVEpisodeLookup : Node
{ "tvepisode.Overview", "Joker makes Batman laugh" },
};
}
internal const string MovieDbBearerToken = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxZjVlNTAyNmJkMDM4YmZjZmU2MjI2MWU2ZGEwNjM0ZiIsInN1YiI6IjRiYzg4OTJjMDE3YTNjMGY5MjAwMDIyZCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.yMwyT8DEK1rF1gQMKJ-ZSy-dUGxFs5T345XwBLrvrWE";
/// <summary>
/// Executes the flow element
/// </summary>
@@ -71,11 +71,9 @@ public class TVEpisodeLookup : Node
/// <returns>the output to call next</returns>
public override int Execute(NodeParameters args)
{
string filename = args.FileName.Replace("\\", "/");
filename = filename[(filename.LastIndexOf("/", StringComparison.Ordinal) + 1)..];
filename = filename[..filename.LastIndexOf(".", StringComparison.Ordinal)];
string filename = FileHelper.GetShortFileNameWithoutExtension(args.LibraryFileName);
(string lookupName, string year) = TVShowLookup.GetLookupName(filename, UseFolderName);
(string lookupName, string year) = TVShowLookup.GetLookupName(args.LibraryFileName, UseFolderName);
(string showName, int? season, int? episode, int? lastEpisode, string year2) = TVShowLookup.GetTVShowInfo(filename);
@@ -94,7 +92,7 @@ public class TVEpisodeLookup : Node
args.Logger?.ILog($"Found show info from filename '{lookupName}' season '{season}' episode '{(episode + (lastEpisode == null ? "" : "-" + lastEpisode))}'");
// RegisterSettings only needs to be called one time when your application starts-up.
MovieDbFactory.RegisterSettings(MovieDbBearerToken);
MovieDbFactory.RegisterSettings(Globals.MovieDbBearerToken);
var movieApi = MovieDbFactory.Create<IApiTVShowRequest>().Value;
+84 -22
View File
@@ -5,6 +5,7 @@ using DM.MovieApi.MovieDb.Movies;
using DM.MovieApi.MovieDb.TV;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
using FileFlows.Plugin.Helpers;
namespace MetaNodes.TheMovieDb;
@@ -52,9 +53,7 @@ public class TVShowLookup : Node
{ "tvshow.Year", 2004 }
};
}
internal const string MovieDbBearerToken = "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxZjVlNTAyNmJkMDM4YmZjZmU2MjI2MWU2ZGEwNjM0ZiIsInN1YiI6IjRiYzg4OTJjMDE3YTNjMGY5MjAwMDIyZCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.yMwyT8DEK1rF1gQMKJ-ZSy-dUGxFs5T345XwBLrvrWE";
/// <summary>
/// Gets or sets if the folder name should be used
/// </summary>
@@ -68,10 +67,10 @@ public class TVShowLookup : Node
/// <returns>the output to call next</returns>
public override int Execute(NodeParameters args)
{
(string lookupName, string year) = GetLookupName(args.FileName, UseFolderName);
(string lookupName, string year) = GetLookupName(args.LibraryFileName, UseFolderName);
// RegisterSettings only needs to be called one time when your application starts-up.
MovieDbFactory.RegisterSettings(MovieDbBearerToken);
MovieDbFactory.RegisterSettings(Globals.MovieDbBearerToken);
var movieApi = MovieDbFactory.Create<IApiTVShowRequest>().Value;
@@ -81,18 +80,26 @@ public class TVShowLookup : Node
// try find an exact match
var result = response.Results.OrderBy(x =>
var results = response.Results.OrderByDescending(x =>
{
if (string.IsNullOrEmpty(year) == false)
{
return year == x.FirstAirDate.Year.ToString() ? 0 : 1;
if(year == x.FirstAirDate.Year.ToString())
return 2;
// sometimes the user may have hte date off by one, or the app may have
if(year == (x.FirstAirDate.Year - 1).ToString())
return 1;
if(year == (x.FirstAirDate.Year + 1).ToString())
return 1;
return 0;
}
return 0;
})
.ThenBy(x => x.Name.ToLower().Trim().Replace(" ", "") == lookupName.ToLower().Trim().Replace(" ", "") ? 0 : 1)
.ThenBy(x => lookupName.ToLower().Trim().Replace(" ", "").StartsWith(x.Name.ToLower().Trim().Replace(" ", "")) ? 0 : 1)
.ThenBy(x => x.Name)
.FirstOrDefault();
// .ThenBy(x => x.Name)
.ToList();
var result = results.FirstOrDefault();
if (result == null)
{
@@ -122,17 +129,16 @@ public class TVShowLookup : Node
internal static (string? LookupName, string? Year) GetLookupName(string filename, bool useFolderName)
{
var fileInfo = new FileInfo(filename);
string lookupName;
if (useFolderName)
{
lookupName = fileInfo.Directory.Name;
lookupName = FileHelper.GetDirectoryName(filename);
if (Regex.IsMatch(lookupName, "^(Season|Staffel|Saison|Specials)", RegexOptions.IgnoreCase))
lookupName = fileInfo.Directory.Parent.Name;
lookupName = FileHelper.GetDirectoryName(FileHelper.GetDirectory(filename));
}
else
{
lookupName = fileInfo.Name[..fileInfo.Name.LastIndexOf(fileInfo.Extension, StringComparison.Ordinal)];
lookupName = FileHelper.GetShortFileNameWithoutExtension(filename);
}
var result = GetTVShowInfo(lookupName);
@@ -189,16 +195,20 @@ public class TVShowLookup : Node
{
// Replace "1x02" format with "s1e02"
text = Regex.Replace(text, @"(?<season>\d+)x(?<episode>\d+)", "s${season}e${episode}", RegexOptions.IgnoreCase);
// Replace "s01.02" with "s01e02"
text = Regex.Replace(text, @"(?<season>s\d+)\.(?<episode>\d+)", "${season}e${episode}", RegexOptions.IgnoreCase);
string year = null;
var reYear = Regex.Match(text, @"\((19|20)[\d]{2}\)", RegexOptions.CultureInvariant);
if (reYear.Success)
{
year = reYear.Value;
text = text.Replace(year, string.Empty);
year = year[1..^1]; // remove the ()
}
// string year = null;
// var reYear = Regex.Match(text, @"\((19|20)[\d]{2}\)", RegexOptions.CultureInvariant);
// if (reYear.Success)
// {
// year = reYear.Value;
// text = text.Replace(year, string.Empty);
// year = year[1..^1]; // remove the ()
// }
(text, var year) = ExtractYearAndCleanText(text);
string pattern = @"^(?<showName>[\w\s.-]+)[. _-]?(?:(s|S)(?<season>\d+)(e|E)(?<episode>\d+)(?:-(?<lastEpisode>\d+))?)";
Match match = Regex.Match(text, pattern);
@@ -220,4 +230,56 @@ public class TVShowLookup : Node
return (show, season, episode, lastEpisode, year);
}
/// <summary>
/// Extracts the year from the given text if it matches specific patterns and removes it from the text.
/// </summary>
/// <param name="text">The input text containing a potential year.</param>
/// <returns>
/// A tuple containing the cleaned text and the extracted year.
/// The year is extracted only if it falls between 1950 and 5 years from the current year.
/// </returns>
static (string cleanedText, string? year) ExtractYearAndCleanText(string text)
{
string year = null;
int currentYear = DateTime.Now.Year;
int upperYearLimit = currentYear + 5;
var cleanedText = text;
// Match year in parentheses (e.g., (2024))
var reYear = Regex.Match(text, $@"(19[5-9]\d|20[0-{upperYearLimit % 100 / 10}]\d|{upperYearLimit})(?=[^\d]|$)", RegexOptions.CultureInvariant);
if (reYear.Success)
{
year = reYear.Value;
cleanedText = text.Replace(year, string.Empty);
}
else
{
// Match dot-separated year (e.g., .2024.)
var reYearAlt = Regex.Match(text, $@"\.(19[5-9]\d|20[0-{upperYearLimit % 100 / 10}]\d|{upperYearLimit})\.", RegexOptions.CultureInvariant);
if (reYearAlt.Success)
{
year = reYearAlt.Value.Trim('.');
cleanedText = text.Replace(reYearAlt.Value, string.Empty);
}
}
// Validate the extracted year
if (year != null)
{
int yearInt = int.Parse(year);
if (yearInt < 1950 || yearInt > upperYearLimit)
{
year = null;
cleanedText = text; // restore it
}
else
{
cleanedText = cleanedText.Replace(" ", " ").Replace("..", ".");
}
}
return (cleanedText, year);
}
}
-2
View File
@@ -74,9 +74,7 @@ public abstract class PlexNode:Node
{
var options = new System.Text.Json.JsonSerializerOptions();
options.PropertyNameCaseInsensitive = true;
#pragma warning disable IL2026
sections = System.Text.Json.JsonSerializer.Deserialize<PlexSections>(sectionsResponse.body, options)!;
#pragma warning restore IL2026
}
catch (Exception ex)
{
-1
View File
@@ -8,7 +8,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Plex</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-3
View File
@@ -117,15 +117,12 @@ File shrunk in size by: {{ difference | file_size }} / {{ percent }}%
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", settings.ApiToken);
// Create the request content
#pragma warning disable IL2026
var content = new StringContent(JsonSerializer.Serialize(
new {
type = "note",
title,
body
}), Encoding.UTF8, "application/json");
#pragma warning restore IL2026
var response = httpClient.PostAsync("https://api.pushbullet.com/v2/pushes", content).Result;
-1
View File
@@ -7,7 +7,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Pushbullet</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -7,7 +7,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Pushover</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -6,7 +6,6 @@
<FileVersion>1.0.4.189</FileVersion>
<ProductVersion>1.0.4.189</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Site Scraping</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
-1
View File
@@ -7,7 +7,6 @@
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Telegram</Product>
-2
View File
@@ -24,13 +24,11 @@ internal class TestLogger : ILogger
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
@@ -301,9 +301,7 @@ public class FfmpegBuilderAudioAddTrack : FfmpegBuilderNode
rgxLanguage = new Regex(language, RegexOptions.IgnoreCase);
}
catch (Exception) { }
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
var bestAudio = streams.Where(x => System.Text.Json.JsonSerializer.Serialize(x).ToLower().Contains("commentary") == false)
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
.OrderBy(x =>
{
if (language != string.Empty)
@@ -105,9 +105,7 @@ public class FfmpegBuilderAudioNormalization : FfmpegBuilderNode
json = json.Substring(0, json.IndexOf("}", StringComparison.Ordinal) + 1);
if (string.IsNullOrEmpty(json))
throw new Exception("Failed to parse TwoPass json");
#pragma warning disable IL2026
LoudNormStats? stats = JsonSerializer.Deserialize<LoudNormStats>(json);
#pragma warning restore IL2026
if (stats.input_i == "-inf" || stats.input_lra == "-inf" || stats.input_tp == "-inf" || stats.input_thresh == "-inf" || stats.target_offset == "-inf")
{
-2
View File
@@ -55,13 +55,11 @@ public class TestLogger : ILogger
/// <param name="args">the arguments of the message</param>
private void Log(LogType type, params object[] args)
{
#pragma warning disable IL2026
string message = type + " -> " + string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive ? x.ToString() :
x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Writer?.Invoke(message);
Messages.Add(message);
}
-2
View File
@@ -76,9 +76,7 @@ public abstract class TestBase
return;
string json = File.ReadAllText(filename);
#pragma warning disable IL2026
var settings = JsonSerializer.Deserialize<TestSettings>(json);
#pragma warning restore IL2026
this.TestPath = settings.TestPath;
this.TempPath = settings.TempPath;
this.FfmpegPath = settings.FfmpegPath;
-2
View File
@@ -34,10 +34,8 @@ internal class VideoMetadata
{
try
{
#pragma warning disable IL2026
string json = JsonSerializer.Serialize(source);
var result = JsonSerializer.Deserialize<VideoMetadata>(json);
#pragma warning restore IL2026
return result ?? new ();
}
catch (Exception)
-1
View File
@@ -6,7 +6,6 @@
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<FileVersion>1.1.1.528</FileVersion>
<ProductVersion>1.1.1.528</ProductVersion>
<PublishTrimmed>true</PublishTrimmed>
<Company>FileFlows</Company>
<Authors>John Andrews</Authors>
<Product>Video</Product>
-2
View File
@@ -233,10 +233,8 @@ namespace FileFlows.VideoNodes
// may be from non Legacy VideoNodes
try
{
#pragma warning disable IL2026
string json = JsonSerializer.Serialize(args.Parameters[VIDEO_INFO]);
var vi = JsonSerializer.Deserialize<VideoInfo>(json);
#pragma warning restore IL2026
if (vi == null)
throw new Exception("Failed to deserailize object");
return vi;