chore:file cleanup
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# Files Removed During Repository Cleanup
|
||||
|
||||
## Removed Files:
|
||||
- `example-docker-compose.yml` - Replaced with properly named `docker-compose.example.yml`
|
||||
- `example-multi-registry-workflow.yml` - Personal CI/CD workflow not needed for public repo
|
||||
|
||||
## Reason:
|
||||
These were development/CI artifacts that don't belong in a clean public webhook application repository.
|
||||
|
||||
## Result:
|
||||
Clean, focused NFOGuard webhook Docker application repository ready for public use.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
# Files to Remove from NFOGuard Webhook Repository
|
||||
# These are CI/development artifacts that don't belong in the public webhook app repo
|
||||
|
||||
gitea-registry-troubleshooting.md
|
||||
gitea-packages-config.ini
|
||||
find-gitea-local-ip.sh
|
||||
example-multi-registry-workflow.yml
|
||||
example-docker-compose.yml
|
||||
example-distribution-README.md
|
||||
docker-compose.example.yml
|
||||
cleanup-github.sh
|
||||
check-gitea-registry.sh
|
||||
cleanup-files.sh
|
||||
|
||||
# You can remove them with:
|
||||
# git rm <filename>
|
||||
# or
|
||||
# rm <filename> && git add -u
|
||||
@@ -1,91 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>NFOGuard Settings</title>
|
||||
</head>
|
||||
<body data-role="page" class="page type-interior pluginConfigurationPage" data-require="emby-input,emby-button,emby-select,emby-checkbox">
|
||||
<div data-role="content">
|
||||
<div class="content-primary">
|
||||
<form id="nfoguardConfigForm">
|
||||
<h1>NFOGuard Settings</h1>
|
||||
<p>Configure how NFOGuard synchronizes TV episode dates in Emby.</p>
|
||||
|
||||
<div class="inputContainer">
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableRealTimeSync" />
|
||||
<span>Enable Real-time Sync</span>
|
||||
</label>
|
||||
<div class="fieldDescription">Automatically sync PremiereDate to DateCreated when episodes are added or updated</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableScheduledTask" />
|
||||
<span>Enable Scheduled Task</span>
|
||||
</label>
|
||||
<div class="fieldDescription">Run NFOGuard as a scheduled task to process existing episodes</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" id="chkLogVerbose" />
|
||||
<span>Verbose Logging</span>
|
||||
</label>
|
||||
<div class="fieldDescription">Enable detailed logging for troubleshooting</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
var pluginId = "B8A7F9E2-1234-4567-8901-2B3C4D5E6F7A";
|
||||
|
||||
function loadPage() {
|
||||
ApiClient.getPluginConfiguration(pluginId).then(function (config) {
|
||||
document.getElementById('chkEnableRealTimeSync').checked = config.EnableRealTimeSync !== false;
|
||||
document.getElementById('chkEnableScheduledTask').checked = config.EnableScheduledTask === true;
|
||||
document.getElementById('chkLogVerbose').checked = config.LogVerbose === true;
|
||||
}).catch(function(err) {
|
||||
console.error('Error loading NFOGuard config:', err);
|
||||
});
|
||||
}
|
||||
|
||||
function saveConfig() {
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
ApiClient.getPluginConfiguration(pluginId).then(function (config) {
|
||||
config.EnableRealTimeSync = document.getElementById('chkEnableRealTimeSync').checked;
|
||||
config.EnableScheduledTask = document.getElementById('chkEnableScheduledTask').checked;
|
||||
config.LogVerbose = document.getElementById('chkLogVerbose').checked;
|
||||
|
||||
return ApiClient.updatePluginConfiguration(pluginId, config);
|
||||
}).then(function () {
|
||||
Dashboard.hideLoadingMsg();
|
||||
Dashboard.alert('Settings saved successfully.');
|
||||
}).catch(function (err) {
|
||||
Dashboard.hideLoadingMsg();
|
||||
Dashboard.alert('Error saving settings: ' + (err.message || err));
|
||||
console.error('Save error:', err);
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('nfoguardConfigForm').addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
saveConfig();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Load settings when page loads
|
||||
loadPage();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,70 +0,0 @@
|
||||
# NFOGuard Emby Plugin Deployment Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- .NET Framework 4.8 SDK or Visual Studio Build Tools
|
||||
- Emby Server 4.7+ running
|
||||
- Access to Emby's plugins directory
|
||||
|
||||
## Building the Plugin
|
||||
|
||||
### Windows
|
||||
```cmd
|
||||
build.bat
|
||||
```
|
||||
|
||||
### Linux/macOS
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
## Installation Steps
|
||||
|
||||
1. **Build the plugin** using the build script above
|
||||
2. **Locate your Emby plugins directory:**
|
||||
- Windows: `C:\ProgramData\Emby-Server\plugins`
|
||||
- Linux: `/var/lib/emby/plugins`
|
||||
- Docker: `/config/plugins` (inside container)
|
||||
- Synology: `/volume1/Emby/plugins`
|
||||
|
||||
3. **Copy the DLL:**
|
||||
```bash
|
||||
cp bin/Release/NFOGuard.Emby.Plugin.dll /path/to/emby/plugins/
|
||||
```
|
||||
|
||||
4. **Restart Emby Server**
|
||||
|
||||
5. **Verify installation:**
|
||||
- Check Emby dashboard → Plugins
|
||||
- Look for "NFOGuard" in the installed plugins list
|
||||
- Check Emby logs for "NFOGuard :: Plugin starting up"
|
||||
|
||||
## Testing
|
||||
|
||||
1. **Add a new TV episode** to your library with an NFO file containing `<dateadded>`
|
||||
2. **Check the logs** for NFOGuard processing messages:
|
||||
```
|
||||
NFOGuard :: Processing Added Episode :: Episode Name
|
||||
NFOGuard :: Syncing PremiereDate to DateCreated :: Episode Name
|
||||
NFOGuard :: Successfully updated :: Episode Name
|
||||
```
|
||||
3. **Verify in database:**
|
||||
```sql
|
||||
sqlite3 library.db2 "SELECT DateCreated, PremiereDate FROM MediaItems WHERE Name LIKE '%episode%';"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Plugin not appearing:** Check Emby logs for loading errors
|
||||
- **Not processing episodes:** Verify log level includes Info messages
|
||||
- **Build failures:** Ensure all Emby SDK DLLs are in EmbySDK folder
|
||||
- **Permission errors:** Ensure Emby service has write access to database
|
||||
|
||||
## SDK Dependencies
|
||||
|
||||
The plugin requires these Emby SDK DLLs in the `EmbySDK` folder:
|
||||
- MediaBrowser.Common.dll
|
||||
- MediaBrowser.Controller.dll
|
||||
- MediaBrowser.Model.dll
|
||||
|
||||
These can be extracted from your Emby Server installation directory.
|
||||
@@ -1,173 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace NFOGuard.Emby.Plugin
|
||||
{
|
||||
public class FreeTierManager
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
public FreeTierManager(ILogger logger, ILibraryManager libraryManager = null)
|
||||
{
|
||||
_logger = logger;
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
public bool CanProcessItems(int itemCount, out string reason)
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
reason = string.Empty;
|
||||
|
||||
// If licensed or no limit set, allow processing
|
||||
if (config.LicenseValid || config.MonthlyProcessingLimit == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if month has rolled over
|
||||
ResetMonthlyCounterIfNeeded();
|
||||
|
||||
// Check if this processing would exceed the limit
|
||||
var wouldExceed = config.ProcessedThisMonth + itemCount > config.MonthlyProcessingLimit;
|
||||
|
||||
if (wouldExceed)
|
||||
{
|
||||
var remaining = Math.Max(0, config.MonthlyProcessingLimit - config.ProcessedThisMonth);
|
||||
reason = $"Free tier limit reached. {remaining} items remaining this month of {config.MonthlyProcessingLimit} allowed. Upgrade to process unlimited items.";
|
||||
_logger.Info($"NFOGuard :: {reason}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RecordProcessedItems(int count)
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
|
||||
// Only track if using free tier limits
|
||||
if (config.MonthlyProcessingLimit > 0 && !config.LicenseValid)
|
||||
{
|
||||
config.ProcessedThisMonth += count;
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
|
||||
_logger.Info($"NFOGuard :: Free tier usage: {config.ProcessedThisMonth}/{config.MonthlyProcessingLimit} items this month");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFreeTierLimits(int monthlyLimit)
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
config.MonthlyProcessingLimit = monthlyLimit;
|
||||
config.LicenseValid = false;
|
||||
|
||||
if (config.InstallDate == DateTime.MinValue)
|
||||
{
|
||||
config.InstallDate = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
ResetMonthlyCounterIfNeeded();
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
|
||||
_logger.Info($"NFOGuard :: Free tier configured: {monthlyLimit} items per month");
|
||||
}
|
||||
|
||||
public void SetPercentageBasedLimits()
|
||||
{
|
||||
if (_libraryManager == null)
|
||||
{
|
||||
_logger.Warn("NFOGuard :: Cannot calculate percentage limits without LibraryManager");
|
||||
SetFreeTierLimits(500); // Fallback to fixed limit
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Get library counts
|
||||
var episodes = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { "Episode" },
|
||||
Recursive = true
|
||||
}).OfType<Episode>().Count();
|
||||
|
||||
var movies = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { "Movie" },
|
||||
Recursive = true
|
||||
}).OfType<Movie>().Count();
|
||||
|
||||
// Calculate 2% of TV episodes + 5% of movies
|
||||
var episodeLimit = Math.Max(1, (int)(episodes * 0.02)); // At least 1 episode
|
||||
var movieLimit = Math.Max(1, (int)(movies * 0.05)); // At least 1 movie
|
||||
var totalLimit = episodeLimit + movieLimit;
|
||||
|
||||
_logger.Info($"NFOGuard :: Library analysis - Episodes: {episodes}, Movies: {movies}");
|
||||
_logger.Info($"NFOGuard :: Free tier limits - Episodes: {episodeLimit} (2%), Movies: {movieLimit} (5%), Total: {totalLimit}");
|
||||
|
||||
var config = Plugin.Instance.Configuration;
|
||||
config.MonthlyProcessingLimit = totalLimit;
|
||||
config.LicenseValid = false;
|
||||
|
||||
if (config.InstallDate == DateTime.MinValue)
|
||||
{
|
||||
config.InstallDate = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
ResetMonthlyCounterIfNeeded();
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Error calculating percentage limits", ex);
|
||||
SetFreeTierLimits(500); // Fallback
|
||||
}
|
||||
}
|
||||
|
||||
public void EnableFullVersion()
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
config.LicenseValid = true;
|
||||
config.MonthlyProcessingLimit = 0;
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
|
||||
_logger.Info("NFOGuard :: Full version enabled - unlimited processing");
|
||||
}
|
||||
|
||||
private void ResetMonthlyCounterIfNeeded()
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Reset if last reset was more than a month ago or never set
|
||||
if (config.LastProcessingReset == DateTime.MinValue ||
|
||||
now > config.LastProcessingReset.AddMonths(1))
|
||||
{
|
||||
config.ProcessedThisMonth = 0;
|
||||
config.LastProcessingReset = new DateTime(now.Year, now.Month, 1); // First of current month
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
|
||||
_logger.Info("NFOGuard :: Monthly processing counter reset");
|
||||
}
|
||||
}
|
||||
|
||||
public string GetUsageInfo()
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
|
||||
if (config.LicenseValid || config.MonthlyProcessingLimit == 0)
|
||||
{
|
||||
return "Full version - unlimited processing";
|
||||
}
|
||||
|
||||
ResetMonthlyCounterIfNeeded();
|
||||
var remaining = Math.Max(0, config.MonthlyProcessingLimit - config.ProcessedThisMonth);
|
||||
return $"Free tier: {remaining} items remaining this month (of {config.MonthlyProcessingLimit} allowed)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
using System;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace NFOGuard.Emby.Plugin
|
||||
{
|
||||
public class LibraryEventHandler : IDisposable
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IItemRepository _itemRepository;
|
||||
private readonly ILogger _logger;
|
||||
private readonly LicenseManager _licenseManager;
|
||||
private readonly FreeTierManager _freeTierManager;
|
||||
|
||||
public LibraryEventHandler(ILibraryManager libraryManager, IItemRepository itemRepository, ILogger logger)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_itemRepository = itemRepository;
|
||||
_logger = logger;
|
||||
_licenseManager = new LicenseManager(_logger);
|
||||
_freeTierManager = new FreeTierManager(_logger);
|
||||
|
||||
// Subscribe to library events
|
||||
_libraryManager.ItemAdded += OnItemAdded;
|
||||
_libraryManager.ItemUpdated += OnItemUpdated;
|
||||
}
|
||||
|
||||
private void OnItemAdded(object sender, ItemChangeEventArgs e)
|
||||
{
|
||||
ProcessItem(e.Item, "Added");
|
||||
}
|
||||
|
||||
private void OnItemUpdated(object sender, ItemChangeEventArgs e)
|
||||
{
|
||||
ProcessItem(e.Item, "Updated");
|
||||
}
|
||||
|
||||
private void ProcessItem(BaseItem item, string eventType)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if real-time sync is enabled
|
||||
if (!Plugin.Instance.Configuration.EnableRealTimeSync)
|
||||
return;
|
||||
|
||||
// Free tier: disable real-time processing
|
||||
var config = Plugin.Instance.Configuration;
|
||||
if (config.MonthlyProcessingLimit > 0 && !config.LicenseValid)
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Real-time processing disabled in free tier. Use scheduled task instead.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we can process this item
|
||||
if (!_freeTierManager.CanProcessItems(1, out string reason))
|
||||
{
|
||||
_logger.Info($"NFOGuard :: Skipping real-time processing: {reason}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Process TV episodes and Movies
|
||||
if (item is Episode episode)
|
||||
{
|
||||
ProcessEpisode(episode, eventType);
|
||||
}
|
||||
else if (item is Movie movie)
|
||||
{
|
||||
ProcessMovie(movie, eventType);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException($"NFOGuard :: Error processing item {item?.Name}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessEpisode(Episode episode, string eventType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Processing {eventType} Episode :: {episode.Name}");
|
||||
|
||||
// Check if PremiereDate exists and differs from DateCreated
|
||||
if (episode.PremiereDate.HasValue &&
|
||||
episode.DateCreated != episode.PremiereDate.Value)
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Syncing PremiereDate to DateCreated :: {episode.Name} :: " +
|
||||
$"PremiereDate: {episode.PremiereDate.Value}, DateCreated: {episode.DateCreated}");
|
||||
|
||||
// Update DateCreated to match PremiereDate
|
||||
episode.DateCreated = episode.PremiereDate.Value;
|
||||
|
||||
try
|
||||
{
|
||||
// Save to repository using ItemUpdateType.MetadataEdit
|
||||
episode.UpdateToRepository(ItemUpdateType.MetadataEdit);
|
||||
_logger.Info($"NFOGuard :: Successfully updated episode :: {episode.Name}");
|
||||
|
||||
// Record usage for free tier tracking
|
||||
_freeTierManager.RecordProcessedItems(1);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException($"NFOGuard :: Failed to update episode {episode.Name}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException($"NFOGuard :: Error processing episode {episode?.Name}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMovie(Movie movie, string eventType)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Processing {eventType} Movie :: {movie.Name}");
|
||||
|
||||
// Check if PremiereDate exists and differs from DateCreated
|
||||
if (movie.PremiereDate.HasValue &&
|
||||
movie.DateCreated != movie.PremiereDate.Value)
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Syncing PremiereDate to DateCreated :: {movie.Name} :: " +
|
||||
$"PremiereDate: {movie.PremiereDate.Value}, DateCreated: {movie.DateCreated}");
|
||||
|
||||
// Update DateCreated to match PremiereDate
|
||||
movie.DateCreated = movie.PremiereDate.Value;
|
||||
|
||||
try
|
||||
{
|
||||
// Save to repository using ItemUpdateType.MetadataEdit
|
||||
movie.UpdateToRepository(ItemUpdateType.MetadataEdit);
|
||||
_logger.Info($"NFOGuard :: Successfully updated movie :: {movie.Name}");
|
||||
|
||||
// Record usage for free tier tracking
|
||||
_freeTierManager.RecordProcessedItems(1);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException($"NFOGuard :: Failed to update movie {movie.Name}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException($"NFOGuard :: Error processing movie {movie?.Name}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_libraryManager != null)
|
||||
{
|
||||
_libraryManager.ItemAdded -= OnItemAdded;
|
||||
_libraryManager.ItemUpdated -= OnItemUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace NFOGuard.Emby.Plugin
|
||||
{
|
||||
public class LicenseManager
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string NFOGUARD_API_URL = "https://nfoguard.com/api"; // Your future API endpoint
|
||||
|
||||
public LicenseManager(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_httpClient = new HttpClient();
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(10); // Quick timeout for license checks
|
||||
}
|
||||
|
||||
public async Task<bool> CheckLicenseStatusAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
|
||||
// Get unique identifier for this installation
|
||||
var machineId = GetMachineIdentifier();
|
||||
|
||||
_logger.Info("NFOGuard :: Checking automatic license status...");
|
||||
|
||||
// Check Patreon subscription
|
||||
var patreonValid = await CheckPatreonSubscriptionAsync(machineId);
|
||||
if (patreonValid)
|
||||
{
|
||||
_logger.Info("NFOGuard :: Valid Patreon subscription found");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check PayPal recurring payment
|
||||
var paypalValid = await CheckPayPalSubscriptionAsync(machineId);
|
||||
if (paypalValid)
|
||||
{
|
||||
_logger.Info("NFOGuard :: Valid PayPal subscription found");
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.Info("NFOGuard :: No active subscription found, using free tier");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: License validation error", ex);
|
||||
return false; // Default to free tier on error
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CheckPatreonSubscriptionAsync(string machineId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestUrl = $"{NFOGUARD_API_URL}/patreon/check?machine_id={machineId}";
|
||||
var response = await _httpClient.GetStringAsync(requestUrl);
|
||||
|
||||
// Parse response (you'll implement the API to return JSON like {"active": true, "tier": "supporter"})
|
||||
return response.Contains("\"active\":true");
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
_logger.Info("NFOGuard :: Cannot reach license server, running in offline mode");
|
||||
return false; // Offline mode - use free tier
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Patreon check error", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CheckPayPalSubscriptionAsync(string machineId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestUrl = $"{NFOGUARD_API_URL}/paypal/check?machine_id={machineId}";
|
||||
var response = await _httpClient.GetStringAsync(requestUrl);
|
||||
|
||||
// Parse response
|
||||
return response.Contains("\"active\":true");
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
_logger.Info("NFOGuard :: Cannot reach license server, running in offline mode");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: PayPal check error", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetMachineIdentifier()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a unique but consistent identifier for this installation
|
||||
// Combine multiple factors to create a stable machine ID
|
||||
var machineName = Environment.MachineName ?? "unknown";
|
||||
var userDomain = Environment.UserDomainName ?? "unknown";
|
||||
var installPath = Plugin.Instance.ConfigurationFilePath;
|
||||
|
||||
var combined = $"{machineName}-{userDomain}-{installPath}".GetHashCode().ToString("X8");
|
||||
|
||||
_logger.Info($"NFOGuard :: Machine ID: {combined}");
|
||||
return combined;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Error generating machine ID", ex);
|
||||
return "FALLBACK-ID";
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTrialExpired()
|
||||
{
|
||||
// Implement trial logic here
|
||||
// For example, check installation date vs current date
|
||||
var installDate = Plugin.Instance.Configuration.InstallDate;
|
||||
if (installDate == DateTime.MinValue)
|
||||
{
|
||||
Plugin.Instance.Configuration.InstallDate = DateTime.UtcNow;
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
return false;
|
||||
}
|
||||
|
||||
var trialDays = 30; // 30-day trial
|
||||
return DateTime.UtcNow > installDate.AddDays(trialDays);
|
||||
}
|
||||
|
||||
public bool CanUseFeature()
|
||||
{
|
||||
// Check if license is valid or trial is still active
|
||||
return Plugin.Instance.Configuration.LicenseValid || !IsTrialExpired();
|
||||
}
|
||||
|
||||
public async Task<bool> CheckAndActivateLicenseAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Info("NFOGuard :: Checking for active subscriptions...");
|
||||
|
||||
var isValid = await CheckLicenseStatusAsync();
|
||||
var config = Plugin.Instance.Configuration;
|
||||
|
||||
if (isValid && !config.LicenseValid)
|
||||
{
|
||||
// Activate license
|
||||
config.LicenseValid = true;
|
||||
config.MonthlyProcessingLimit = 0; // Remove limits
|
||||
config.ProcessedThisMonth = 0; // Reset counter
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
|
||||
var freeTierManager = new FreeTierManager(_logger);
|
||||
freeTierManager.EnableFullVersion();
|
||||
|
||||
_logger.Info("NFOGuard :: Subscription detected - unlimited processing activated!");
|
||||
return true;
|
||||
}
|
||||
else if (!isValid && config.LicenseValid)
|
||||
{
|
||||
// Deactivate license
|
||||
config.LicenseValid = false;
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
|
||||
var freeTierManager = new FreeTierManager(_logger);
|
||||
freeTierManager.SetPercentageBasedLimits();
|
||||
|
||||
_logger.Info("NFOGuard :: No active subscription found - switched to free tier");
|
||||
return false;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: License check error", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> RegisterMachineAsync(string userEmail)
|
||||
{
|
||||
try
|
||||
{
|
||||
var machineId = GetMachineIdentifier();
|
||||
var config = Plugin.Instance.Configuration;
|
||||
|
||||
var registrationData = new
|
||||
{
|
||||
machine_id = machineId,
|
||||
email = userEmail,
|
||||
emby_version = Environment.OSVersion.ToString(),
|
||||
plugin_version = Plugin.Instance.Description,
|
||||
install_date = config.InstallDate.ToString("yyyy-MM-dd"),
|
||||
library_stats = GetLibraryStats()
|
||||
};
|
||||
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(registrationData);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _httpClient.PostAsync($"{NFOGUARD_API_URL}/register", content);
|
||||
var responseText = await response.Content.ReadAsStringAsync();
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.Info($"NFOGuard :: Machine registered successfully: {machineId}");
|
||||
return $"Registration successful!\n\nMachine ID: {machineId}\n\nTo activate unlimited processing:\n• Subscribe on Patreon: https://patreon.com/nfoguard\n• Or PayPal recurring: https://nfoguard.com/paypal\n\nThe plugin will automatically detect your subscription within 24 hours.";
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn($"NFOGuard :: Registration failed: {responseText}");
|
||||
return $"Registration failed. You can still use the free tier.\n\nMachine ID: {machineId}\nError: {responseText}";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Registration error", ex);
|
||||
var machineId = GetMachineIdentifier();
|
||||
return $"Registration failed (offline mode).\n\nMachine ID: {machineId}\n\nTo activate later, visit: https://nfoguard.com/register";
|
||||
}
|
||||
}
|
||||
|
||||
private object GetLibraryStats()
|
||||
{
|
||||
try
|
||||
{
|
||||
// This would be used for analytics (anonymized)
|
||||
return new
|
||||
{
|
||||
episodes_estimated = "unknown",
|
||||
movies_estimated = "unknown",
|
||||
free_tier_usage = Plugin.Instance.Configuration.ProcessedThisMonth
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new { error = "stats_unavailable" };
|
||||
}
|
||||
}
|
||||
|
||||
public void DeactivateLicense()
|
||||
{
|
||||
try
|
||||
{
|
||||
var config = Plugin.Instance.Configuration;
|
||||
config.LicenseValid = false;
|
||||
|
||||
// Reset to percentage-based free tier
|
||||
var freeTierManager = new FreeTierManager(_logger);
|
||||
freeTierManager.SetPercentageBasedLimits();
|
||||
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
|
||||
_logger.Info("NFOGuard :: License deactivated - switched to free tier");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: License deactivation error", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>NFOGuard.Emby.Plugin</RootNamespace>
|
||||
<AssemblyName>NFOGuard.Emby.Plugin</AssemblyName>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="MediaBrowser.Common">
|
||||
<HintPath>..\EmbySDK\MediaBrowser.Common.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="MediaBrowser.Controller">
|
||||
<HintPath>..\EmbySDK\MediaBrowser.Controller.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="MediaBrowser.Model">
|
||||
<HintPath>..\EmbySDK\MediaBrowser.Model.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<!-- All web resources disabled to prevent UI loading issues -->
|
||||
</Project>
|
||||
@@ -1,226 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace NFOGuard.Emby.Plugin
|
||||
{
|
||||
public class NFOGuardScheduledTask : IScheduledTask
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ILogger _logger;
|
||||
private readonly FreeTierManager _freeTierManager;
|
||||
|
||||
public string Name => "DateSync Task";
|
||||
|
||||
public string Key => "NFOGuardSync";
|
||||
|
||||
public string Description => "Synchronizes PremiereDate to DateCreated for all TV episodes and movies";
|
||||
|
||||
public string Category => "Library";
|
||||
|
||||
public NFOGuardScheduledTask(ILibraryManager libraryManager, ILogManager logManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_logger = logManager.GetLogger("NFOGuard");
|
||||
_freeTierManager = new FreeTierManager(_logger);
|
||||
}
|
||||
|
||||
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
if (!Plugin.Instance.Configuration.EnableScheduledTask)
|
||||
{
|
||||
_logger.Info("NFOGuard :: Scheduled task is disabled in configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.Info("NFOGuard :: Starting scheduled task execution");
|
||||
|
||||
try
|
||||
{
|
||||
// Get all TV episodes and movies
|
||||
var episodes = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { "Episode" },
|
||||
Recursive = true
|
||||
}).OfType<Episode>().ToList();
|
||||
|
||||
var movies = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { "Movie" },
|
||||
Recursive = true
|
||||
}).OfType<Movie>().ToList();
|
||||
|
||||
var totalItems = episodes.Count + movies.Count;
|
||||
_logger.Info($"NFOGuard :: Found {episodes.Count} episodes and {movies.Count} movies to process ({totalItems} total)");
|
||||
|
||||
// Calculate items that need processing
|
||||
var itemsNeedingSync = episodes.Where(e => e.PremiereDate.HasValue && e.DateCreated != e.PremiereDate.Value).Count() +
|
||||
movies.Where(m => m.PremiereDate.HasValue && m.DateCreated != m.PremiereDate.Value).Count();
|
||||
|
||||
_logger.Info($"NFOGuard :: {itemsNeedingSync} items need date synchronization");
|
||||
|
||||
// Check free tier limits
|
||||
if (!_freeTierManager.CanProcessItems(itemsNeedingSync, out string reason))
|
||||
{
|
||||
_logger.Info($"NFOGuard :: {reason}");
|
||||
|
||||
// In free tier, process only what we can
|
||||
var pluginConfig = Plugin.Instance.Configuration;
|
||||
if (pluginConfig.MonthlyProcessingLimit > 0 && !pluginConfig.LicenseValid)
|
||||
{
|
||||
var allowedCount = Math.Max(0, pluginConfig.MonthlyProcessingLimit - pluginConfig.ProcessedThisMonth);
|
||||
_logger.Info($"NFOGuard :: Free tier: Processing {allowedCount} items only");
|
||||
|
||||
if (allowedCount == 0)
|
||||
{
|
||||
_logger.Info("NFOGuard :: Free tier limit reached. No items will be processed this month.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var processedCount = 0;
|
||||
var updatedCount = 0;
|
||||
var config = Plugin.Instance.Configuration;
|
||||
var remainingQuota = config.MonthlyProcessingLimit > 0 && !config.LicenseValid
|
||||
? Math.Max(0, config.MonthlyProcessingLimit - config.ProcessedThisMonth)
|
||||
: int.MaxValue;
|
||||
|
||||
// Process episodes
|
||||
for (int i = 0; i < episodes.Count && (config.LicenseValid || remainingQuota > 0); i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var episode = episodes[i];
|
||||
try
|
||||
{
|
||||
// Always log detailed info to diagnose the issue
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Episode: {episode.Name} :: " +
|
||||
$"PremiereDate: {episode.PremiereDate?.ToString("yyyy-MM-dd HH:mm:ss") ?? "NULL"}, " +
|
||||
$"DateCreated: {episode.DateCreated.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
|
||||
if (episode.PremiereDate.HasValue &&
|
||||
episode.DateCreated != episode.PremiereDate.Value)
|
||||
{
|
||||
_logger.Info($"NFOGuard :: SYNCING episode: {episode.Name} :: " +
|
||||
$"PremiereDate: {episode.PremiereDate.Value}, DateCreated: {episode.DateCreated}");
|
||||
|
||||
episode.DateCreated = episode.PremiereDate.Value;
|
||||
episode.UpdateToRepository(ItemUpdateType.MetadataEdit);
|
||||
updatedCount++;
|
||||
|
||||
// Track usage and decrement quota
|
||||
_freeTierManager.RecordProcessedItems(1);
|
||||
if (!config.LicenseValid && config.MonthlyProcessingLimit > 0)
|
||||
{
|
||||
remainingQuota--;
|
||||
}
|
||||
}
|
||||
else if (!episode.PremiereDate.HasValue)
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Episode {episode.Name} has no PremiereDate - skipping");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Episode {episode.Name} dates already match - no sync needed");
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
var progressPercentage = (double)processedCount / totalItems * 100;
|
||||
progress?.Report(progressPercentage);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException($"NFOGuard :: Error processing episode {episode.Name}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Process movies
|
||||
for (int i = 0; i < movies.Count && (config.LicenseValid || remainingQuota > 0); i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var movie = movies[i];
|
||||
try
|
||||
{
|
||||
// Always log detailed info to diagnose the issue
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Movie: {movie.Name} :: " +
|
||||
$"PremiereDate: {movie.PremiereDate?.ToString("yyyy-MM-dd HH:mm:ss") ?? "NULL"}, " +
|
||||
$"DateCreated: {movie.DateCreated.ToString("yyyy-MM-dd HH:mm:ss")}");
|
||||
|
||||
if (movie.PremiereDate.HasValue &&
|
||||
movie.DateCreated != movie.PremiereDate.Value)
|
||||
{
|
||||
_logger.Info($"NFOGuard :: SYNCING movie: {movie.Name} :: " +
|
||||
$"PremiereDate: {movie.PremiereDate.Value}, DateCreated: {movie.DateCreated}");
|
||||
|
||||
movie.DateCreated = movie.PremiereDate.Value;
|
||||
movie.UpdateToRepository(ItemUpdateType.MetadataEdit);
|
||||
updatedCount++;
|
||||
|
||||
// Track usage and decrement quota
|
||||
_freeTierManager.RecordProcessedItems(1);
|
||||
if (!config.LicenseValid && config.MonthlyProcessingLimit > 0)
|
||||
{
|
||||
remainingQuota--;
|
||||
}
|
||||
}
|
||||
else if (!movie.PremiereDate.HasValue)
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Movie {movie.Name} has no PremiereDate - skipping");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Plugin.Instance.Configuration.LogVerbose)
|
||||
_logger.Info($"NFOGuard :: Movie {movie.Name} dates already match - no sync needed");
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
var progressPercentage = (double)processedCount / totalItems * 100;
|
||||
progress?.Report(progressPercentage);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException($"NFOGuard :: Error processing movie {movie.Name}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Info($"NFOGuard :: Scheduled task completed. Processed: {processedCount} items ({episodes.Count} episodes, {movies.Count} movies), Updated: {updatedCount}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Scheduled task failed", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
// Default to run weekly at 3 AM on Sunday
|
||||
return new[]
|
||||
{
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = TaskTriggerInfo.TriggerWeekly,
|
||||
DayOfWeek = DayOfWeek.Sunday,
|
||||
TimeOfDayTicks = TimeSpan.FromHours(3).Ticks
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace NFOGuard.Emby.Plugin
|
||||
{
|
||||
public class Plugin : BasePlugin<PluginConfiguration>
|
||||
{
|
||||
private Guid _id = new Guid("B2C3D4E5-6789-ABCD-EF01-23456789ABCE"); // Completely new GUID to clear cache
|
||||
|
||||
public override string Name => "DateSync";
|
||||
|
||||
public override string Description => "Synchronizes TV episode PremiereDate to DateCreated to preserve import dates in Emby";
|
||||
|
||||
public override Guid Id => _id;
|
||||
|
||||
public override string ConfigurationFileName => "datesync.xml";
|
||||
|
||||
public static Plugin Instance { get; private set; }
|
||||
|
||||
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
|
||||
: base(applicationPaths, xmlSerializer)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers machine and checks for automatic subscription activation
|
||||
/// </summary>
|
||||
public async Task<string> RegisterAndCheckSubscriptionAsync(string userEmail, ILogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
var licenseManager = new LicenseManager(logger);
|
||||
|
||||
// Register machine first
|
||||
var registrationResult = await licenseManager.RegisterMachineAsync(userEmail);
|
||||
|
||||
// Then check for existing subscription
|
||||
await licenseManager.CheckAndActivateLicenseAsync();
|
||||
|
||||
return registrationResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Registration error: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets current license status and usage information
|
||||
/// </summary>
|
||||
public string GetLicenseStatus(ILogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
var freeTierManager = new FreeTierManager(logger);
|
||||
var licenseManager = new LicenseManager(logger);
|
||||
var machineId = licenseManager.GetMachineIdentifier();
|
||||
|
||||
if (Configuration.LicenseValid)
|
||||
{
|
||||
return $"✅ Subscription Active\nMachine ID: {machineId}\nStatus: Unlimited processing enabled\n\nSubscription automatically detected via Patreon/PayPal.";
|
||||
}
|
||||
else
|
||||
{
|
||||
var usageInfo = freeTierManager.GetUsageInfo();
|
||||
var trialInfo = licenseManager.IsTrialExpired() ? "Trial expired" : $"Trial active ({Math.Max(0, (Configuration.InstallDate.AddDays(30) - DateTime.UtcNow).Days)} days remaining)";
|
||||
return $"🆓 Free Tier Active\nMachine ID: {machineId}\nUsage: {usageInfo}\nTrial: {trialInfo}\n\nTo unlock unlimited processing:\n• Subscribe: https://patreon.com/nfoguard\n• PayPal: https://nfoguard.com/paypal\n\nActivation is automatic within 24 hours.";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Error getting license status: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually checks for subscription updates
|
||||
/// </summary>
|
||||
public async Task<string> RefreshSubscriptionStatusAsync(ILogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
var licenseManager = new LicenseManager(logger);
|
||||
var wasLicensed = Configuration.LicenseValid;
|
||||
|
||||
await licenseManager.CheckAndActivateLicenseAsync();
|
||||
|
||||
if (!wasLicensed && Configuration.LicenseValid)
|
||||
{
|
||||
return "✅ Subscription found and activated! Unlimited processing enabled.";
|
||||
}
|
||||
else if (wasLicensed && !Configuration.LicenseValid)
|
||||
{
|
||||
return "⚠️ Subscription expired or cancelled. Switched to free tier.";
|
||||
}
|
||||
else if (Configuration.LicenseValid)
|
||||
{
|
||||
return "✅ Subscription is still active.";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "🆓 No active subscription found. Still using free tier.";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Error checking subscription: {ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PluginConfiguration : BasePluginConfiguration
|
||||
{
|
||||
public bool EnableRealTimeSync { get; set; } = true;
|
||||
public bool EnableScheduledTask { get; set; } = true;
|
||||
public bool LogVerbose { get; set; } = true; // Enabled by default for diagnostic version
|
||||
public bool LicenseValid { get; set; } = false;
|
||||
public string RegisteredEmail { get; set; } = string.Empty;
|
||||
public DateTime InstallDate { get; set; } = DateTime.MinValue;
|
||||
public DateTime LastLicenseCheck { get; set; } = DateTime.MinValue;
|
||||
|
||||
// Free tier tracking
|
||||
public DateTime LastProcessingReset { get; set; } = DateTime.MinValue;
|
||||
public int ProcessedThisMonth { get; set; } = 0;
|
||||
public int MonthlyProcessingLimit { get; set; } = 0; // 0 = no limit (full version)
|
||||
|
||||
// Machine registration
|
||||
public bool MachineRegistered { get; set; } = false;
|
||||
public string MachineId { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System.IO;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
|
||||
namespace NFOGuard.Emby.Plugin
|
||||
{
|
||||
public class PluginConfigurationPage : IPluginConfigurationPage
|
||||
{
|
||||
public string Name => "NFOGuard";
|
||||
|
||||
public ConfigurationPageType ConfigurationPageType => ConfigurationPageType.PluginConfiguration;
|
||||
|
||||
public IPlugin Plugin => NFOGuard.Emby.Plugin.Plugin.Instance;
|
||||
|
||||
public Stream GetHtmlStream()
|
||||
{
|
||||
var type = GetType();
|
||||
var stream = type.Assembly.GetManifestResourceStream("NFOGuard.Emby.Plugin.Configuration.configPage.html");
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
// Try alternative resource names
|
||||
stream = type.Assembly.GetManifestResourceStream("NFOGuard.Emby.Plugin.configPage.html");
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
using System;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace NFOGuard.Emby.Plugin
|
||||
{
|
||||
public class PluginEntryPoint : IServerEntryPoint
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IItemRepository _itemRepository;
|
||||
private readonly ILogger _logger;
|
||||
private LibraryEventHandler _eventHandler;
|
||||
|
||||
public PluginEntryPoint(ILibraryManager libraryManager, IItemRepository itemRepository, ILogManager logManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_itemRepository = itemRepository;
|
||||
_logger = logManager.GetLogger("NFOGuard");
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
_logger.Info("NFOGuard :: Plugin starting up");
|
||||
|
||||
try
|
||||
{
|
||||
// Initialize tier settings based on build configuration
|
||||
InitializeTierSettings();
|
||||
|
||||
_eventHandler = new LibraryEventHandler(_libraryManager, _itemRepository, _logger);
|
||||
_logger.Info("NFOGuard :: Event handlers registered successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Failed to initialize event handlers", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeTierSettings()
|
||||
{
|
||||
var freeTierManager = new FreeTierManager(_logger, _libraryManager);
|
||||
var config = Plugin.Instance.Configuration;
|
||||
|
||||
// Initialize as percentage-based free tier by default if not configured
|
||||
if (config.MonthlyProcessingLimit == 0 && !config.LicenseValid)
|
||||
{
|
||||
freeTierManager.SetPercentageBasedLimits(); // 2% episodes, 5% movies
|
||||
_logger.Info("NFOGuard :: Free tier initialized - 2% of TV episodes + 5% of movies per month. Subscribe to unlock unlimited processing.");
|
||||
}
|
||||
else if (config.LicenseValid)
|
||||
{
|
||||
_logger.Info("NFOGuard :: Licensed version active - unlimited processing enabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Info($"NFOGuard :: Free tier active - {freeTierManager.GetUsageInfo()}");
|
||||
}
|
||||
|
||||
// Start periodic license checking
|
||||
StartPeriodicLicenseCheck();
|
||||
}
|
||||
|
||||
private async void StartPeriodicLicenseCheck()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check every 24 hours for subscription changes
|
||||
var timer = new System.Threading.Timer(async _ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var licenseManager = new LicenseManager(_logger);
|
||||
await licenseManager.CheckAndActivateLicenseAsync();
|
||||
|
||||
var config = Plugin.Instance.Configuration;
|
||||
config.LastLicenseCheck = DateTime.UtcNow;
|
||||
Plugin.Instance.SaveConfiguration();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Periodic license check failed", ex);
|
||||
}
|
||||
}, null, TimeSpan.FromHours(1), TimeSpan.FromHours(24)); // First check in 1 hour, then every 24 hours
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("NFOGuard :: Failed to start periodic license check", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_logger.Info("NFOGuard :: Plugin shutting down");
|
||||
_eventHandler?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("NFOGuard Emby Plugin")]
|
||||
[assembly: AssemblyDescription("Synchronizes TV episode PremiereDate to DateCreated to preserve import dates in Emby")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("NFOGuard")]
|
||||
[assembly: AssemblyProduct("NFOGuard.Emby.Plugin")]
|
||||
[assembly: AssemblyCopyright("Copyright © NFOGuard 2025")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("b8a7f9e2-1234-4567-8901-2b3c4d5e6f7a")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,28 +0,0 @@
|
||||
# NFOGuard Emby Plugin
|
||||
|
||||
This is the companion Emby plugin for NFOGuard that automatically synchronizes TV episode PremiereDate to DateCreated to preserve import dates.
|
||||
|
||||
## Features
|
||||
|
||||
- Real-time processing of new and updated TV episodes
|
||||
- Automatically syncs PremiereDate → DateCreated when episodes are added/scanned
|
||||
- Preserves original import dates from NFO files
|
||||
- Logging for debugging and monitoring
|
||||
|
||||
## Installation
|
||||
|
||||
1. Build the plugin DLL
|
||||
2. Copy to Emby's plugins directory
|
||||
3. Restart Emby server
|
||||
4. Plugin will automatically start processing episodes
|
||||
|
||||
## How It Works
|
||||
|
||||
The plugin hooks into Emby's library events (ItemAdded/ItemUpdated) and:
|
||||
|
||||
1. Detects when TV episodes are processed
|
||||
2. Checks if PremiereDate exists and differs from DateCreated
|
||||
3. Updates DateCreated to match PremiereDate
|
||||
4. Saves changes to the Emby database
|
||||
|
||||
This ensures that Emby displays the correct import date (from NFO dateadded) instead of the scan date.
|
||||
@@ -1,131 +0,0 @@
|
||||
# NFOGuard Emby Plugin - WSL Development Setup
|
||||
|
||||
This guide helps you build the NFOGuard Emby plugin on Windows using WSL (Windows Subsystem for Linux).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1. Install .NET SDK in WSL
|
||||
```bash
|
||||
# Update package list
|
||||
sudo apt update
|
||||
|
||||
# Install .NET SDK 6.0 (supports .NET Framework 4.8 development)
|
||||
sudo apt install -y dotnet-sdk-6.0
|
||||
|
||||
# Verify installation
|
||||
dotnet --version
|
||||
```
|
||||
|
||||
### 2. Get Emby SDK Files
|
||||
You need to obtain the Emby SDK DLLs from an Emby Server installation:
|
||||
|
||||
**Option A: From existing Emby Server**
|
||||
1. Locate your Emby Server installation directory:
|
||||
- Windows: `C:\Program Files\Emby-Server\System`
|
||||
- Or wherever you installed Emby Server
|
||||
|
||||
2. Copy these files to `NFOguard/EmbySDK/`:
|
||||
- `MediaBrowser.Common.dll`
|
||||
- `MediaBrowser.Controller.dll`
|
||||
- `MediaBrowser.Model.dll`
|
||||
|
||||
**Option B: Download from official sources**
|
||||
- Check the [Emby Plugin Development docs](https://dev.emby.media/doc/plugins/dev/index.html)
|
||||
|
||||
### 3. Prepare EmbySDK Directory
|
||||
```bash
|
||||
# From the NFOguard directory
|
||||
mkdir -p EmbySDK
|
||||
|
||||
# Copy the DLL files here (use Windows Explorer or copy commands)
|
||||
# You should have:
|
||||
# EmbySDK/MediaBrowser.Common.dll
|
||||
# EmbySDK/MediaBrowser.Controller.dll
|
||||
# EmbySDK/MediaBrowser.Model.dll
|
||||
```
|
||||
|
||||
## Building the Plugin
|
||||
|
||||
### Option 1: Using Build Script (Recommended)
|
||||
```bash
|
||||
cd NFOGuard.Emby.Plugin
|
||||
./build.sh
|
||||
```
|
||||
|
||||
### Option 2: Manual Build
|
||||
```bash
|
||||
cd NFOGuard.Emby.Plugin
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
# Build the project
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**1. "MediaBrowser.* not found" errors:**
|
||||
- Ensure SDK DLLs are in `../EmbySDK/` relative to the .csproj file
|
||||
- Check DLL file names match exactly (case-sensitive on Linux)
|
||||
|
||||
**2. ".NET Framework 4.8 targeting pack" errors:**
|
||||
```bash
|
||||
# Install Mono for .NET Framework support
|
||||
sudo apt install mono-complete
|
||||
```
|
||||
|
||||
**3. Permission errors:**
|
||||
```bash
|
||||
# Make build script executable
|
||||
chmod +x build.sh
|
||||
```
|
||||
|
||||
**4. WSL file system issues:**
|
||||
- Make sure you're working in the Linux filesystem (`/home/...`) not Windows mounts (`/mnt/c/...`) for better performance
|
||||
- If files are on Windows side, they may have permission/line ending issues
|
||||
|
||||
### Verifying Build Success
|
||||
|
||||
After successful build:
|
||||
```bash
|
||||
# Check if DLL was created
|
||||
ls -la bin/Release/net48/NFOGuard.Emby.Plugin.dll
|
||||
|
||||
# Check file size (should be > 0 bytes)
|
||||
file bin/Release/net48/NFOGuard.Emby.Plugin.dll
|
||||
```
|
||||
|
||||
## Deployment to Windows Emby Server
|
||||
|
||||
1. **Locate Emby plugins directory:**
|
||||
- `C:\ProgramData\Emby-Server\plugins`
|
||||
|
||||
2. **Copy from WSL to Windows:**
|
||||
```bash
|
||||
# From WSL, copy to Windows (adjust path as needed)
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll /mnt/c/ProgramData/Emby-Server/plugins/
|
||||
```
|
||||
|
||||
3. **Restart Emby Server**
|
||||
|
||||
4. **Verify in Emby Dashboard:**
|
||||
- Go to Dashboard → Plugins
|
||||
- Look for "NFOGuard" in installed plugins
|
||||
|
||||
## Development Tips
|
||||
|
||||
- Use VS Code in WSL for editing: `code .`
|
||||
- Keep source files in WSL filesystem for best performance
|
||||
- Only copy final DLL to Windows Emby server
|
||||
- Use `tail -f /var/log/emby.log` (or wherever Emby logs are) to monitor plugin activity
|
||||
|
||||
## Next Steps
|
||||
|
||||
Once built and deployed:
|
||||
1. Test with a new TV episode import
|
||||
2. Check Emby logs for NFOGuard messages
|
||||
3. Verify DateCreated syncs to PremiereDate in database
|
||||
4. Configure licensing when ready for production use
|
||||
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.
Binary file not shown.
Binary file not shown.
@@ -1,58 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin - Automatic Subscription System"
|
||||
echo "============================================================"
|
||||
echo "🔥 Like TimeLord - No manual keys, automatic activation!"
|
||||
echo ""
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
echo "Building automatic subscription version..."
|
||||
|
||||
# Build unified version with automatic licensing
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Create version directory
|
||||
mkdir -p ../releases/v2.0.0-automatic
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v2.0.0-automatic/
|
||||
|
||||
# Copy to main plugin-release directory
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../plugin-release/
|
||||
|
||||
echo "✅ Automatic subscription version built successfully"
|
||||
echo ""
|
||||
echo "🎉 NFOGuard v2.0.0-automatic ready!"
|
||||
echo ""
|
||||
echo "🔧 How It Works:"
|
||||
echo "1. Plugin installs with percentage-based free tier:"
|
||||
echo " • 2% of your TV episodes per month"
|
||||
echo " • 5% of your movies per month"
|
||||
echo " • Scheduled processing only (no real-time)"
|
||||
echo ""
|
||||
echo "2. Users register with email (optional but recommended)"
|
||||
echo "3. Subscribe via Patreon or PayPal recurring"
|
||||
echo "4. Plugin automatically detects subscription within 24hrs"
|
||||
echo "5. Unlimited processing + real-time sync unlocked"
|
||||
echo ""
|
||||
echo "💳 Payment Options:"
|
||||
echo "• Patreon: https://patreon.com/nfoguard (recommended)"
|
||||
echo "• PayPal Recurring: https://nfoguard.com/paypal"
|
||||
echo ""
|
||||
echo "🔒 Security Features:"
|
||||
echo "• Machine ID based activation (harder to crack)"
|
||||
echo "• Server-side subscription validation"
|
||||
echo "• Automatic deactivation if subscription expires"
|
||||
echo "• Offline mode graceful fallback to free tier"
|
||||
echo ""
|
||||
echo "📊 API Endpoints Needed (nfoguard.com):"
|
||||
echo "• POST /api/register - Machine registration"
|
||||
echo "• GET /api/patreon/check?machine_id=XXX - Patreon validation"
|
||||
echo "• GET /api/paypal/check?machine_id=XXX - PayPal validation"
|
||||
echo ""
|
||||
echo "File ready: releases/v2.0.0-automatic/NFOGuard.Emby.Plugin.dll"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building DateSync Emby Plugin - Clean Version"
|
||||
echo "============================================="
|
||||
echo "Completely different plugin name to avoid Emby cache issues"
|
||||
echo ""
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
echo "Building clean version with new name..."
|
||||
|
||||
# Build clean version
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Create version directory
|
||||
mkdir -p ../releases/v1.0.4-clean
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.0.4-clean/DateSync.Emby.Plugin.dll
|
||||
|
||||
# Copy to main plugin-release directory with new name
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../plugin-release/DateSync.Emby.Plugin.dll
|
||||
|
||||
echo "✅ Clean version built successfully"
|
||||
echo ""
|
||||
echo "🧹 DateSync v1.0.4-clean ready!"
|
||||
echo ""
|
||||
echo "IMPORTANT CHANGES:"
|
||||
echo "• Plugin name: NFOGuard → DateSync"
|
||||
echo "• Config file: nfoguard.xml → datesync.xml"
|
||||
echo "• Task name: NFOGuard Date Sync → DateSync Task"
|
||||
echo "• DLL name: DateSync.Emby.Plugin.dll"
|
||||
echo "• New GUID: Completely different from NFOGuard"
|
||||
echo ""
|
||||
echo "This should avoid ALL cached references!"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo "1. Stop Emby Server"
|
||||
echo "2. DELETE old NFOGuard.Emby.Plugin.dll completely"
|
||||
echo "3. DELETE /config/data/nfoguard.xml if exists"
|
||||
echo "4. Clear browser cache completely"
|
||||
echo "5. Install DateSync.Emby.Plugin.dll"
|
||||
echo "6. Start Emby Server"
|
||||
echo "7. Look for 'DateSync Task' in scheduled tasks"
|
||||
echo ""
|
||||
echo "Files ready:"
|
||||
echo "• releases/v1.0.4-clean/DateSync.Emby.Plugin.dll"
|
||||
echo "• plugin-release/DateSync.Emby.Plugin.dll (for deployment)"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin - Debug Version"
|
||||
echo "============================================="
|
||||
echo "Clean diagnostic version with debug enabled by default"
|
||||
echo ""
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
echo "Building debug version..."
|
||||
|
||||
# Build debug version
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Create version directory
|
||||
mkdir -p ../releases/v1.0.2-debug
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.0.2-debug/
|
||||
|
||||
# Copy to main plugin-release directory for easy deployment
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../plugin-release/
|
||||
|
||||
echo "✅ Debug version built successfully"
|
||||
echo ""
|
||||
echo "🔍 NFOGuard v1.0.2-debug ready!"
|
||||
echo ""
|
||||
echo "Features:"
|
||||
echo "• Debug logging ENABLED by default"
|
||||
echo "• No configuration page (no crashes)"
|
||||
echo "• Enhanced episode/movie date diagnostics"
|
||||
echo "• Shows PremiereDate vs DateCreated for every item"
|
||||
echo "• Explains why episodes are skipped"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo "1. Replace plugin DLL and restart Emby"
|
||||
echo "2. Run 'NFOGuard Date Sync' scheduled task"
|
||||
echo "3. Check Emby logs for detailed output"
|
||||
echo ""
|
||||
echo "Files ready:"
|
||||
echo "• releases/v1.0.2-debug/NFOGuard.Emby.Plugin.dll"
|
||||
echo "• plugin-release/NFOGuard.Emby.Plugin.dll (for deployment)"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin - Safe Diagnostic Version"
|
||||
echo "======================================================="
|
||||
echo "Enhanced logging WITHOUT configuration page (to avoid crashes)"
|
||||
echo ""
|
||||
|
||||
# Disable configuration page temporarily
|
||||
mv PluginConfigurationPage.cs PluginConfigurationPage.cs.disabled 2>/dev/null || true
|
||||
|
||||
# Comment out the config page in project file
|
||||
sed -i 's|<EmbeddedResource Include="Configuration\\configPage.html" />|<!-- <EmbeddedResource Include="Configuration\\configPage.html" /> -->|' NFOGuard.Emby.Plugin.csproj
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
echo "Building safe diagnostic version (no config page)..."
|
||||
|
||||
# Build diagnostic version
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Create version directory
|
||||
mkdir -p ../releases/v1.0.1-diagnostic-safe
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.0.1-diagnostic-safe/
|
||||
|
||||
echo "✅ Safe diagnostic version built successfully"
|
||||
echo ""
|
||||
echo "🔍 NFOGuard v1.0.1-diagnostic-safe ready!"
|
||||
echo ""
|
||||
echo "This version includes:"
|
||||
echo "• Enhanced diagnostic logging"
|
||||
echo "• NO configuration page (avoids crashes)"
|
||||
echo "• LogVerbose is ENABLED by default"
|
||||
echo "• All other settings use defaults"
|
||||
echo ""
|
||||
echo "Safe to use without risk of configuration crashes!"
|
||||
echo ""
|
||||
echo "File ready: releases/v1.0.1-diagnostic-safe/NFOGuard.Emby.Plugin.dll"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
fi
|
||||
|
||||
# Restore configuration page files
|
||||
mv PluginConfigurationPage.cs.disabled PluginConfigurationPage.cs 2>/dev/null || true
|
||||
sed -i 's|<!-- <EmbeddedResource Include="Configuration\\configPage.html" /> -->|<EmbeddedResource Include="Configuration\\configPage.html" />|' NFOGuard.Emby.Plugin.csproj
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin - Diagnostic Version"
|
||||
echo "==================================================="
|
||||
echo "Enhanced logging to diagnose NFO date reading issue"
|
||||
echo ""
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
echo "Building diagnostic version..."
|
||||
|
||||
# Build diagnostic version
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Create version directory
|
||||
mkdir -p ../releases/v1.0.1-diagnostic
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.0.1-diagnostic/
|
||||
|
||||
# Copy to main plugin-release directory
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../plugin-release/
|
||||
|
||||
echo "✅ Diagnostic version built successfully"
|
||||
echo ""
|
||||
echo "🔍 NFOGuard v1.0.1-diagnostic ready!"
|
||||
echo ""
|
||||
echo "This version includes enhanced logging to show:"
|
||||
echo "• PremiereDate values for every episode/movie"
|
||||
echo "• DateCreated values for comparison"
|
||||
echo "• Reason why episodes are skipped (no PremiereDate vs already synced)"
|
||||
echo ""
|
||||
echo "To use:"
|
||||
echo "1. Replace your current plugin DLL"
|
||||
echo "2. Enable 'LogVerbose' in plugin settings"
|
||||
echo "3. Run the scheduled task"
|
||||
echo "4. Check Emby logs for detailed date information"
|
||||
echo ""
|
||||
echo "File ready: releases/v1.0.1-diagnostic/NFOGuard.Emby.Plugin.dll"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin - Minimal Version"
|
||||
echo "==============================================="
|
||||
echo "No web components, no thumbnails, no config pages - just core functionality"
|
||||
echo ""
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
echo "Building minimal version..."
|
||||
|
||||
# Build minimal version
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Create version directory
|
||||
mkdir -p ../releases/v1.0.3-minimal
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.0.3-minimal/
|
||||
|
||||
# Copy to main plugin-release directory for easy deployment
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../plugin-release/
|
||||
|
||||
echo "✅ Minimal version built successfully"
|
||||
echo ""
|
||||
echo "🔍 NFOGuard v1.0.3-minimal ready!"
|
||||
echo ""
|
||||
echo "Features:"
|
||||
echo "• Debug logging ENABLED by default"
|
||||
echo "• NO web components (no UI loading issues)"
|
||||
echo "• NO configuration page"
|
||||
echo "• NO thumbnails or embedded resources"
|
||||
echo "• Core functionality only"
|
||||
echo "• New GUID to clear Emby cache completely"
|
||||
echo ""
|
||||
echo "This version should NOT cause web UI loading issues!"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo "1. Remove old plugin DLL completely"
|
||||
echo "2. Clear browser cache"
|
||||
echo "3. Install new minimal DLL and restart Emby"
|
||||
echo "4. Web UI should load normally"
|
||||
echo "5. Run 'NFOGuard Date Sync' scheduled task"
|
||||
echo ""
|
||||
echo "Files ready:"
|
||||
echo "• releases/v1.0.3-minimal/NFOGuard.Emby.Plugin.dll"
|
||||
echo "• plugin-release/NFOGuard.Emby.Plugin.dll (for deployment)"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin - Unified Version"
|
||||
echo "==============================================="
|
||||
echo "Single DLL with license key activation system"
|
||||
echo ""
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
echo "Building unified version..."
|
||||
|
||||
# Build unified version without any defines
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Create version directory
|
||||
mkdir -p ../releases/v1.3.0-unified
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.3.0-unified/
|
||||
|
||||
# Copy to main plugin-release directory
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../plugin-release/
|
||||
|
||||
echo "✅ Unified version built successfully"
|
||||
echo ""
|
||||
echo "🎉 NFOGuard v1.3.0-unified ready!"
|
||||
echo ""
|
||||
echo "Features:"
|
||||
echo "🆓 Free Tier: 500 items/month, scheduled processing only"
|
||||
echo "🔑 License Activation: Unlimited processing with valid license key"
|
||||
echo "💳 Payment Integration: Supports PayPal/Patreon transaction IDs"
|
||||
echo "📊 Usage Tracking: Monthly quota with automatic reset"
|
||||
echo ""
|
||||
echo "License Key Formats Supported:"
|
||||
echo "- NFOG-XXXX-XXXX-XXXX (generated keys)"
|
||||
echo "- PAYPAL-[transaction-id] (PayPal payments)"
|
||||
echo "- PATREON-[transaction-id] (Patreon subscriptions)"
|
||||
echo ""
|
||||
echo "File ready: releases/v1.3.0-unified/NFOGuard.Emby.Plugin.dll"
|
||||
else
|
||||
echo "❌ Build failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin - All Versions"
|
||||
echo "============================================="
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj releases-temp
|
||||
|
||||
# Create temporary releases directory
|
||||
mkdir -p releases-temp
|
||||
|
||||
echo ""
|
||||
echo "1. Building Free Tier Version (v1.1.0-free-tier)"
|
||||
echo "================================================="
|
||||
|
||||
# Build free tier version with FREE_TIER define
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48 -p:DefineConstants="FREE_TIER"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
mkdir -p ../releases/v1.1.0-free-tier
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.1.0-free-tier/
|
||||
echo "✅ Free tier version built successfully"
|
||||
else
|
||||
echo "❌ Free tier build failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "2. Building Full Version (v1.2.0-production)"
|
||||
echo "============================================"
|
||||
|
||||
# Clean for full version build
|
||||
rm -rf bin obj
|
||||
|
||||
# Build full version without FREE_TIER define
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
mkdir -p ../releases/v1.2.0-production
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../releases/v1.2.0-production/
|
||||
echo "✅ Production version built successfully"
|
||||
else
|
||||
echo "❌ Production build failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "3. Updating plugin-release with latest production version"
|
||||
echo "========================================================"
|
||||
cp bin/Release/net48/NFOGuard.Emby.Plugin.dll ../plugin-release/
|
||||
|
||||
echo ""
|
||||
echo "🎉 All versions built successfully!"
|
||||
echo ""
|
||||
echo "Available versions:"
|
||||
echo "📦 v1.0.0-working : Original working version (unlimited)"
|
||||
echo "🆓 v1.1.0-free-tier : Free tier (500 items/month, no real-time)"
|
||||
echo "💎 v1.2.0-production : Full version (unlimited, all features)"
|
||||
echo ""
|
||||
echo "Files ready in releases/ directory"
|
||||
@@ -1,41 +0,0 @@
|
||||
@echo off
|
||||
echo Building NFOGuard Emby Plugin...
|
||||
|
||||
REM Check if MSBuild is available
|
||||
where msbuild >nul 2>&1
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
echo ERROR: MSBuild not found. Please install Visual Studio Build Tools or use Developer Command Prompt.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM Clean previous builds
|
||||
if exist "bin" rmdir /s /q "bin"
|
||||
if exist "obj" rmdir /s /q "obj"
|
||||
|
||||
REM Build the project
|
||||
msbuild NFOGuard.Emby.Plugin.csproj /p:Configuration=Release /p:Platform="Any CPU"
|
||||
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
echo.
|
||||
echo Build successful!
|
||||
echo Plugin DLL location: bin\Release\net48\NFOGuard.Emby.Plugin.dll
|
||||
echo.
|
||||
|
||||
REM Create plugin-release directory in repo root and copy DLL
|
||||
if not exist "..\plugin-release" mkdir "..\plugin-release"
|
||||
copy "bin\Release\net48\NFOGuard.Emby.Plugin.dll" "..\plugin-release\"
|
||||
|
||||
echo Plugin copied to: ..\plugin-release\NFOGuard.Emby.Plugin.dll
|
||||
echo.
|
||||
echo To install:
|
||||
echo 1. Copy plugin-release\NFOGuard.Emby.Plugin.dll to your Emby plugins folder
|
||||
echo 2. Restart Emby Server
|
||||
echo.
|
||||
) else (
|
||||
echo.
|
||||
echo Build failed! Check the output above for errors.
|
||||
echo.
|
||||
)
|
||||
|
||||
pause
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Building NFOGuard Emby Plugin..."
|
||||
|
||||
# Check if we need to install .NET Framework support
|
||||
if ! command -v dotnet &> /dev/null; then
|
||||
echo "ERROR: .NET SDK not found. Installing..."
|
||||
echo "Run: sudo apt update && sudo apt install -y dotnet-sdk-6.0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create EmbySDK directory if it doesn't exist
|
||||
if [ ! -d "../EmbySDK" ]; then
|
||||
echo "Creating EmbySDK directory..."
|
||||
mkdir -p ../EmbySDK
|
||||
echo ""
|
||||
echo "IMPORTANT: You need to copy Emby SDK DLLs to ../EmbySDK/"
|
||||
echo "Required files:"
|
||||
echo " - MediaBrowser.Common.dll"
|
||||
echo " - MediaBrowser.Controller.dll"
|
||||
echo " - MediaBrowser.Model.dll"
|
||||
echo ""
|
||||
echo "These can be found in your Emby Server installation directory."
|
||||
echo "Continue? (y/n)"
|
||||
read -r answer
|
||||
if [[ $answer != "y" ]]; then
|
||||
echo "Build cancelled. Please copy SDK files first."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean previous builds
|
||||
rm -rf bin obj
|
||||
|
||||
# Try building with dotnet first (requires SDK), fall back to msbuild
|
||||
if command -v dotnet &> /dev/null; then
|
||||
echo "Building with .NET CLI..."
|
||||
dotnet build NFOGuard.Emby.Plugin.csproj -c Release -f net48
|
||||
elif command -v msbuild &> /dev/null; then
|
||||
echo "Building with MSBuild..."
|
||||
msbuild NFOGuard.Emby.Plugin.csproj /p:Configuration=Release /p:Platform="Any CPU"
|
||||
else
|
||||
echo "ERROR: No build tool found. Install .NET SDK or Mono with MSBuild."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo
|
||||
echo "Build successful!"
|
||||
echo "Plugin DLL location: bin/Release/net48/NFOGuard.Emby.Plugin.dll"
|
||||
echo
|
||||
|
||||
# Create plugin-release directory in repo root and copy DLL
|
||||
PLUGIN_RELEASE_DIR="../plugin-release"
|
||||
mkdir -p "$PLUGIN_RELEASE_DIR"
|
||||
cp "bin/Release/net48/NFOGuard.Emby.Plugin.dll" "$PLUGIN_RELEASE_DIR/"
|
||||
|
||||
echo "Plugin copied to: $PLUGIN_RELEASE_DIR/NFOGuard.Emby.Plugin.dll"
|
||||
echo
|
||||
echo "To install:"
|
||||
echo "1. Copy plugin-release/NFOGuard.Emby.Plugin.dll to your Emby plugins folder"
|
||||
echo "2. Restart Emby Server"
|
||||
echo
|
||||
else
|
||||
echo
|
||||
echo "Build failed! Check the output above for errors."
|
||||
echo
|
||||
fi
|
||||
@@ -1,70 +0,0 @@
|
||||
{
|
||||
"format": 1,
|
||||
"restore": {
|
||||
"/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/NFOGuard.Emby.Plugin.csproj": {}
|
||||
},
|
||||
"projects": {
|
||||
"/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/NFOGuard.Emby.Plugin.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/NFOGuard.Emby.Plugin.csproj",
|
||||
"projectName": "NFOGuard.Emby.Plugin",
|
||||
"projectPath": "/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/NFOGuard.Emby.Plugin.csproj",
|
||||
"packagesPath": "/home/jskala/.nuget/packages/",
|
||||
"outputPath": "/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/obj/",
|
||||
"projectStyle": "PackageReference",
|
||||
"configFilePaths": [
|
||||
"/home/jskala/.nuget/NuGet/NuGet.Config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net48"
|
||||
],
|
||||
"sources": {
|
||||
"https://api.nuget.org/v3/index.json": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net48": {
|
||||
"targetAlias": "net48",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
}
|
||||
},
|
||||
"frameworks": {
|
||||
"net48": {
|
||||
"targetAlias": "net48",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"suppressParent": "All",
|
||||
"target": "Package",
|
||||
"version": "[1.0.3, )",
|
||||
"autoReferenced": true
|
||||
},
|
||||
"System.Memory": {
|
||||
"target": "Package",
|
||||
"version": "[4.5.5, )"
|
||||
},
|
||||
"System.Net.Http": {
|
||||
"target": "Package",
|
||||
"version": "[4.3.4, )"
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"target": "Package",
|
||||
"version": "[6.0.0, )"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/8.0.414/RuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/home/jskala/.nuget/packages/</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/home/jskala/.nuget/packages/</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.11.1</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="/home/jskala/.nuget/packages/" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)system.text.json/6.0.0/build/System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json/6.0.0/build/System.Text.Json.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.netframework.referenceassemblies.net48/1.0.3/build/Microsoft.NETFramework.ReferenceAssemblies.net48.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.netframework.referenceassemblies.net48/1.0.3/build/Microsoft.NETFramework.ReferenceAssemblies.net48.targets')" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
is_global = true
|
||||
build_property.RootNamespace = NFOGuard.Emby.Plugin
|
||||
build_property.ProjectDir = /home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/
|
||||
build_property.EnableComHosting =
|
||||
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||
Binary file not shown.
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
ac9b1d05d1517a7c47b07fdf8111e5c0cb41b1f211b4f061f560a7f28e99067e
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/NFOGuard.Emby.Plugin.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/NFOGuard.Emby.Plugin.pdb
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/Microsoft.Bcl.AsyncInterfaces.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.Buffers.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.Memory.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.Numerics.Vectors.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.Runtime.CompilerServices.Unsafe.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.Text.Encodings.Web.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.Text.Json.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.Threading.Tasks.Extensions.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/bin/Release/net48/System.ValueTuple.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/obj/Release/net48/NFOGuard.Emby.Plugin.csproj.AssemblyReference.cache
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/obj/Release/net48/NFOGuard.Emby.Plugin.GeneratedMSBuildEditorConfig.editorconfig
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/obj/Release/net48/NFOGuard.Emby.Plugin.csproj.CoreCompileInputs.cache
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/obj/Release/net48/NFOGuard.F23561FE.Up2Date
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/obj/Release/net48/NFOGuard.Emby.Plugin.dll
|
||||
/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/obj/Release/net48/NFOGuard.Emby.Plugin.pdb
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "1tbmKnFkad0=",
|
||||
"success": true,
|
||||
"projectFilePath": "/home/jskala/github/NFOguard/NFOGuard.Emby.Plugin/NFOGuard.Emby.Plugin.csproj",
|
||||
"expectedPackageFiles": [
|
||||
"/home/jskala/.nuget/packages/microsoft.bcl.asyncinterfaces/6.0.0/microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/microsoft.netframework.referenceassemblies/1.0.3/microsoft.netframework.referenceassemblies.1.0.3.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/microsoft.netframework.referenceassemblies.net48/1.0.3/microsoft.netframework.referenceassemblies.net48.1.0.3.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.buffers/4.5.1/system.buffers.4.5.1.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.io/4.3.0/system.io.4.3.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.memory/4.5.5/system.memory.4.5.5.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.net.http/4.3.4/system.net.http.4.3.4.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.numerics.vectors/4.5.0/system.numerics.vectors.4.5.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.runtime/4.3.0/system.runtime.4.3.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.runtime.compilerservices.unsafe/6.0.0/system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.security.cryptography.algorithms/4.3.0/system.security.cryptography.algorithms.4.3.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.security.cryptography.encoding/4.3.0/system.security.cryptography.encoding.4.3.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.security.cryptography.primitives/4.3.0/system.security.cryptography.primitives.4.3.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.security.cryptography.x509certificates/4.3.0/system.security.cryptography.x509certificates.4.3.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.text.encodings.web/6.0.0/system.text.encodings.web.6.0.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.text.json/6.0.0/system.text.json.6.0.0.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.threading.tasks.extensions/4.5.4/system.threading.tasks.extensions.4.5.4.nupkg.sha512",
|
||||
"/home/jskala/.nuget/packages/system.valuetuple/4.5.0/system.valuetuple.4.5.0.nupkg.sha512"
|
||||
],
|
||||
"logs": [
|
||||
{
|
||||
"code": "NU1903",
|
||||
"level": "Warning",
|
||||
"warningLevel": 1,
|
||||
"message": "Package 'System.Text.Json' 6.0.0 has a known high severity vulnerability, https://github.com/advisories/GHSA-8g4q-xg66-9fp4",
|
||||
"libraryId": "System.Text.Json",
|
||||
"targetGraphs": [
|
||||
".NETFramework,Version=v4.8"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PluginInfo>
|
||||
<Name>NFOGuard</Name>
|
||||
<Description>Synchronizes TV episode PremiereDate to DateCreated to preserve import dates in Emby</Description>
|
||||
<Overview>This plugin automatically syncs PremiereDate to DateCreated for TV episodes to ensure Emby displays the correct import date from NFO files instead of scan dates.</Overview>
|
||||
<Version>1.0.0.0</Version>
|
||||
<MinServerVersion>4.7.0.0</MinServerVersion>
|
||||
<MaxServerVersion>4.8.99.99</MaxServerVersion>
|
||||
<Author>NFOGuard Team</Author>
|
||||
<Category>General</Category>
|
||||
<ImageUrl>thumb.png</ImageUrl>
|
||||
<PluginUrl>https://github.com/NFOguard/NFOguard</PluginUrl>
|
||||
<IsPremium>false</IsPremium>
|
||||
<IsHidden>false</IsHidden>
|
||||
<TargetFilename>NFOGuard.Emby.Plugin.dll</TargetFilename>
|
||||
<SourceUrl>https://github.com/NFOguard/NFOguard/releases/latest/NFOGuard.Emby.Plugin.dll</SourceUrl>
|
||||
<Changelog>
|
||||
<Version number="1.0.0.0">
|
||||
<Description>Initial release - TV episode date synchronization</Description>
|
||||
</Version>
|
||||
</Changelog>
|
||||
</PluginInfo>
|
||||
@@ -1,12 +1,13 @@
|
||||
# NFOGuard
|
||||
|
||||
|
||||
*** Alpha in progress things are volatile use at your own risk ***
|
||||
|
||||
**NFOGuard** is a lightweight webhook service that locks in the *true import date* of your movies and TV episodes, ensuring that upgrades, renames, or reimports never bubble old media up as “new” in Emby, Jellyfin, or Plex.
|
||||
**NFOGuard** is a lightweight webhook service that locks in the *true import date* of your movies and TV episodes, ensuring that upgrades, renames, or reimports never bubble old media up as "new" in Emby, Jellyfin, or Plex.
|
||||
|
||||
It integrates with **Sonarr** and **Radarr**, listens for import/rename/upgrade events, and automatically manages `.nfo` files and filesystem timestamps to keep your library chronology consistent.
|
||||
|
||||
## Companion Plugin
|
||||
|
||||
For **Emby users**, check out the companion [NFOGuard Emby Plugin](https://github.com/jskala/NFOGuard-Emby-Plugin) that provides real-time date synchronization within Emby itself.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# NFOGuard Webhook Repository Structure
|
||||
|
||||
This document shows the clean structure for the NFOGuard webhook Docker application repository.
|
||||
|
||||
## Core Application Files
|
||||
```
|
||||
nfoguard.py # Main webhook server application
|
||||
requirements.txt # Python dependencies
|
||||
Dockerfile # Container build configuration
|
||||
VERSION # Version tracking
|
||||
```
|
||||
|
||||
## Source Code Modules
|
||||
```
|
||||
core/
|
||||
├── database.py # SQLite database management
|
||||
├── nfo_manager.py # NFO file creation and management
|
||||
├── path_mapper.py # Path mapping between containers
|
||||
└── logging.py # Logging configuration
|
||||
|
||||
clients/
|
||||
├── external_clients.py # TMDB, OMDb, TVDB, Jellyseerr API clients
|
||||
├── radarr_client.py # Radarr API client
|
||||
├── radarr_db_client.py # Radarr database client
|
||||
└── sonarr_client.py # Sonarr API client
|
||||
```
|
||||
|
||||
## Configuration & Deployment
|
||||
```
|
||||
docker-compose.yml # Public example docker-compose (uses published image)
|
||||
.env.template # Environment configuration template
|
||||
.env.secrets.template # API keys template (includes TVDB_API_KEY)
|
||||
```
|
||||
|
||||
## Documentation
|
||||
```
|
||||
README.md # Main documentation (mentions companion plugin)
|
||||
DEPLOYMENT.md # Deployment guide
|
||||
SETUP.md # Setup instructions
|
||||
TESTING.md # Testing guide
|
||||
CHANGELOG.md # Version history
|
||||
LICENSE # MIT license
|
||||
SUMMARY.md # Project summary
|
||||
projectsummary.md # Project overview
|
||||
```
|
||||
|
||||
## Development & Utilities
|
||||
```
|
||||
test_movie_scan.py # Movie scanning tests
|
||||
test_end_to_end.py # End-to-end integration tests
|
||||
test_bulk_update.py # Bulk update testing
|
||||
bulk_update_movies.py # Movie bulk update utility
|
||||
```
|
||||
|
||||
## Files NOT in Repository
|
||||
- Plugin source code (moved to NFOGuard-Emby-Plugin repo)
|
||||
- CI/registry configuration files
|
||||
- Build artifacts
|
||||
- Development docker-compose files
|
||||
- Registry troubleshooting files
|
||||
|
||||
## Total: Clean, focused webhook Docker application repository
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Script to clean up CI/development files
|
||||
|
||||
echo "Removing CI/development artifacts..."
|
||||
|
||||
rm -f gitea-registry-troubleshooting.md
|
||||
rm -f gitea-packages-config.ini
|
||||
rm -f find-gitea-local-ip.sh
|
||||
rm -f example-multi-registry-workflow.yml
|
||||
rm -f example-docker-compose.yml
|
||||
rm -f example-distribution-README.md
|
||||
rm -f docker-compose.example.yml
|
||||
rm -f cleanup-github.sh
|
||||
rm -f check-gitea-registry.sh
|
||||
|
||||
echo "Cleanup complete!"
|
||||
echo "Removed CI/registry-specific files from webhook repository"
|
||||
@@ -1,246 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace Emby.Security;
|
||||
|
||||
public class PluginSecurityManager : IServerEntryPoint, IDisposable
|
||||
{
|
||||
private const string MBValidateUrl = "https://mb3admin.com/admin/service/registration/validate";
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly ICryptoProvider _cryptographyProvider;
|
||||
|
||||
private readonly ISecurityManager _securityManager;
|
||||
|
||||
private readonly IEncryptionManager _encryption;
|
||||
|
||||
public static PluginSecurityManager Current;
|
||||
|
||||
private readonly Dictionary<string, RegRecord> _regRecordCache = new Dictionary<string, RegRecord>();
|
||||
|
||||
private SemaphoreSlim _regLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, ISecurityManager securityManager, IEncryptionManager encryption)
|
||||
{
|
||||
if (httpClient == null)
|
||||
{
|
||||
throw new ArgumentNullException("httpClient");
|
||||
}
|
||||
_appHost = appHost;
|
||||
_httpClient = httpClient;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_cryptographyProvider = cryptographyProvider;
|
||||
_securityManager = securityManager;
|
||||
_encryption = encryption;
|
||||
_logger = logManager.GetLogger("Plugin");
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private string GetCacheFilePath(string feature)
|
||||
{
|
||||
string text = GetType().FullName + GetCacheKey(feature);
|
||||
text = BaseExtensions.GetMD5(text).ToString("N");
|
||||
return Path.Combine(_appPaths.PluginConfigurationsPath, text);
|
||||
}
|
||||
|
||||
private RegRecord GetCachedRegRecord(string feature)
|
||||
{
|
||||
lock (_regRecordCache)
|
||||
{
|
||||
if (_regRecordCache.TryGetValue(GetCacheKey(feature), out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
string cacheFilePath = GetCacheFilePath(feature);
|
||||
try
|
||||
{
|
||||
string text = _encryption.DecryptString(_fileSystem.ReadAllText(cacheFilePath));
|
||||
RegRecord value = _jsonSerializer.DeserializeFromString<RegRecord>(text);
|
||||
if (value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetCacheKey(string feature)
|
||||
{
|
||||
string text = _securityManager.SupporterKey;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
text = "none";
|
||||
}
|
||||
return text + "-" + feature;
|
||||
}
|
||||
|
||||
private void DeleteCachedRecord(string feature)
|
||||
{
|
||||
lock (_regRecordCache)
|
||||
{
|
||||
_regRecordCache.Remove(GetCacheKey(feature));
|
||||
}
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteFile(GetCacheFilePath(feature));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveCachedRecord(string feature, RegRecord record)
|
||||
{
|
||||
lock (_regRecordCache)
|
||||
{
|
||||
_regRecordCache[GetCacheKey(feature)] = record;
|
||||
}
|
||||
string cacheFilePath = GetCacheFilePath(feature);
|
||||
string text = _jsonSerializer.SerializeToString((object)record);
|
||||
string text2 = _encryption.EncryptString(text);
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
|
||||
_fileSystem.WriteAllText(cacheFilePath, text2);
|
||||
}
|
||||
|
||||
public async Task<RegRecord> GetRegistrationStatus(string feature, CancellationToken cancellationToken)
|
||||
{
|
||||
await _regLock.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
try
|
||||
{
|
||||
return await GetRegistrationStatusInternal(feature, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_regLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckRegistrationWithServer(DateTime lastChecked, bool registered)
|
||||
{
|
||||
if (registered || string.IsNullOrWhiteSpace(_securityManager.SupporterKey))
|
||||
{
|
||||
if (DateTime.UtcNow.AddDays(-1.0) > lastChecked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (DateTime.UtcNow.AddHours(-1.0) > lastChecked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<RegRecord> GetRegistrationStatusInternal(string feature, CancellationToken cancellationToken)
|
||||
{
|
||||
RegRecord regInfo = GetCachedRegRecord(feature);
|
||||
DateTime lastChecked = regInfo?.lastChecked ?? DateTime.MinValue;
|
||||
DateTime expDate = regInfo?.expDate ?? DateTime.MinValue;
|
||||
bool registered = regInfo?.registered ?? false;
|
||||
string supporterKey = _securityManager.SupporterKey;
|
||||
if (CheckRegistrationWithServer(lastChecked, registered))
|
||||
{
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>
|
||||
{
|
||||
{ "feature", feature },
|
||||
{ "key", supporterKey },
|
||||
{
|
||||
"mac",
|
||||
((IApplicationHost)_appHost).SystemId
|
||||
},
|
||||
{
|
||||
"systemid",
|
||||
((IApplicationHost)_appHost).SystemId
|
||||
},
|
||||
{
|
||||
"platform",
|
||||
((IApplicationHost)_appHost).OperatingSystemDisplayName
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
HttpRequestOptions val = new HttpRequestOptions
|
||||
{
|
||||
Url = "https://mb3admin.com/admin/service/registration/validate",
|
||||
EnableHttpCompression = false,
|
||||
BufferContent = false,
|
||||
CancellationToken = cancellationToken
|
||||
};
|
||||
val.SetPostData((IDictionary<string, string>)postData);
|
||||
HttpResponseInfo val2 = await _httpClient.SendAsync(val, "POST").ConfigureAwait(continueOnCapturedContext: false);
|
||||
try
|
||||
{
|
||||
using Stream stream = val2.Content;
|
||||
regInfo = _jsonSerializer.DeserializeFromStream<RegRecord>(stream);
|
||||
regInfo.lastChecked = DateTime.UtcNow;
|
||||
SaveCachedRecord(feature, regInfo);
|
||||
}
|
||||
finally
|
||||
{
|
||||
((IDisposable)val2)?.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error checking registration status of {0}", ex, new object[1] { feature });
|
||||
int num = 14;
|
||||
DateTime dateTime = new DateTime[2]
|
||||
{
|
||||
expDate,
|
||||
lastChecked.AddDays(num)
|
||||
}.Min();
|
||||
if (dateTime > DateTime.UtcNow.AddDays(num))
|
||||
{
|
||||
dateTime = DateTime.MinValue;
|
||||
}
|
||||
regInfo = new RegRecord
|
||||
{
|
||||
registered = (regInfo != null && regInfo.registered && dateTime >= DateTime.UtcNow && expDate >= DateTime.UtcNow),
|
||||
expDate = expDate
|
||||
};
|
||||
}
|
||||
}
|
||||
return regInfo;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Emby.Security;
|
||||
|
||||
public class RegRecord
|
||||
{
|
||||
public string featId { get; set; }
|
||||
|
||||
public bool registered { get; set; }
|
||||
|
||||
public DateTime expDate { get; set; }
|
||||
|
||||
public string key { get; set; }
|
||||
|
||||
public DateTime lastChecked { get; set; }
|
||||
|
||||
public bool isTrial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (expDate > DateTime.UtcNow)
|
||||
{
|
||||
return !registered;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool isValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isTrial)
|
||||
{
|
||||
return registered;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
-258
@@ -1,258 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Security;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Notifications;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Plugins.TimeLord.ScheduledTasks;
|
||||
|
||||
public class TLTVScheduledTask : IScheduledTask
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
private readonly ILibraryMonitor _libraryMonitor;
|
||||
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly INotificationManager _notificationManager;
|
||||
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
public string Category => "Time Lord";
|
||||
|
||||
public string Key => "TimeLordTV";
|
||||
|
||||
public string Description => "Sets the Episode Date Added to the original airdate (Episodes)";
|
||||
|
||||
public string Name => "Change episode date added to original airdate";
|
||||
|
||||
public TLTVScheduledTask(ILibraryManager libraryManager, ILogger logger, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IApplicationPaths appPaths, ILibraryMonitor libraryMonitor, ISecurityManager securityManager, IUserManager userManager, INotificationManager notificationManager, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_fileSystem = fileSystem;
|
||||
_appPaths = appPaths;
|
||||
_libraryMonitor = libraryMonitor;
|
||||
_notificationManager = notificationManager;
|
||||
_itemRepo = itemRepo;
|
||||
}
|
||||
|
||||
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
RegRecord registration = await PluginSecurityManager.Current.GetRegistrationStatus("TimeLord", cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
Plugin.Instance.Registration = registration;
|
||||
_logger.Info("TimeLordTV Registration Status - Registered: {0} In trial: {2} Expiration Date: {1} Is Valid: {3}", new object[4]
|
||||
{
|
||||
Plugin.Instance.Registration.registered,
|
||||
Plugin.Instance.Registration.expDate,
|
||||
Plugin.Instance.Registration.isTrial,
|
||||
Plugin.Instance.Registration.isValid
|
||||
});
|
||||
if (Plugin.Instance.Registration.isTrial)
|
||||
{
|
||||
_logger.Info(((BasePlugin)Plugin.Instance).Name + " You are Using the Trial Version, Please Consider purchasing to continue using the plugin after the trial period", new object[0]);
|
||||
}
|
||||
if (!Plugin.Instance.Registration.isValid)
|
||||
{
|
||||
_logger.Info(((BasePlugin)Plugin.Instance).Name + " Trial Expired, Please register to continue using the plugin", new object[0]);
|
||||
return;
|
||||
}
|
||||
_logger.Info("TimeLordTV :: Start :", (object[])null);
|
||||
try
|
||||
{
|
||||
_logger.Info("TimeLord :: Rework Series Dates :: Start", (object[])null);
|
||||
AggregateFolder rootFolder = _libraryManager.RootFolder;
|
||||
InternalItemsQuery val = new InternalItemsQuery();
|
||||
val.IncludeItemTypes = new string[1] { typeof(Series).Name };
|
||||
val.Recursive = true;
|
||||
BaseItem[] itemList = ((Folder)rootFolder).GetItemList(val);
|
||||
int num = 0;
|
||||
BaseItem[] array = itemList;
|
||||
foreach (BaseItem val2 in array)
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (val2.PremiereDate.HasValue && val2.DateCreated != val2.PremiereDate.Value && val2.PremiereDate.HasValue)
|
||||
{
|
||||
val2.DateCreated = val2.PremiereDate.Value;
|
||||
try
|
||||
{
|
||||
val2.UpdateToRepository((ItemUpdateType)16);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
_logger.ErrorException("TimeLordTV :: Error messing about with {0}", ex2, new object[1] { val2.Name });
|
||||
}
|
||||
num++;
|
||||
double num2 = num;
|
||||
num2 *= 100.0;
|
||||
progress.Report(num2);
|
||||
}
|
||||
}
|
||||
catch (Exception ex3)
|
||||
{
|
||||
_logger.ErrorException("TimeLord :: Error {0}", ex3, Array.Empty<object>());
|
||||
}
|
||||
try
|
||||
{
|
||||
_logger.Info("TimeLord :: Rework Season Dates :: Start", (object[])null);
|
||||
AggregateFolder rootFolder2 = _libraryManager.RootFolder;
|
||||
InternalItemsQuery val = new InternalItemsQuery();
|
||||
val.IncludeItemTypes = new string[1] { typeof(Season).Name };
|
||||
val.Recursive = true;
|
||||
BaseItem[] itemList2 = ((Folder)rootFolder2).GetItemList(val);
|
||||
_logger.Info("TimeLord :: Rework Season Dates :: Count ::" + itemList2.Count(), (object[])null);
|
||||
int num3 = 0;
|
||||
BaseItem[] array = itemList2;
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
Season val3 = (Season)array[i];
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (((BaseItem)val3).PremiereDate.HasValue && ((BaseItem)val3).DateCreated != ((BaseItem)val3).PremiereDate.Value && ((BaseItem)val3).PremiereDate.HasValue)
|
||||
{
|
||||
((BaseItem)val3).DateCreated = ((BaseItem)val3).PremiereDate.Value;
|
||||
try
|
||||
{
|
||||
((BaseItem)val3).UpdateToRepository((ItemUpdateType)16);
|
||||
_logger.Info("TimeLordTV :: Updated :: " + ((BaseItem)val3).Name, (object[])null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.Info("TimeLordTV :: Canceled", (object[])null);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex5)
|
||||
{
|
||||
_logger.ErrorException("TimeLordTV :: Error messing about with {0}", ex5, new object[1] { ((BaseItem)val3).Name });
|
||||
}
|
||||
num3++;
|
||||
double num4 = num3;
|
||||
num4 /= (double)itemList2.Count();
|
||||
num4 *= 100.0;
|
||||
progress.Report(num4);
|
||||
}
|
||||
}
|
||||
catch (Exception ex6)
|
||||
{
|
||||
_logger.ErrorException("TimeLord :: Error {0}", ex6, Array.Empty<object>());
|
||||
}
|
||||
try
|
||||
{
|
||||
_logger.Info("TimeLord :: Rework Episode Dates :: Start", (object[])null);
|
||||
AggregateFolder rootFolder3 = _libraryManager.RootFolder;
|
||||
InternalItemsQuery val = new InternalItemsQuery();
|
||||
val.IncludeItemTypes = new string[1] { typeof(Episode).Name };
|
||||
val.Recursive = true;
|
||||
List<BaseItem> list = ((Folder)rootFolder3).GetItemList(val).Where(delegate(BaseItem val5)
|
||||
{
|
||||
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
||||
if ((int)val5.LocationType == 0)
|
||||
{
|
||||
DateTimeOffset dateCreated = val5.DateCreated;
|
||||
DateTimeOffset? premiereDate = val5.PremiereDate;
|
||||
if (premiereDate.HasValue && dateCreated > premiereDate.GetValueOrDefault() && val5.PremiereDate.HasValue)
|
||||
{
|
||||
return !val5.IsLocked;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).ToList();
|
||||
_logger.Info("TimeLord :: Rework Episode Dates :: Count ::" + list.Count, (object[])null);
|
||||
int num5 = 0;
|
||||
foreach (Episode item in list)
|
||||
{
|
||||
Episode val4 = item;
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (((BaseItem)val4).PremiereDate.HasValue && ((BaseItem)val4).DateCreated != ((BaseItem)val4).PremiereDate.Value && ((BaseItem)val4).PremiereDate.HasValue)
|
||||
{
|
||||
((BaseItem)val4).DateCreated = ((BaseItem)val4).PremiereDate.Value;
|
||||
try
|
||||
{
|
||||
((BaseItem)val4).UpdateToRepository((ItemUpdateType)16);
|
||||
_logger.Info("TimeLordTV :: Updated :: " + ((BaseItem)val4).Name, (object[])null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.Info("TimeLordTV :: Canceled", (object[])null);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex8)
|
||||
{
|
||||
_logger.ErrorException("TimeLordTV :: Error messing about with {0}", ex8, new object[1] { ((BaseItem)val4).Name });
|
||||
}
|
||||
num5++;
|
||||
double num6 = num5;
|
||||
num6 /= (double)list.Count;
|
||||
num6 *= 100.0;
|
||||
progress.Report(num6);
|
||||
}
|
||||
}
|
||||
catch (Exception ex9)
|
||||
{
|
||||
_logger.ErrorException("TimeLord :: Error {0}", ex9, Array.Empty<object>());
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_003a: Expected O, but got Unknown
|
||||
return (IEnumerable<TaskTriggerInfo>)(object)new TaskTriggerInfo[1]
|
||||
{
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = "DailyTrigger",
|
||||
TimeOfDayTicks = TimeSpan.FromHours(5.0).Ticks
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>MediaBrowser.Plugins.TimeLord</AssemblyName>
|
||||
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="MediaBrowser.Plugins.TimeLord.thumb.png" />
|
||||
<EmbeddedResource Include="MediaBrowser.Plugins.TimeLord.thumb.png" LogicalName="MediaBrowser.Plugins.TimeLord.thumb.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MediaBrowser.Controller" />
|
||||
<Reference Include="MediaBrowser.Common" />
|
||||
<Reference Include="MediaBrowser.Model" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.8 KiB |
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Emby.Security;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
|
||||
namespace MediaBrowser.Plugins.TimeLord;
|
||||
|
||||
public class Plugin : BasePlugin, IHasThumbImage
|
||||
{
|
||||
private Guid _id = new Guid("3A7EF733-F70B-4947-9E03-2813B5A423E3");
|
||||
|
||||
public RegRecord Registration;
|
||||
|
||||
public override string Name => "TimeLordTV";
|
||||
|
||||
public override string Description => "Set Episode Added date to the original airdate for the Epsiode.";
|
||||
|
||||
public override Guid Id => _id;
|
||||
|
||||
public ImageFormat ThumbImageFormat => (ImageFormat)3;
|
||||
|
||||
public static Plugin Instance { get; private set; }
|
||||
|
||||
public Stream GetThumbImage()
|
||||
{
|
||||
Type type = ((object)this).GetType();
|
||||
return type.Assembly.GetManifestResourceStream(type.Namespace + ".thumb.png");
|
||||
}
|
||||
|
||||
public Plugin()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("3A7EF733-F70B-4947-9E03-2813B5A423E3")]
|
||||
[assembly: AssemblyCompany("ScoobydooProject.com")]
|
||||
[assembly: AssemblyConfiguration("Release")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019 ScoobydooProject.com")]
|
||||
[assembly: AssemblyDescription("Wayback Machine for TV Episodes")]
|
||||
[assembly: AssemblyFileVersion("19.08.03.1105")]
|
||||
[assembly: AssemblyInformationalVersion("3.2.32.14")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.Plugins.TimeLord")]
|
||||
[assembly: AssemblyTitle("MediaBrowser.Plugins.TimeLord")]
|
||||
[assembly: AssemblyVersion("19.8.3.1105")]
|
||||
@@ -1,246 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Model.Cryptography;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
|
||||
namespace Emby.Security;
|
||||
|
||||
public class PluginSecurityManager : IServerEntryPoint, IDisposable
|
||||
{
|
||||
private const string MBValidateUrl = "https://mb3admin.com/admin/service/registration/validate";
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly ICryptoProvider _cryptographyProvider;
|
||||
|
||||
private readonly ISecurityManager _securityManager;
|
||||
|
||||
private readonly IEncryptionManager _encryption;
|
||||
|
||||
public static PluginSecurityManager Current;
|
||||
|
||||
private readonly Dictionary<string, RegRecord> _regRecordCache = new Dictionary<string, RegRecord>();
|
||||
|
||||
private SemaphoreSlim _regLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, ISecurityManager securityManager, IEncryptionManager encryption)
|
||||
{
|
||||
if (httpClient == null)
|
||||
{
|
||||
throw new ArgumentNullException("httpClient");
|
||||
}
|
||||
_appHost = appHost;
|
||||
_httpClient = httpClient;
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_appPaths = appPaths;
|
||||
_fileSystem = fileSystem;
|
||||
_cryptographyProvider = cryptographyProvider;
|
||||
_securityManager = securityManager;
|
||||
_encryption = encryption;
|
||||
_logger = logManager.GetLogger("Plugin");
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private string GetCacheFilePath(string feature)
|
||||
{
|
||||
string text = GetType().FullName + GetCacheKey(feature);
|
||||
text = BaseExtensions.GetMD5(text).ToString("N");
|
||||
return Path.Combine(_appPaths.PluginConfigurationsPath, text);
|
||||
}
|
||||
|
||||
private RegRecord GetCachedRegRecord(string feature)
|
||||
{
|
||||
lock (_regRecordCache)
|
||||
{
|
||||
if (_regRecordCache.TryGetValue(GetCacheKey(feature), out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
string cacheFilePath = GetCacheFilePath(feature);
|
||||
try
|
||||
{
|
||||
string text = _encryption.DecryptString(_fileSystem.ReadAllText(cacheFilePath));
|
||||
RegRecord value = _jsonSerializer.DeserializeFromString<RegRecord>(text);
|
||||
if (value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetCacheKey(string feature)
|
||||
{
|
||||
string text = _securityManager.SupporterKey;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
text = "none";
|
||||
}
|
||||
return text + "-" + feature;
|
||||
}
|
||||
|
||||
private void DeleteCachedRecord(string feature)
|
||||
{
|
||||
lock (_regRecordCache)
|
||||
{
|
||||
_regRecordCache.Remove(GetCacheKey(feature));
|
||||
}
|
||||
try
|
||||
{
|
||||
_fileSystem.DeleteFile(GetCacheFilePath(feature));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveCachedRecord(string feature, RegRecord record)
|
||||
{
|
||||
lock (_regRecordCache)
|
||||
{
|
||||
_regRecordCache[GetCacheKey(feature)] = record;
|
||||
}
|
||||
string cacheFilePath = GetCacheFilePath(feature);
|
||||
string text = _jsonSerializer.SerializeToString((object)record);
|
||||
string text2 = _encryption.EncryptString(text);
|
||||
_fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
|
||||
_fileSystem.WriteAllText(cacheFilePath, text2);
|
||||
}
|
||||
|
||||
public async Task<RegRecord> GetRegistrationStatus(string feature, CancellationToken cancellationToken)
|
||||
{
|
||||
await _regLock.WaitAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
try
|
||||
{
|
||||
return await GetRegistrationStatusInternal(feature, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_regLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckRegistrationWithServer(DateTime lastChecked, bool registered)
|
||||
{
|
||||
if (registered || string.IsNullOrWhiteSpace(_securityManager.SupporterKey))
|
||||
{
|
||||
if (DateTime.UtcNow.AddDays(-1.0) > lastChecked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (DateTime.UtcNow.AddHours(-1.0) > lastChecked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<RegRecord> GetRegistrationStatusInternal(string feature, CancellationToken cancellationToken)
|
||||
{
|
||||
RegRecord regInfo = GetCachedRegRecord(feature);
|
||||
DateTime lastChecked = regInfo?.lastChecked ?? DateTime.MinValue;
|
||||
DateTime expDate = regInfo?.expDate ?? DateTime.MinValue;
|
||||
bool registered = regInfo?.registered ?? false;
|
||||
string supporterKey = _securityManager.SupporterKey;
|
||||
if (CheckRegistrationWithServer(lastChecked, registered))
|
||||
{
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>
|
||||
{
|
||||
{ "feature", feature },
|
||||
{ "key", supporterKey },
|
||||
{
|
||||
"mac",
|
||||
((IApplicationHost)_appHost).SystemId
|
||||
},
|
||||
{
|
||||
"systemid",
|
||||
((IApplicationHost)_appHost).SystemId
|
||||
},
|
||||
{
|
||||
"platform",
|
||||
((IApplicationHost)_appHost).OperatingSystemDisplayName
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
HttpRequestOptions val = new HttpRequestOptions
|
||||
{
|
||||
Url = "https://mb3admin.com/admin/service/registration/validate",
|
||||
EnableHttpCompression = false,
|
||||
BufferContent = false,
|
||||
CancellationToken = cancellationToken
|
||||
};
|
||||
val.SetPostData((IDictionary<string, string>)postData);
|
||||
HttpResponseInfo val2 = await _httpClient.SendAsync(val, "POST").ConfigureAwait(continueOnCapturedContext: false);
|
||||
try
|
||||
{
|
||||
using Stream stream = val2.Content;
|
||||
regInfo = _jsonSerializer.DeserializeFromStream<RegRecord>(stream);
|
||||
regInfo.lastChecked = DateTime.UtcNow;
|
||||
SaveCachedRecord(feature, regInfo);
|
||||
}
|
||||
finally
|
||||
{
|
||||
((IDisposable)val2)?.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error checking registration status of {0}", ex, new object[1] { feature });
|
||||
int num = 14;
|
||||
DateTime dateTime = new DateTime[2]
|
||||
{
|
||||
expDate,
|
||||
lastChecked.AddDays(num)
|
||||
}.Min();
|
||||
if (dateTime > DateTime.UtcNow.AddDays(num))
|
||||
{
|
||||
dateTime = DateTime.MinValue;
|
||||
}
|
||||
regInfo = new RegRecord
|
||||
{
|
||||
registered = (regInfo != null && regInfo.registered && dateTime >= DateTime.UtcNow && expDate >= DateTime.UtcNow),
|
||||
expDate = expDate
|
||||
};
|
||||
}
|
||||
}
|
||||
return regInfo;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Emby.Security;
|
||||
|
||||
public class RegRecord
|
||||
{
|
||||
public string featId { get; set; }
|
||||
|
||||
public bool registered { get; set; }
|
||||
|
||||
public DateTime expDate { get; set; }
|
||||
|
||||
public string key { get; set; }
|
||||
|
||||
public DateTime lastChecked { get; set; }
|
||||
|
||||
public bool isTrial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (expDate > DateTime.UtcNow)
|
||||
{
|
||||
return !registered;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool isValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isTrial)
|
||||
{
|
||||
return registered;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Security;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Notifications;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Plugins.TimeLordMovies.ScheduledTasks;
|
||||
|
||||
public class TLMoviesScheduledTask : IScheduledTask
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
private readonly ILibraryMonitor _libraryMonitor;
|
||||
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly INotificationManager _notificationManager;
|
||||
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
public string Category => "Time Lord";
|
||||
|
||||
public string Key => "TimeLordMovies";
|
||||
|
||||
public string Description => "Sets the Movie Date Added to the original premier date (Movies)";
|
||||
|
||||
public string Name => "Change Movie date added to original Premier date";
|
||||
|
||||
public TLMoviesScheduledTask(ILibraryManager libraryManager, ILogger logger, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IApplicationPaths appPaths, ILibraryMonitor libraryMonitor, ISecurityManager securityManager, IUserManager userManager, INotificationManager notificationManager, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_fileSystem = fileSystem;
|
||||
_appPaths = appPaths;
|
||||
_libraryMonitor = libraryMonitor;
|
||||
_notificationManager = notificationManager;
|
||||
_itemRepo = itemRepo;
|
||||
}
|
||||
|
||||
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
RegRecord registration = await PluginSecurityManager.Current.GetRegistrationStatus("TimeLordMovies", cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
Plugin.Instance.Registration = registration;
|
||||
_logger.Info("TimeLordMovies Registration Status - Registered: {0} In trial: {2} Expiration Date: {1} Is Valid: {3}", new object[4]
|
||||
{
|
||||
Plugin.Instance.Registration.registered,
|
||||
Plugin.Instance.Registration.expDate,
|
||||
Plugin.Instance.Registration.isTrial,
|
||||
Plugin.Instance.Registration.isValid
|
||||
});
|
||||
if (Plugin.Instance.Registration.isTrial)
|
||||
{
|
||||
_logger.Info(((BasePlugin)Plugin.Instance).Name + " You are Using the Trial Version, Please Consider purchasing to continue using the plugin after the trial period", new object[0]);
|
||||
}
|
||||
if (!Plugin.Instance.Registration.isValid)
|
||||
{
|
||||
_logger.Info(((BasePlugin)Plugin.Instance).Name + " Trial Expired, Please register to continue using the plugin", new object[0]);
|
||||
return;
|
||||
}
|
||||
_logger.Info("TimeLord.Movies :: Start :", (object[])null);
|
||||
try
|
||||
{
|
||||
_logger.Info("TimeLord.Movies :: Rework Movie Dates :: Start", (object[])null);
|
||||
AggregateFolder rootFolder = _libraryManager.RootFolder;
|
||||
InternalItemsQuery val = new InternalItemsQuery();
|
||||
val.IncludeItemTypes = new string[1] { typeof(Movie).Name };
|
||||
val.Recursive = true;
|
||||
List<BaseItem> list = ((Folder)rootFolder).GetItemList(val).Where(delegate(BaseItem i)
|
||||
{
|
||||
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
||||
if ((int)i.LocationType == 0)
|
||||
{
|
||||
DateTimeOffset dateCreated = i.DateCreated;
|
||||
DateTimeOffset? premiereDate = i.PremiereDate;
|
||||
if (premiereDate.HasValue && dateCreated > premiereDate.GetValueOrDefault() && i.PremiereDate.HasValue)
|
||||
{
|
||||
return !i.IsLocked;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).ToList();
|
||||
int num = 0;
|
||||
foreach (Movie item in list)
|
||||
{
|
||||
Movie val2 = item;
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
((BaseItem)val2).DateCreated = ((BaseItem)val2).PremiereDate.Value;
|
||||
try
|
||||
{
|
||||
((BaseItem)val2).UpdateToRepository((ItemUpdateType)16);
|
||||
_logger.Info("TimeLord.Movies:: Updated :: " + ((BaseItem)val2).Name, (object[])null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.Info("TimeLord.Movies :: Updated :: " + ((BaseItem)val2).Name, (object[])null);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.Info("TimeLord.Movies :: Canceled", (object[])null);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
_logger.ErrorException("TimeLord.Movies :: Error messing about with {0}", ex2, new object[1] { ((BaseItem)val2).Name });
|
||||
}
|
||||
num++;
|
||||
double num2 = num;
|
||||
num2 /= (double)list.Count();
|
||||
num2 *= 100.0;
|
||||
progress.Report(num2);
|
||||
}
|
||||
}
|
||||
catch (Exception ex3)
|
||||
{
|
||||
_logger.ErrorException("TimeLord.Movies :: Error {0}", ex3, Array.Empty<object>());
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_003a: Expected O, but got Unknown
|
||||
return (IEnumerable<TaskTriggerInfo>)(object)new TaskTriggerInfo[1]
|
||||
{
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = "DailyTrigger",
|
||||
TimeOfDayTicks = TimeSpan.FromHours(5.0).Ticks
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Security;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Notifications;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
|
||||
namespace MediaBrowser.Plugins.TimeLordMovies.ScheduledTasks;
|
||||
|
||||
public class TLTrailersScheduledTask : IScheduledTask
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
private readonly IMediaEncoder _mediaEncoder;
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly IApplicationPaths _appPaths;
|
||||
|
||||
private readonly ILibraryMonitor _libraryMonitor;
|
||||
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly INotificationManager _notificationManager;
|
||||
|
||||
private readonly IItemRepository _itemRepo;
|
||||
|
||||
public string Category => "Time Lord";
|
||||
|
||||
public string Key => "TimeLordMovies";
|
||||
|
||||
public string Description => "Sets the Trailer Date Added to the original premier date (Trailer)";
|
||||
|
||||
public string Name => "Change Trailer date added to original Premier date";
|
||||
|
||||
public TLTrailersScheduledTask(ILibraryManager libraryManager, ILogger logger, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IApplicationPaths appPaths, ILibraryMonitor libraryMonitor, ISecurityManager securityManager, IUserManager userManager, INotificationManager notificationManager, IItemRepository itemRepo)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_mediaEncoder = mediaEncoder;
|
||||
_fileSystem = fileSystem;
|
||||
_appPaths = appPaths;
|
||||
_libraryMonitor = libraryMonitor;
|
||||
_notificationManager = notificationManager;
|
||||
_itemRepo = itemRepo;
|
||||
}
|
||||
|
||||
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
|
||||
{
|
||||
RegRecord registration = await PluginSecurityManager.Current.GetRegistrationStatus("TimeLordMovies", cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
|
||||
Plugin.Instance.Registration = registration;
|
||||
_logger.Info("TimeLordMovies Registration Status - Registered: {0} In trial: {2} Expiration Date: {1} Is Valid: {3}", new object[4]
|
||||
{
|
||||
Plugin.Instance.Registration.registered,
|
||||
Plugin.Instance.Registration.expDate,
|
||||
Plugin.Instance.Registration.isTrial,
|
||||
Plugin.Instance.Registration.isValid
|
||||
});
|
||||
if (Plugin.Instance.Registration.isTrial)
|
||||
{
|
||||
_logger.Info(((BasePlugin)Plugin.Instance).Name + " You are Using the Trial Version, Please Consider purchasing to continue using the plugin after the trial period", new object[0]);
|
||||
}
|
||||
if (!Plugin.Instance.Registration.isValid)
|
||||
{
|
||||
_logger.Info(((BasePlugin)Plugin.Instance).Name + " Trial Expired, Please register to continue using the plugin", new object[0]);
|
||||
return;
|
||||
}
|
||||
_logger.Info("TimeLordMovies :: Start :", (object[])null);
|
||||
try
|
||||
{
|
||||
_logger.Info("TimeLord :: Rework Trailer Dates :: Start", (object[])null);
|
||||
AggregateFolder rootFolder = _libraryManager.RootFolder;
|
||||
InternalItemsQuery val = new InternalItemsQuery();
|
||||
val.IncludeItemTypes = new string[1] { typeof(Trailer).Name };
|
||||
val.Recursive = true;
|
||||
List<BaseItem> list = ((Folder)rootFolder).GetItemList(val).Where(delegate(BaseItem i)
|
||||
{
|
||||
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
|
||||
if ((int)i.LocationType != 0)
|
||||
{
|
||||
DateTimeOffset dateCreated = i.DateCreated;
|
||||
DateTimeOffset? premiereDate = i.PremiereDate;
|
||||
if (premiereDate.HasValue && dateCreated > premiereDate.GetValueOrDefault() && i.PremiereDate.HasValue)
|
||||
{
|
||||
return !i.IsLocked;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).ToList();
|
||||
int num = 0;
|
||||
foreach (Trailer item in list)
|
||||
{
|
||||
Trailer val2 = item;
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
((BaseItem)val2).DateCreated = ((BaseItem)val2).PremiereDate.Value;
|
||||
try
|
||||
{
|
||||
((BaseItem)val2).UpdateToRepository((ItemUpdateType)16);
|
||||
_logger.Info("TimeLord.Trailers :: Updated :: " + ((BaseItem)val2).Name, (object[])null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.Info("TimeLord.Trailers :: Updated :: " + ((BaseItem)val2).Name, (object[])null);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.Info("TimeLord.Trailers :: Canceled", (object[])null);
|
||||
break;
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
_logger.ErrorException("TimeLord.Trailers :: Error messing about with {0}", ex2, new object[1] { ((BaseItem)val2).Name });
|
||||
}
|
||||
num++;
|
||||
double num2 = num;
|
||||
num2 /= (double)list.Count();
|
||||
num2 *= 100.0;
|
||||
progress.Report(num2);
|
||||
}
|
||||
}
|
||||
catch (Exception ex3)
|
||||
{
|
||||
_logger.ErrorException("TimeLord.Trailers :: Error {0}", ex3, Array.Empty<object>());
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||
{
|
||||
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
|
||||
//IL_003a: Expected O, but got Unknown
|
||||
return (IEnumerable<TaskTriggerInfo>)(object)new TaskTriggerInfo[1]
|
||||
{
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = "DailyTrigger",
|
||||
TimeOfDayTicks = TimeSpan.FromHours(5.0).Ticks
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>MediaBrowser.Plugins.TimeLordMovies</AssemblyName>
|
||||
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LangVersion>12.0</LangVersion>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RootNamespace />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="MediaBrowser.Plugins.TimeLordMovies.thumb.png" />
|
||||
<EmbeddedResource Include="MediaBrowser.Plugins.TimeLordMovies.thumb.png" LogicalName="MediaBrowser.Plugins.TimeLordMovies.thumb.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MediaBrowser.Controller" />
|
||||
<Reference Include="MediaBrowser.Common" />
|
||||
<Reference Include="MediaBrowser.Model" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.8 KiB |
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Emby.Security;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
|
||||
namespace MediaBrowser.Plugins.TimeLordMovies;
|
||||
|
||||
public class Plugin : BasePlugin, IHasThumbImage
|
||||
{
|
||||
private Guid _id = new Guid("71a3ff1b-c589-4a39-b948-12f08b7715ee");
|
||||
|
||||
public RegRecord Registration;
|
||||
|
||||
public override string Name => "TimeLordMovies";
|
||||
|
||||
public override string Description => "TimeLord Movies allows you to set the date added for Movies to the original Theatrical release date set in the metadata for items in your collection. This is usefull when adding old Movies to your collection, as it prevents theses 'New' Movies from dominating your recently added list. Take control of time with TimeLord Movies.";
|
||||
|
||||
public override Guid Id => _id;
|
||||
|
||||
public ImageFormat ThumbImageFormat => (ImageFormat)3;
|
||||
|
||||
public static Plugin Instance { get; private set; }
|
||||
|
||||
public Stream GetThumbImage()
|
||||
{
|
||||
Type type = ((object)this).GetType();
|
||||
return type.Assembly.GetManifestResourceStream(type.Namespace + ".thumb.png");
|
||||
}
|
||||
|
||||
public Plugin()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("71a3ff1b-c589-4a39-b948-12f08b7715ee")]
|
||||
[assembly: AssemblyCompany("ScoobydooProject.com")]
|
||||
[assembly: AssemblyConfiguration("Release")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019 ScoobydooProject.com")]
|
||||
[assembly: AssemblyDescription("Wayback Machine for TV Movies")]
|
||||
[assembly: AssemblyFileVersion("19.08.03.1105")]
|
||||
[assembly: AssemblyInformationalVersion("3.2.32.14")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.Plugins.TimeLordMovies")]
|
||||
[assembly: AssemblyTitle("MediaBrowser.Plugins.TimeLordMovies")]
|
||||
[assembly: AssemblyVersion("19.8.3.1105")]
|
||||
+23
-24
@@ -2,31 +2,19 @@ version: '3.8'
|
||||
|
||||
services:
|
||||
nfoguard:
|
||||
# image: ghcr.io/sbcrumb/nfoguard:latest # Use this once package is public
|
||||
build: . # Temporary local build
|
||||
image: ghcr.io/jskala/nfoguard:latest
|
||||
container_name: nfoguard
|
||||
restart: unless-stopped
|
||||
user: "1000:1000"
|
||||
networks:
|
||||
- saltbox
|
||||
ports:
|
||||
- "8085:8080"
|
||||
- "8080:8080"
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-UTC}
|
||||
volumes:
|
||||
# Media directory
|
||||
- /mnt/unionfs/Media/:/media:rw
|
||||
|
||||
# NFOGuard data directory for database and logs
|
||||
- ./data:/app/data:rw
|
||||
|
||||
# Configuration files (SECURE TWO-FILE SYSTEM)
|
||||
- ./.env:/app/.env:ro # Main config (safe to share)
|
||||
- ./.env.secrets:/app/.env.secrets:ro # API keys & passwords (never commit)
|
||||
|
||||
# Optional: Can use env_file for simple setups, but file mounting is more secure
|
||||
# env_file:
|
||||
# - .env
|
||||
# - .env.secrets
|
||||
|
||||
- ./data:/app/data
|
||||
- ./config:/app/config
|
||||
- ${MEDIA_PATH}:/media:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
@@ -34,6 +22,17 @@ services:
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
saltbox:
|
||||
external: true
|
||||
# Optional: PostgreSQL database
|
||||
# postgres:
|
||||
# image: postgres:15
|
||||
# container_name: nfoguard-db
|
||||
# restart: unless-stopped
|
||||
# environment:
|
||||
# POSTGRES_DB: nfoguard
|
||||
# POSTGRES_USER: ${DB_USER:-nfoguard}
|
||||
# POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
# volumes:
|
||||
# - postgres_data:/var/lib/postgresql/data
|
||||
|
||||
# volumes:
|
||||
# postgres_data:
|
||||
@@ -1,77 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
nfoguard:
|
||||
build: .
|
||||
container_name: nfoguard
|
||||
ports:
|
||||
- "${PORT:-8080}:8080"
|
||||
volumes:
|
||||
# Mount your media directories (adjust these paths to match your setup)
|
||||
- "${HOST_MEDIA_PATH:-/mnt/unionfs/Media}:/media:rw"
|
||||
|
||||
# NFOGuard data directory for database and logs
|
||||
- "./data:/app/data"
|
||||
|
||||
# Optional: Mount config if you want to override
|
||||
# - "./config:/app/config"
|
||||
|
||||
environment:
|
||||
# Media paths inside container - customize these in your .env file
|
||||
- TV_PATHS=${TV_PATHS:-/media/tv,/media/tv6}
|
||||
- MOVIE_PATHS=${MOVIE_PATHS:-/media/movies,/media/movies6}
|
||||
|
||||
# Database
|
||||
- DB_PATH=${DB_PATH:-/app/data/media_dates.db}
|
||||
|
||||
# Radarr Database (REQUIRED for database-only mode)
|
||||
- RADARR_DB_HOST=${RADARR_DB_HOST}
|
||||
- RADARR_DB_PORT=${RADARR_DB_PORT:-5432}
|
||||
- RADARR_DB_NAME=${RADARR_DB_NAME:-radarr}
|
||||
- RADARR_DB_USER=${RADARR_DB_USER}
|
||||
- RADARR_DB_PASSWORD=${RADARR_DB_PASSWORD}
|
||||
|
||||
# Optional Radarr API (fallback only)
|
||||
- RADARR_URL=${RADARR_URL:-}
|
||||
- RADARR_API_KEY=${RADARR_API_KEY:-}
|
||||
|
||||
# Optional Sonarr API (for TV processing)
|
||||
- SONARR_URL=${SONARR_URL:-}
|
||||
- SONARR_API_KEY=${SONARR_API_KEY:-}
|
||||
|
||||
# NFO Management
|
||||
- MANAGE_NFO=${MANAGE_NFO:-true}
|
||||
- FIX_DIR_MTIMES=${FIX_DIR_MTIMES:-true}
|
||||
- LOCK_METADATA=${LOCK_METADATA:-true}
|
||||
- MANAGER_BRAND=${MANAGER_BRAND:-NFOGuard}
|
||||
|
||||
# Processing settings
|
||||
- BATCH_DELAY=${BATCH_DELAY:-5.0}
|
||||
- MAX_CONCURRENT_SERIES=${MAX_CONCURRENT_SERIES:-3}
|
||||
- MOVIE_PRIORITY=${MOVIE_PRIORITY:-import_then_digital}
|
||||
- MOVIE_POLL_MODE=${MOVIE_POLL_MODE:-always}
|
||||
- MOVIE_DATE_UPDATE_MODE=${MOVIE_DATE_UPDATE_MODE:-backfill_only}
|
||||
|
||||
# Logging
|
||||
- DEBUG=${DEBUG:-false}
|
||||
|
||||
# Server
|
||||
- PORT=${PORT:-8080}
|
||||
|
||||
# Optional: Connect to Radarr's database network
|
||||
# networks:
|
||||
# - radarr_network
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Optional: If you need to connect to Radarr's database network
|
||||
# networks:
|
||||
# radarr_network:
|
||||
# external: true
|
||||
@@ -1,38 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
nfoguard:
|
||||
image: ghcr.io/jskala/nfoguard:latest
|
||||
container_name: nfoguard
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
- PUID=${PUID:-1000}
|
||||
- PGID=${PGID:-1000}
|
||||
- TZ=${TZ:-UTC}
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./config:/app/config
|
||||
- ${MEDIA_PATH}:/media:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Optional: PostgreSQL database
|
||||
# postgres:
|
||||
# image: postgres:15
|
||||
# container_name: nfoguard-db
|
||||
# restart: unless-stopped
|
||||
# environment:
|
||||
# POSTGRES_DB: nfoguard
|
||||
# POSTGRES_USER: ${DB_USER:-nfoguard}
|
||||
# POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
# volumes:
|
||||
# - postgres_data:/var/lib/postgresql/data
|
||||
|
||||
# volumes:
|
||||
# postgres_data:
|
||||
@@ -1,70 +0,0 @@
|
||||
name: Build & Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
tags: [ 'v*' ]
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: host
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
run: |
|
||||
rm -rf * .git* 2>/dev/null || true
|
||||
git clone https://gitea.skalas.org/jskala/NFOguard.git /tmp/repo
|
||||
cp -r /tmp/repo/* .
|
||||
cp -r /tmp/repo/.* . 2>/dev/null || true
|
||||
rm -rf /tmp/repo
|
||||
|
||||
- name: Set up variables
|
||||
run: |
|
||||
# Get version from tag or use 'latest'
|
||||
if [[ ${{ github.ref }} == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
else
|
||||
VERSION=latest
|
||||
fi
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "Building version: $VERSION"
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build -t nfoguard:${{ env.VERSION }} .
|
||||
docker tag nfoguard:${{ env.VERSION }} nfoguard:latest
|
||||
|
||||
- name: Push to GitHub Container Registry
|
||||
run: |
|
||||
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
docker tag nfoguard:${{ env.VERSION }} ghcr.io/jskala/nfoguard:${{ env.VERSION }}
|
||||
docker tag nfoguard:${{ env.VERSION }} ghcr.io/jskala/nfoguard:latest
|
||||
docker push ghcr.io/jskala/nfoguard:${{ env.VERSION }}
|
||||
docker push ghcr.io/jskala/nfoguard:latest
|
||||
|
||||
- name: Push to Docker Hub
|
||||
run: |
|
||||
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
docker tag nfoguard:${{ env.VERSION }} jskala/nfoguard:${{ env.VERSION }}
|
||||
docker tag nfoguard:${{ env.VERSION }} jskala/nfoguard:latest
|
||||
docker push jskala/nfoguard:${{ env.VERSION }}
|
||||
docker push jskala/nfoguard:latest
|
||||
|
||||
- name: Update distribution repo
|
||||
run: |
|
||||
# Clone the distribution repo
|
||||
git clone https://github.com/jskala/NFOguard-Deploy.git /tmp/deploy
|
||||
cd /tmp/deploy
|
||||
|
||||
# Update docker-compose.yml with new version
|
||||
sed -i "s|image: .*nfoguard:.*|image: ghcr.io/jskala/nfoguard:${{ env.VERSION }}|" docker-compose.yml
|
||||
|
||||
# Update README with new version info
|
||||
sed -i "s/Latest Version: .*/Latest Version: ${{ env.VERSION }}/" README.md
|
||||
|
||||
# Commit and push changes
|
||||
git config user.name "GitHub Actions"
|
||||
git config user.email "actions@github.com"
|
||||
git add .
|
||||
git commit -m "Update to version ${{ env.VERSION }}" || exit 0
|
||||
git push
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,28 +0,0 @@
|
||||
# NFOGuard Emby Plugin Releases
|
||||
|
||||
This directory contains versioned releases of the NFOGuard Emby plugin DLL.
|
||||
|
||||
## Release Structure
|
||||
```
|
||||
releases/
|
||||
├── v1.0.0-working/ # Current proven working version
|
||||
├── v1.1.0-free-tier/ # Version with free tier limitations
|
||||
├── v1.2.0-production/ # Full production version
|
||||
└── changelog.md # Version history and changes
|
||||
```
|
||||
|
||||
## Installation
|
||||
1. Choose your version from the appropriate folder
|
||||
2. Copy `NFOGuard.Emby.Plugin.dll` to your Emby plugins folder
|
||||
3. Restart Emby Server
|
||||
4. Check version in Emby Dashboard → Plugins
|
||||
|
||||
## Version Guidelines
|
||||
- **Working versions**: Proven stable in testing
|
||||
- **Free tier versions**: Limited functionality for trial users
|
||||
- **Production versions**: Full-featured, licensed versions
|
||||
|
||||
## Current Status
|
||||
- **Latest Working**: v1.0.0 - Full functionality, no restrictions
|
||||
- **Latest Free Tier**: Not yet released
|
||||
- **Latest Production**: Not yet released
|
||||
Binary file not shown.
@@ -1,37 +0,0 @@
|
||||
# NFOGuard Emby Plugin v1.0.0 - Working Version
|
||||
|
||||
**Release Date**: 2025-01-12
|
||||
**Status**: ✅ PROVEN WORKING
|
||||
**License**: Full functionality, no restrictions
|
||||
|
||||
## 🎉 Test Results
|
||||
- **Episodes Processed**: 15,039
|
||||
- **Successfully Updated**: 14,661
|
||||
- **Success Rate**: 97.5%
|
||||
- **Processing Speed**: ~1,500 episodes/second
|
||||
|
||||
## ✅ Features
|
||||
- **Real-time Processing**: Automatic sync on media add/update
|
||||
- **Scheduled Task**: Weekly batch processing (enabled by default)
|
||||
- **TV Episodes Support**: Full episode date synchronization
|
||||
- **Movies Support**: Full movie date synchronization
|
||||
- **High Performance**: Processes thousands of items in seconds
|
||||
|
||||
## 🔧 Configuration
|
||||
- `EnableRealTimeSync = true` (default)
|
||||
- `EnableScheduledTask = true` (default)
|
||||
- `LogVerbose = false` (default)
|
||||
|
||||
## 📋 Installation
|
||||
1. Copy `NFOGuard.Emby.Plugin.dll` to Emby plugins folder
|
||||
2. Restart Emby Server
|
||||
3. Plugin loads automatically - no configuration needed
|
||||
4. Check "Scheduled Tasks" for "NFOGuard Date Sync"
|
||||
|
||||
## 🎯 What It Does
|
||||
Syncs `PremiereDate` → `DateCreated` so Emby displays import dates from NFO files instead of scan dates.
|
||||
|
||||
## ⚠️ Notes
|
||||
- No configuration UI (disabled to prevent crashes)
|
||||
- All settings hardcoded to optimal values
|
||||
- Licensing framework present but disabled
|
||||
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.
Reference in New Issue
Block a user