6 Commits

Author SHA1 Message Date
08e26943c9 Added debugging 2025-12-04 20:24:24 -07:00
65dd638b09 Added logging 2025-12-03 23:30:58 -07:00
6b20bc0828 Fixed bug for nullable movie user data 2025-12-03 23:00:19 -07:00
db3a06cc67 Logic fix 2025-12-03 22:35:14 -07:00
1a4cefba40 Updated version 2025-12-03 22:03:54 -07:00
2387fecfb6 Added main page 2025-12-03 22:03:07 -07:00
7 changed files with 114 additions and 37 deletions

View File

@@ -1,5 +1,5 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<AssemblyVersion>0.0.0.2</AssemblyVersion> <AssemblyVersion>0.0.0.4</AssemblyVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -31,4 +31,9 @@ public class PluginConfiguration : BasePluginConfiguration
/// Gets or sets the cut off days before deleting unwatched files. /// Gets or sets the cut off days before deleting unwatched files.
/// </summary> /// </summary>
public int StaleMediaCutoff { get; set; } = 90; public int StaleMediaCutoff { get; set; } = 90;
/// <summary>
/// Gets or sets debug mode.
/// </summary>
public bool DebugMode { get; set; }
} }

View File

@@ -24,6 +24,12 @@
<input id="StaleMediaCutoff" name="StaleMediaCutoff" type="number" is="emby-input" style="width: 20%;"/> <input id="StaleMediaCutoff" name="StaleMediaCutoff" type="number" is="emby-input" style="width: 20%;"/>
<div class="fieldDescription">How many days to wait before marking files as stale</div> <div class="fieldDescription">How many days to wait before marking files as stale</div>
</div> </div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label">
<input id="DebugMode" name="DebugMode" type="checkbox" is="emby-checkbox" />
<span>Debug Mode</span>
</label>
</div>
<!-- <div class="selectContainer"> <!-- <div class="selectContainer">
<label class="selectLabel" for="Options">Several Options</label> <label class="selectLabel" for="Options">Several Options</label>
<select is="emby-select" id="Options" name="Options" class="emby-select-withcolor emby-select"> <select is="emby-select" id="Options" name="Options" class="emby-select-withcolor emby-select">
@@ -71,6 +77,7 @@
document.querySelector('#RadarrAPIKey').value = config.RadarrAPIKey; document.querySelector('#RadarrAPIKey').value = config.RadarrAPIKey;
document.querySelector('#SonarrAPIKey').value = config.SonarrAPIKey; document.querySelector('#SonarrAPIKey').value = config.SonarrAPIKey;
document.querySelector('#StaleMediaCutoff').value = config.StaleMediaCutoff; document.querySelector('#StaleMediaCutoff').value = config.StaleMediaCutoff;
document.querySelector('#DebugMode').checked = config.DebugMode;
Dashboard.hideLoadingMsg(); Dashboard.hideLoadingMsg();
}); });
}); });
@@ -86,6 +93,7 @@
config.RadarrAPIKey = document.querySelector('#RadarrAPIKey').value; config.RadarrAPIKey = document.querySelector('#RadarrAPIKey').value;
config.SonarrAPIKey = document.querySelector('#SonarrAPIKey').value; config.SonarrAPIKey = document.querySelector('#SonarrAPIKey').value;
config.StaleMediaCutoff = document.querySelector('#StaleMediaCutoff').value; config.StaleMediaCutoff = document.querySelector('#StaleMediaCutoff').value;
config.DebugMode = document.querySelector('#DebugMode').checked;
ApiClient.updatePluginConfiguration(MediaCleanerConfig.pluginUniqueId, config).then(function (result) { ApiClient.updatePluginConfiguration(MediaCleanerConfig.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result); Dashboard.processPluginConfigurationUpdateResult(result);
}); });

View File

@@ -20,8 +20,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Configuration\configPage.html" /> <None Remove="Configuration\settings.html" />
<EmbeddedResource Include="Configuration\configPage.html" /> <EmbeddedResource Include="Configuration\settings.html" />
<None Remove="Pages\home.html" />
<EmbeddedResource Include="Pages\home.html" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
@@ -29,20 +31,6 @@
</PropertyGroup> </PropertyGroup>
<Target Name="GeneratePluginJson" BeforeTargets="Publish"> <Target Name="GeneratePluginJson" BeforeTargets="Publish">
<WriteLinesToFile <WriteLinesToFile File="$(PublishDir)\meta.json" Lines="{&#xA; &quot;guid&quot;: &quot;fef007a8-3e8f-4aa8-a22e-486a387f4192&quot;,&#xA; &quot;name&quot;: &quot;Media Cleaner&quot;,&#xA; &quot;category&quot;: &quot;Library&quot;,&#xA; &quot;overview&quot;: &quot;A cleaner for your stale media.&quot;,&#xA; &quot;description&quot;: &quot;Clean out the stale media from your library using scheduled tasks&quot;,&#xA; &quot;timestamp&quot;: &quot;$(Timestamp)&quot;,&#xA; &quot;targetAbi&quot;: &quot;10.11.0&quot;,&#xA; &quot;owner&quot;: &quot;T-Gander&quot;,&#xA; &quot;version&quot;: &quot;$(AssemblyVersion)&quot;&#xA; }" Overwrite="true" Encoding="utf-8" />
File="$(PublishDir)\meta.json"
Lines='{
"guid": "fef007a8-3e8f-4aa8-a22e-486a387f4192",
"name": "Media Cleaner",
"category": "Library",
"overview": "A cleaner for your stale media.",
"description": "Clean out the stale media from your library using scheduled tasks",
"timestamp": "$(Timestamp)",
"targetAbi": "10.11.0",
"owner": "T-Gander",
"version": "$(AssemblyVersion)"
}'
Overwrite="true"
Encoding="utf-8" />
</Target> </Target>
</Project> </Project>

View File

@@ -0,0 +1,11 @@
<div data-role="page" class="page type-interior pluginConfigurationPage withTabs">
<div data-role="content">
<div class="content-primary">
<div>
<a href="#configurationpage?name=Home">Home</a>
<a href="#configurationpage?name=Settings">Settings</a>
</div>
<h2>Media Cleaner</h2>
</div>
</div>
</div>

View File

@@ -44,9 +44,15 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
[ [
new PluginPageInfo new PluginPageInfo
{ {
Name = Name, Name = "Settings",
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.configPage.html", GetType().Namespace) EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Configuration.settings.html", GetType().Namespace),
} },
new PluginPageInfo
{
Name = "Home",
EmbeddedResourcePath = string.Format(CultureInfo.InvariantCulture, "{0}.Pages.home.html", GetType().Namespace),
EnableInMainMenu = true,
},
]; ];
} }
} }

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.Common; using System.Data.Common;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@@ -61,10 +62,13 @@ public sealed class StaleMediaTask : IScheduledTask
}; };
List<BaseItem> allItems = [.. _libraryManager.GetItemsResult(query).Items]; List<BaseItem> allItems = [.. _libraryManager.GetItemsResult(query).Items];
if (Configuration.DebugMode)
{
_logger.LogInformation("Total items found: {AllItems}", allItems); _logger.LogInformation("Total items found: {AllItems}", allItems);
}
List<BaseItem> series = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Series)]; List<BaseItem> series = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Series)];
List<BaseItem> movies = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Movie && item.UserData.Count > 0)]; List<BaseItem> movies = [.. allItems.Where(item => item.GetBaseItemKind() == BaseItemKind.Movie)];
List<BaseItem> staleEpisodes = [.. series.SelectMany(GetStaleEpisodes)]; List<BaseItem> staleEpisodes = [.. series.SelectMany(GetStaleEpisodes)];
List<BaseItem> staleMovies = [.. GetStaleMovies(movies)]; List<BaseItem> staleMovies = [.. GetStaleMovies(movies)];
@@ -82,10 +86,13 @@ public sealed class StaleMediaTask : IScheduledTask
List<SeriesInfo> seriesInfoList = FindSeriesInfoFromEpisodes(staleEpisodes); List<SeriesInfo> seriesInfoList = FindSeriesInfoFromEpisodes(staleEpisodes);
foreach (var seriesInfo in seriesInfoList) foreach (var seriesInfo in seriesInfoList)
{
if (Configuration.DebugMode)
{ {
_logger.LogInformation("Series Info: ID: {Id} | Series Name: {SeriesName} | Stale Seasons: {Seasons}", [seriesInfo.Id, seriesInfo.SeriesName, string.Join(", ", seriesInfo.Seasons)]); _logger.LogInformation("Series Info: ID: {Id} | Series Name: {SeriesName} | Stale Seasons: {Seasons}", [seriesInfo.Id, seriesInfo.SeriesName, string.Join(", ", seriesInfo.Seasons)]);
} }
} }
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -96,15 +103,36 @@ public sealed class StaleMediaTask : IScheduledTask
foreach (var movie in movies) foreach (var movie in movies)
{ {
bool movieIsStale = movie.DateCreated > DateTime.Now.AddDays(-Configuration.StaleMediaCutoff); bool movieIsStale = movie.DateCreated < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff);
bool movieHasUserData = movie.UserData.Count > 0;
if (movieHasUserData)
{
if (Configuration.DebugMode){
_logger.LogInformation("Movie has user data: {Movie}", movie);
_logger.LogInformation("-------------------------------------------------");
}
var mostRecentUserData = movie.UserData.OrderByDescending(data => data.LastPlayedDate).First(); var mostRecentUserData = movie.UserData.OrderByDescending(data => data.LastPlayedDate).First();
if (Configuration.DebugMode){
_logger.LogInformation("Most recent user data: {Movie}", movie);
foreach (var property in typeof(UserData).GetProperties())
{
_logger.LogInformation("{PropertyName}: {PropertyValue}", property.Name, property.GetValue(mostRecentUserData));
}
_logger.LogInformation("-------------------------------------------------");
}
if (mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff)) if (mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff))
{ {
_logger.LogInformation("Most recent user data last played date is outside of cutoff. Adding to stale movies.");
staleMovies.Add(movie); staleMovies.Add(movie);
} }
}
else if (movieIsStale) else if (movieIsStale)
{ {
_logger.LogInformation("Movie has no user data and was created outside of cutoff: {DateCreated}", movie.DateCreated);
staleMovies.Add(movie); staleMovies.Add(movie);
} }
} }
@@ -165,18 +193,45 @@ public sealed class StaleMediaTask : IScheduledTask
ParentId = season.Id, ParentId = season.Id,
Recursive = false Recursive = false
}); });
bool seasonHasUserData = episodes.Any(episode => episode.UserData.Count > 0); bool seasonHasUserData = episodes.Any(episode => episode.UserData.Count > 0);
bool seasonIsStale = episodes.All(episode => episode.DateCreated > DateTime.Now.AddDays(-Configuration.StaleMediaCutoff)); if (seasonHasUserData && Configuration.DebugMode)
{
_logger.LogInformation("Season has user data for episodes: {Episodes}", episodes);
_logger.LogInformation("-------------------------------------------------");
}
bool seasonIsStale = episodes.All(episode => episode.DateCreated < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff));
if (seasonIsStale && Configuration.DebugMode)
{
_logger.LogInformation("All episodes are outside media cutoff.");
_logger.LogInformation("-------------------------------------------------");
}
if (seasonHasUserData) if (seasonHasUserData)
{ {
var episodesWithUserData = episodes.Where(episode => episode.UserData.Count > 0).ToList(); var episodesWithUserData = episodes.Where(episode => episode.UserData.Count > 0).ToList();
if(Configuration.DebugMode){
_logger.LogInformation("Episodes with user data: {EpisodesWithUserData}", episodesWithUserData);
_logger.LogInformation("-------------------------------------------------");
}
foreach (var episode in episodesWithUserData) foreach (var episode in episodesWithUserData)
{ {
var mostRecentUserData = episode.UserData.OrderByDescending(data => data.LastPlayedDate).First(); var mostRecentUserData = episode.UserData.OrderByDescending(data => data.LastPlayedDate).First();
if(Configuration.DebugMode){
foreach (var property in typeof(UserData).GetProperties())
{
_logger.LogInformation("{PropertyName}: {PropertyValue}", property.Name, property.GetValue(mostRecentUserData));
}
_logger.LogInformation("-------------------------------------------------");
}
if (mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff)) if (mostRecentUserData.LastPlayedDate < DateTime.Now.AddDays(-Configuration.StaleMediaCutoff))
{ {
if(Configuration.DebugMode){
_logger.LogInformation("Most Recent User Data Last Played Date is: {LastPlayedDate}. All Episodes are stale.", mostRecentUserData.LastPlayedDate);
_logger.LogInformation("-------------------------------------------------");
}
staleEpisodes.AddRange(episodes); staleEpisodes.AddRange(episodes);
break; break;
} }
@@ -185,6 +240,10 @@ public sealed class StaleMediaTask : IScheduledTask
// Check for episodes that have gone unwatched for stale media cutoff // Check for episodes that have gone unwatched for stale media cutoff
else if (seasonIsStale) else if (seasonIsStale)
{ {
if(Configuration.DebugMode){
_logger.LogInformation("No user data, adding all episodes as it is outside of cutoff.");
_logger.LogInformation("-------------------------------------------------");
}
staleEpisodes.AddRange(episodes); staleEpisodes.AddRange(episodes);
} }
} }