mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-05-13 13:38:31 -05:00
added plex plugin
This commit is contained in:
@@ -12,3 +12,4 @@ deploy/**
|
||||
*.ffplugin
|
||||
DiscordNodes/settings.json
|
||||
Gotify/settings.json
|
||||
Plex/settings.json
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gotify", "Gotify\Gotify.csp
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscordNodes", "DiscordNodes\DiscordNodes.csproj", "{27721E6D-1FBF-4950-824E-5231CACDE64C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plex", "Plex\Plex.csproj", "{4127A3CC-8BCE-41AE-BDE4-6FA75EF2D8A1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -63,6 +65,10 @@ Global
|
||||
{27721E6D-1FBF-4950-824E-5231CACDE64C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{27721E6D-1FBF-4950-824E-5231CACDE64C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{27721E6D-1FBF-4950-824E-5231CACDE64C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4127A3CC-8BCE-41AE-BDE4-6FA75EF2D8A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4127A3CC-8BCE-41AE-BDE4-6FA75EF2D8A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4127A3CC-8BCE-41AE-BDE4-6FA75EF2D8A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4127A3CC-8BCE-41AE-BDE4-6FA75EF2D8A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
namespace FileFlows.Plex;
|
||||
|
||||
internal static class ExtensionMethods
|
||||
{
|
||||
public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
global using System;
|
||||
global using System.Text;
|
||||
global using System.ComponentModel.DataAnnotations;
|
||||
global using FileFlows.Plugin;
|
||||
global using FileFlows.Plugin.Attributes;
|
||||
@@ -0,0 +1,119 @@
|
||||
using FileFlows.Plex.Models;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FileFlows.Plex.MediaManagement;
|
||||
|
||||
public class PlexUpdater: Node
|
||||
{
|
||||
public override int Inputs => 1;
|
||||
public override int Outputs => 2;
|
||||
public override FlowElementType Type => FlowElementType.Process;
|
||||
public override string Icon => "fas fa-paper-plane";
|
||||
|
||||
public override int Execute(NodeParameters args)
|
||||
{
|
||||
var settings = args.GetPluginSettings<PluginSettings>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settings?.AccessToken))
|
||||
{
|
||||
args.Logger?.WLog("No access token set");
|
||||
return 2;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(settings?.ServerUrl))
|
||||
{
|
||||
args.Logger?.WLog("No server URL set");
|
||||
return 2;
|
||||
}
|
||||
|
||||
// get the path
|
||||
string path = args.WorkingFile;
|
||||
path = args.UnMapPath(path);
|
||||
if (args.IsDirectory == false)
|
||||
{
|
||||
bool windows = path.StartsWith("\\") || Regex.IsMatch(path, @"^[a-zA-Z]:\\");
|
||||
string pathSeparator = windows ? "\\" : "/";
|
||||
path = path.Substring(0, path.LastIndexOf(pathSeparator));
|
||||
}
|
||||
|
||||
string url = settings.ServerUrl;
|
||||
if (url.EndsWith("/") == false)
|
||||
url += "/";
|
||||
url += "library/sections";
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
|
||||
var sectionsResponse= GetWebRequest(httpClient, url + "?X-Plex-Token=" + settings.AccessToken);
|
||||
if (sectionsResponse.success == false)
|
||||
{
|
||||
args.Logger?.WLog("Failed to retrieve sections" + (string.IsNullOrWhiteSpace(sectionsResponse.body) ? "" : ": " + sectionsResponse.body));
|
||||
return 2;
|
||||
}
|
||||
|
||||
PlexSections sections;
|
||||
try
|
||||
{
|
||||
var options = new System.Text.Json.JsonSerializerOptions();
|
||||
options.PropertyNameCaseInsensitive = true;
|
||||
sections = System.Text.Json.JsonSerializer.Deserialize<PlexSections>(sectionsResponse.body, options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.Logger?.ELog("Failed deserializing sections json: " + ex.Message);
|
||||
return 2;
|
||||
}
|
||||
string pathLower = path.ToLower();
|
||||
var section = sections?.MediaContainer?.Directory?.Where(x => {
|
||||
if (x.Location?.Any() != true)
|
||||
return false;
|
||||
foreach (var loc in x.Location) {
|
||||
if (loc.Path == null)
|
||||
continue;
|
||||
if (pathLower.StartsWith(loc.Path.ToLower()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).FirstOrDefault();
|
||||
if(section == null)
|
||||
{
|
||||
args.Logger?.WLog("Failed to find Plex section for path: " + path);
|
||||
return 2;
|
||||
}
|
||||
|
||||
url += $"/{section.Key}/refresh?path={Uri.EscapeDataString(path)}&X-Plex-Token=" + settings.AccessToken;
|
||||
|
||||
var updateResponse = GetWebRequest(httpClient, url);
|
||||
if (updateResponse.success == false)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(updateResponse.body) == false)
|
||||
args.Logger.WLog("Failed to update Plex:" + updateResponse.body);
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private Func<HttpClient, string, (bool success, string body)> _GetWebRequest;
|
||||
internal Func<HttpClient, string, (bool success, string body)> GetWebRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_GetWebRequest == null)
|
||||
{
|
||||
_GetWebRequest = (HttpClient client, string url) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
var response = client.GetAsync(url).Result;
|
||||
string body = response.Content.ReadAsStringAsync().Result;
|
||||
return (response.IsSuccessStatusCode, body);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return (false, ex.Message);
|
||||
}
|
||||
};
|
||||
}
|
||||
return _GetWebRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace FileFlows.Plex.Models;
|
||||
|
||||
internal class PlexDirectory
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public PlexDirectoryLocation[] Location { get; set; }
|
||||
}
|
||||
|
||||
public class PlexDirectoryLocation
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Path { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace FileFlows.Plex.Models;
|
||||
|
||||
internal class PlexSections
|
||||
{
|
||||
public PlexSection MediaContainer { get; set; }
|
||||
}
|
||||
|
||||
internal class PlexSection
|
||||
{
|
||||
public int Size { get; set; }
|
||||
public PlexDirectory[] Directory { get; set; }
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"Plugins": {
|
||||
"Plex": {
|
||||
"Description": "A plugin that allows you to communicate with Plex.",
|
||||
"Fields": {
|
||||
"ServerUrl": "Server",
|
||||
"ServerUrl-Placeholder": "http://localhost:32400/",
|
||||
"ServerUrl-Help": "The URL of the Plex server",
|
||||
"AccessToken": "Access Token",
|
||||
"AccessToken-Help": "The access token used to communicate with the Plex server.\nhttps://github.com/revenz/Fenrus/wiki/Plex-Token"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Flow": {
|
||||
"Parts": {
|
||||
"PlexUpdater": {
|
||||
"Outputs": {
|
||||
"1": "Plex update request sent",
|
||||
"2": "Plex update request failed to send"
|
||||
},
|
||||
"Description": "Sends a message to a Plex server to update the library."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace FileFlows.Plex;
|
||||
|
||||
public class Plugin : FileFlows.Plugin.IPlugin
|
||||
{
|
||||
public string Name => "Plex";
|
||||
public string MinimumVersion => "0.5.2.690";
|
||||
|
||||
public void Init()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace FileFlows.Plex
|
||||
{
|
||||
using FileFlows.Plugin;
|
||||
using FileFlows.Plugin.Attributes;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class PluginSettings:IPluginSettings
|
||||
{
|
||||
[Text(1)]
|
||||
[Required]
|
||||
public string ServerUrl { get; set; }
|
||||
|
||||
[Text(2)]
|
||||
[Required]
|
||||
public string AccessToken { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#if(DEBUG)
|
||||
|
||||
using FileFlows.Plex.Media;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace FileFlows.Plex.Tests;
|
||||
|
||||
[TestClass]
|
||||
public class PlexTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void Plex_Basic()
|
||||
{
|
||||
var args = new NodeParameters(@"/media/movies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty);
|
||||
args.GetPluginSettingsJson = (string input) =>
|
||||
{
|
||||
return File.ReadAllText("../../../settings.json");
|
||||
};
|
||||
|
||||
var node = new PlexUpdater();
|
||||
Assert.AreEqual(1, node.Execute(args));
|
||||
}
|
||||
[TestMethod]
|
||||
public void Plex_Fail()
|
||||
{
|
||||
var args = new NodeParameters(@"/media/unknownmovies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty);
|
||||
args.GetPluginSettingsJson = (string input) =>
|
||||
{
|
||||
return File.ReadAllText("../../../settings.json");
|
||||
};
|
||||
|
||||
var node = new PlexUpdater();
|
||||
Assert.AreEqual(2, node.Execute(args));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
#if(DEBUG)
|
||||
|
||||
namespace FileFlows.Plex.Tests;
|
||||
|
||||
using FileFlows.Plugin;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
internal class TestLogger : ILogger
|
||||
{
|
||||
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;
|
||||
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)));
|
||||
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 override string ToString()
|
||||
{
|
||||
return String.Join(Environment.NewLine, this.Messages.ToArray());
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -76,4 +76,10 @@ webhooks
|
||||
[webhooktoken]
|
||||
Gotify
|
||||
gotify
|
||||
lan
|
||||
lan
|
||||
Plex
|
||||
Jellyfin
|
||||
Emby
|
||||
revenz
|
||||
Fenrus
|
||||
Plex-Token
|
||||
|
||||
Reference in New Issue
Block a user