-
-
Home
-
Settings
+
Loading...
+
+
+
Media Cleaner
+
+
+
+
+
+ | Name |
+ Actions |
+
+
+
+
+
+
+
+
+
+
+ | Name |
+ Seasons |
+ Actions |
+
+
+
+
-
Media Cleaner
-
-
-
- | ID |
- Series Name |
-
-
-
-
diff --git a/Jellyfin.Plugin.MediaCleaner/Pages/home.js b/Jellyfin.Plugin.MediaCleaner/Pages/home.js
new file mode 100644
index 0000000..ea248fd
--- /dev/null
+++ b/Jellyfin.Plugin.MediaCleaner/Pages/home.js
@@ -0,0 +1,151 @@
+document.addEventListener('pageshow', async () => {
+ await fetchHomepageCSS();
+ await updateMediaCleanerState();
+
+ var moviesTitle = document.getElementById("moviesTitle");
+ var seriesTitle = document.getElementById("seriesTitle");
+
+ moviesTitle.innerHTML = await getMediaCleanerMoviesTitle();
+ seriesTitle.innerHTML = await getMediaCleanerSeriesTitle();
+
+ await populateTables();
+ addClickHandlersToLinks();
+ finishLoading();
+});
+
+const getMediaCleanerSeriesInfo = async () => {
+ const response = await fetch("/mediacleaner/state/getSeriesInfo");
+
+ if(!response.ok){
+ throw new Error(`Response status: ${response.status}`)
+ }
+
+ return response.json();
+};
+
+const getMediaCleanerMovieInfo = async () => {
+ const response = await fetch("/mediacleaner/state/getMovieInfo");
+
+ if(!response.ok){
+ throw new Error(`Response status: ${response.status}`)
+ }
+
+ return response.json();
+};
+
+const updateMediaCleanerState = async () => {
+ const response = await fetch("/mediacleaner/state/updateState");
+
+ if(!response.ok){
+ throw new Error(`Response status: ${response.status}`)
+ }
+
+ return response.json();
+};
+
+const getMediaCleanerSeriesTitle = async () => {
+ const response = await fetch("/mediacleaner/state/getSeriesTitle");
+
+ if(!response.ok){
+ throw new Error(`Response status: ${response.status}`);
+ }
+
+ return response.json();
+};
+
+const getMediaCleanerMoviesTitle = async () => {
+ const response = await fetch("/mediacleaner/state/getMoviesTitle");
+
+ if(!response.ok){
+ throw new Error(`Response status: ${response.status}`);
+ }
+
+ return response.json();
+};
+
+const populateTables = async () => {
+ var moviesInfo = await getMediaCleanerMovieInfo();
+ var seriesInfo = await getMediaCleanerSeriesInfo();
+
+ var seriesTableBody = seriesTable.getElementsByTagName('tbody')[0];
+ seriesTableBody.replaceChildren();
+
+ var moviesTableBody = moviesTable.getElementsByTagName('tbody')[0];
+ moviesTableBody.replaceChildren();
+
+ if (moviesInfo.length > 0){
+ for(let i = 0; i < moviesInfo.length; i++){
+ var row = moviesTableBody.insertRow(-1);
+ var cell1 = row.insertCell(0);
+ var cell2 = row.insertCell(1);
+ cell1.innerHTML = moviesInfo[i].Name;
+ // Will need to be enabled once radarr and sonarr integration is enabled.
+ // Maybe change this to an element to remove hard coding.
+ cell2.innerHTML = "
";
+ }
+ }
+ else{
+ var columnCount = moviesTable.tHead.rows[0].cells.length;
+ var row = moviesTableBody.insertRow(-1);
+ var cell1 = row.insertCell(0);
+ cell1.colSpan = columnCount;
+ cell1.innerHTML = "No stale movies found.";
+ }
+
+ if(seriesInfo.length > 0){
+ for(let i = 0; i < seriesInfo.length; i++){
+ var row = seriesTableBody.insertRow(-1);
+ var cell1 = row.insertCell(0);
+ var cell2 = row.insertCell(1);
+ var cell3 = row.insertCell(2);
+ cell1.innerHTML = seriesInfo[i].Name;
+ cell2.innerHTML = seriesInfo[i].Seasons.map(season => season.replace("Season ", "")).join(", ");
+ // Will need to be enabled once radarr and sonarr integration is enabled.
+ // Maybe change this to an element to remove hard coding.
+ cell3.innerHTML = "
";
+ cell3.className = "actions-cell";
+ }
+ }
+ else{
+ var columnCount = seriesTable.tHead.rows[0].cells.length;
+ var row = seriesTableBody.insertRow(-1);
+ var cell1 = row.insertCell(0);
+ cell1.colSpan = columnCount;
+ cell1.innerHTML = "No stale series found.";
+ }
+};
+
+const addClickHandlersToLinks = () => {
+ const linkbtns = document.querySelectorAll("button.links")
+ linkbtns.forEach(btn => {
+ btn.addEventListener("click", () => {
+ const target = btn.dataset.target;
+ if (!target) return;
+ window.location.hash = target;
+ })
+ })
+}
+
+const finishLoading = () => {
+ const loadingElement = document.getElementById("loading");
+
+ const homepage = document.getElementById("homepage");
+ loadingElement.style.visibility = "hidden";
+ homepage.style.visibility = "visible";
+
+ console.log("Loading element: ", loadingElement);
+ console.log("Homepage element: ", homepage);
+}
+
+const fetchHomepageCSS = async () => {
+ const response = await fetch('/web/configurationpage?name=home.css')
+
+ if(!response.ok){
+ throw new Error(`Response status: ${response.status}`);
+ }
+
+ const css = await response.text();
+ const styles = document.createElement('style');
+ styles.textContent = css;
+ document.head.appendChild(styles);
+}
diff --git a/Jellyfin.Plugin.MediaCleaner/Pages/media_cleaner_table.js b/Jellyfin.Plugin.MediaCleaner/Pages/media_cleaner_table.js
deleted file mode 100644
index a56f7ba..0000000
--- a/Jellyfin.Plugin.MediaCleaner/Pages/media_cleaner_table.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var table = document.getElementById("seriesTable");
-
-
-
-const getMediaCleanerState = async () => {
- const response = await fetch('/mediacleaner/state');
-
- if(!response.ok){
- throw new Error(`Response status: ${response.status}`)
- }
-
- return response.json();
-}
-
-var state = await getMediaCleanerState();
-
-console.log("State: ", state);
-
-for(let i = 0; i < state.length; i++){
- var row = table.insertRow(-1);
- var cell1 = row.insertCell(0);
- var cell2 = row.insertCell(1);
- var cell3 = row.insertCell(2);
- cell1.innerHTML = state[i].Id;
- cell2.innerHTML = state[i].SeriesName;
- cell3.innerHTML = state[i].Seasons.length;
-}
diff --git a/Jellyfin.Plugin.MediaCleaner/Plugin.cs b/Jellyfin.Plugin.MediaCleaner/Plugin.cs
index 5e4271d..0c29674 100644
--- a/Jellyfin.Plugin.MediaCleaner/Plugin.cs
+++ b/Jellyfin.Plugin.MediaCleaner/Plugin.cs
@@ -57,8 +57,13 @@ public class Plugin : BasePlugin
, IHasWebPages
},
new PluginPageInfo
{
- Name = "media_cleaner_table.js",
- EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.media_cleaner_table.js", GetType().Namespace),
+ Name = "home.js",
+ EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.home.js", GetType().Namespace),
+ },
+ new PluginPageInfo
+ {
+ Name = "home.css",
+ EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.home.css", GetType().Namespace),
}
];
}
diff --git a/Jellyfin.Plugin.MediaCleaner/PluginServiceRegistrator.cs b/Jellyfin.Plugin.MediaCleaner/PluginServiceRegistrator.cs
index e5b2db9..079f689 100644
--- a/Jellyfin.Plugin.MediaCleaner/PluginServiceRegistrator.cs
+++ b/Jellyfin.Plugin.MediaCleaner/PluginServiceRegistrator.cs
@@ -8,6 +8,6 @@ public class PluginServiceRegistrator : IPluginServiceRegistrator
{
public void RegisterServices(IServiceCollection serviceCollection, IServerApplicationHost applicationHost)
{
- serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
}
}
diff --git a/Jellyfin.Plugin.MediaCleaner/ScheduledTasks/StaleMediaTask.cs b/Jellyfin.Plugin.MediaCleaner/StaleMediaScanner.cs
similarity index 62%
rename from Jellyfin.Plugin.MediaCleaner/ScheduledTasks/StaleMediaTask.cs
rename to Jellyfin.Plugin.MediaCleaner/StaleMediaScanner.cs
index 7c9f3d1..fde16f9 100644
--- a/Jellyfin.Plugin.MediaCleaner/ScheduledTasks/StaleMediaTask.cs
+++ b/Jellyfin.Plugin.MediaCleaner/StaleMediaScanner.cs
@@ -14,18 +14,20 @@ using Jellyfin.Database.Implementations.Entities;
using Jellyfin.Database.Implementations.Entities.Libraries;
using Jellyfin.Plugin.MediaCleaner.Configuration;
using Jellyfin.Plugin.MediaCleaner.Helpers;
+using Jellyfin.Plugin.MediaCleaner;
using Jellyfin.Plugin.MediaCleaner.Models;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Tasks;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
-namespace Jellyfin.Plugin.MediaCleaner.ScheduledTasks;
+namespace Jellyfin.Plugin.MediaCleaner;
///
/// A task to scan media for stale files.
///
-public sealed class StaleMediaTask : IScheduledTask
+public sealed class StaleMediaScanner
{
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
@@ -38,7 +40,7 @@ public sealed class StaleMediaTask : IScheduledTask
///
/// Logger for StaleMediaTask.
/// Accesses jellyfin's library manager for media.
- public StaleMediaTask(ILogger logger, ILibraryManager libraryManager)
+ public StaleMediaScanner(ILogger logger, ILibraryManager libraryManager)
{
_logger = logger;
_libraryManager = libraryManager;
@@ -47,18 +49,7 @@ public sealed class StaleMediaTask : IScheduledTask
_seriesHelper = new SeriesHelper(_logger);
}
- private static PluginConfiguration Configuration =>
- Plugin.Instance!.Configuration;
-
- string IScheduledTask.Name => "Scan Stale Media";
-
- string IScheduledTask.Key => "Stale Media";
-
- string IScheduledTask.Description => "Scan Stale Media";
-
- string IScheduledTask.Category => "Media";
-
- Task IScheduledTask.ExecuteAsync(IProgress progress, CancellationToken cancellationToken)
+ public async Task> ScanStaleMedia()
{
_loggingHelper.LogDebugInformation("--DEBUG MODE ACTIVE--");
_loggingHelper.LogInformation("-------------------------------------------------");
@@ -73,59 +64,92 @@ public sealed class StaleMediaTask : IScheduledTask
List allItems = [.. _libraryManager.GetItemsResult(query).Items];
- _loggingHelper.LogInformation("Total items: {ItemCount}", allItems.Count);
- _loggingHelper.LogInformation("Stale items found: {AllItems}", allItems);
+ _loggingHelper.LogInformation("Total items to scan: {ItemCount}", allItems.Count);
+ _loggingHelper.LogDebugInformation("Items found: {AllItems}", allItems);
List series = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Series)];
List movies = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Movie)];
- _loggingHelper.LogInformation("-------------------------------------------------");
- _loggingHelper.LogInformation("Starting scan of series items.");
- _loggingHelper.LogInformation("-------------------------------------------------");
+ _loggingHelper.LogDebugInformation("-------------------------------------------------");
+ _loggingHelper.LogDebugInformation("Starting scan of series items.");
List staleSeasons = [.. series.SelectMany(GetStaleSeasons)];
- _loggingHelper.LogInformation("Starting scan of movies items.");
- _loggingHelper.LogInformation("-------------------------------------------------");
+ _loggingHelper.LogDebugInformation("-------------------------------------------------");
+ _loggingHelper.LogDebugInformation("End of scan for series items.");
+
+ _loggingHelper.LogDebugInformation("-------------------------------------------------");
+ _loggingHelper.LogDebugInformation("Starting scan of movie items.");
List staleMovies = [.. GetStaleMovies(movies)];
- _loggingHelper.LogInformation("-------------------------------------------------");
- _loggingHelper.LogInformation("Stale Movies found: {StaleMovies}", staleMovies.Count);
-
- if (staleMovies.Count > 0 && Configuration.DebugMode)
- {
- foreach (var movieInfo in staleMovies)
- {
- _loggingHelper.LogDebugInformation("Movie Info: ID: {Id} | Movie Name: {MovieName}", [movieInfo.Id, movieInfo.Name]);
- }
- }
+ _loggingHelper.LogDebugInformation("-------------------------------------------------");
+ _loggingHelper.LogDebugInformation("End of scan for movie items.");
_loggingHelper.LogInformation("-------------------------------------------------");
_loggingHelper.LogInformation("Stale seasons found: {StaleSeasons}", staleSeasons.Count);
- if (staleSeasons.Count > 0 && Configuration.DebugMode)
- {
- IEnumerable staleSeriesInfo = FindSeriesInfo(staleSeasons);
+ IEnumerable staleSeriesInfo = [];
- foreach (var seriesInfo in staleSeriesInfo)
+ if (staleSeasons.Count > 0)
+ {
+ staleSeriesInfo = FindSeriesInfo(staleSeasons);
+
+ foreach (SeriesInfo seriesInfo in staleSeriesInfo.Cast())
{
- _loggingHelper.LogDebugInformation("Series Info: ID: {Id} | Series Name: {SeriesName} | Stale Seasons: {Seasons}", [seriesInfo.Id, seriesInfo.SeriesName, string.Join(", ", seriesInfo.Seasons)]);
+ _loggingHelper.LogInformation("Series Info: ID: {Id} | Series Name: {SeriesName} | Stale Seasons: {Seasons}", [seriesInfo.Id, seriesInfo.Name, string.Join(", ", seriesInfo.Seasons)]);
}
}
+ else
+ {
+ _loggingHelper.LogInformation("No stale seasons found!");
+ }
+
+ _loggingHelper.LogInformation("-------------------------------------------------");
+ _loggingHelper.LogInformation("Stale Movies found: {StaleMovies}", staleMovies.Count);
+
+ IEnumerable staleMoviesInfo = [];
+
+ if (staleMovies.Count > 0)
+ {
+ staleMoviesInfo = staleMovies.Select(movie => new MovieInfo
+ {
+ Id = movie.Id,
+ Name = movie.Name
+ });
+
+ foreach (MovieInfo movieInfo in staleMoviesInfo.Cast())
+ {
+ _loggingHelper.LogInformation("Movie Info: ID: {Id} | Movie Name: {MovieName}", [movieInfo.Id, movieInfo.Name]);
+ }
+ }
+ else
+ {
+ _loggingHelper.LogInformation("No stale movies found!");
+ }
_loggingHelper.LogInformation("-------------------------------------------------");
_loggingHelper.LogInformation("Ending stale media scan...");
_loggingHelper.LogInformation("-------------------------------------------------");
- return Task.CompletedTask;
+ IEnumerable mediaInfo = staleSeriesInfo.Concat(staleMoviesInfo);
+
+ return mediaInfo;
}
private List GetStaleMovies(List movies)
{
List staleMovies = [];
- staleMovies.AddRange(movies.Where(_movieHelper.IsMovieStale));
+ try
+ {
+ staleMovies.AddRange(movies.Where(_movieHelper.IsMovieStale));
+ }
+ catch (ArgumentNullException ex)
+ {
+ _loggingHelper.LogInformation("Arguement Null Exception in GetStaleMovies!");
+ _loggingHelper.LogInformation(ex.Message);
+ }
return staleMovies;
}
@@ -133,10 +157,10 @@ public sealed class StaleMediaTask : IScheduledTask
private List GetStaleSeasons(BaseItem item)
{
+ _loggingHelper.LogDebugInformation("-------------------------------------------------");
_loggingHelper.LogDebugInformation("Debug data for series: {SeriesName}", item.Name);
_loggingHelper.LogDebugInformation("-------------------------------------------------");
- // Gets each season in a show
var seasons = _libraryManager.GetItemList(new InternalItemsQuery
{
ParentId = item.Id,
@@ -153,7 +177,17 @@ public sealed class StaleMediaTask : IScheduledTask
_loggingHelper.LogDebugInformation("Season debug information for {SeasonNumber}:", season);
- bool isSeasonDataStale = _seriesHelper.IsSeasonDataStale(episodes);
+ bool isSeasonDataStale = false;
+
+ try
+ {
+ isSeasonDataStale = _seriesHelper.IsSeasonDataStale(episodes);
+ }
+ catch (ArgumentNullException ex)
+ {
+ _loggingHelper.LogInformation("Arguement Null Exception in GetStaleSeasons!");
+ _loggingHelper.LogInformation(ex.Message);
+ }
_loggingHelper.LogDebugInformation("End of season debug information for {SeasonNumber}.", season);
@@ -163,12 +197,11 @@ public sealed class StaleMediaTask : IScheduledTask
_loggingHelper.LogDebugInformation("-------------------------------------------------");
_loggingHelper.LogDebugInformation("End of scanning for series: {Series}", item);
- _loggingHelper.LogDebugInformation("-------------------------------------------------");
return staleSeasons;
}
- private IEnumerable FindSeriesInfo(IReadOnlyCollection seasons)
+ private IEnumerable FindSeriesInfo(IReadOnlyCollection seasons)
{
Guid[] seriesIds = [.. seasons.Select(season => season.ParentId).Distinct()];
@@ -182,21 +215,11 @@ public sealed class StaleMediaTask : IScheduledTask
return new SeriesInfo
{
Id = series.Id,
- SeriesName = series.Name,
+ Name = series.Name,
Seasons = [.. seasons.Where(season => season.ParentId == series.Id).Select(season => season.Name)]
};
});
return seriesInfoList;
}
-
- IEnumerable IScheduledTask.GetDefaultTriggers()
- {
- // Run this task every 24 hours
- yield return new TaskTriggerInfo
- {
- Type = TaskTriggerInfoType.IntervalTrigger,
- IntervalTicks = TimeSpan.FromHours(24).Ticks
- };
- }
}