Final form of #1228.
Reworking the plugin system. Adding configurations, relocation, hashing & release providers, and more.
### Internal Changes:
- Added a new `IPluginManager` interface responsible for managing all local plugins. It can be used to list out installed plugins, enable or disable plugins, pin the version of a plugin if multiple versions are installed, uninstall user managed plugins, and load the plugin info for new plugins not yet loaded. Plugin installation and updates have deliberately been left out of it's responsibilities for now. It can either be extended to handle that in the future, or we could create a new `IPluginInstallationManager` (or similar) if we want to keep it solely responsible for local plugins.
- Added a new `IConfigurationService`, `IConfiguration`, `IConfigurationDefinition` `ConfigurationProvider<TConfig>`, for plugins (and the core) to create, load, save and **validate** configurations, in addition to creating a JSON schema (with extensions) to generate a UI for the config in the web ui. The existing `ISettingsProvider` have been moved to use the new service, and the built-in providers have their own settings served through this service.
- Added the ability to load a setting from an environment variable as long as it's marked with the new `EnvironmentVariableAttribute(string name)`, and track which of them are loaded globally and on a per-configuration basis. We can also lock the setting so it can't be changed when the env. var. is loaded (the default behavior), or allow it to be changed but load the initial value on every startup from the environment.
- Added the ability to indicate a setting needs a restart to take effect by marking it with the new `RestartRequiredAttribute`, and track if a server restart is needed because one or more changed, on a per-configuration, per setting basis. Complete with events and properties to track it globally and on a per configuration basis.
- Refactored the existing `IRenamer` and `IRenamerConfig` based system with it's own custom attribute based settings into a new relocation system, upgrading the existing `IRelocationService` to not just be used for utility methods, but instead also be responsible for everything related to relocating video files. This involved merging functionality from 3 existing services into 1 unified service, and moving the whole relocation pipeline into the abstraction for plugins to take advantage of. The new system uses an `IRelocationProvider` attached to an `IRelocationPipe`, optionally with an `IConfiguration` for an auto-generated configuration UI complete with **validation**, where the configuration is stored with the pipe instead of in the file system. The APIv3 have also been updated to reflect the newer plugin systems, so both plugins and clients have the same capabilities when it comes to relocating files.
- Added a new `IVideoHashingService`, `IHashProvider`, `IHashDigest`, `HashDigest` to the plugin abstraction. The new `IVideoHashingService` operates on raw `System.IO.FileInfo`s and is responsible for providing hashes to the `HashFileJob` before a `IVideo` & `IVideoFile` is necessarily assigned to a file location. So far there are runtime checks in place to make sure _at least 1_ `"ED2K"` hasher is enabled at all times, since we still rely on it as our absolute ID (in combination with the video file size) internally, but the hasher doesn't necessarily need to be provided by the new `"Core"` hasher. It contains events for when a `IVideo` & `IVideoFile` has been hashed (and added to the system), and when providers have been updated. The service can be used to switch between sequential mode and parallel mode — which controls how plugin providers are called, view all available and/or enabled hash types, enable or disable hash types per provider, and re-order the run priority of providers in sequential mode.<br/><br/>**_Note_**: _The priority doesn't affect the parallel mode because every provider is… ran in parallel._
- Added a new `"Core"` hasher (`CoreHashProvider`) implementing the `"ED2K"`, `"MD5"`, `"CRC32"`, `"SHA1"`, `"SHA256"`, & `"SHA512"` hash types, with the `"ED2K"` enabled by default.
- Added a new `IVideoReleaseService`, `IReleaseInfoProvider`, `IReleaseInfo`, `IReleaseVideoCrossReference`, `IReleaseMediaInfo`, `VideoReleaseEventArgs` to the plugin abstraction. The new `IVideoReleaseService` is responsible for everything release to release info, be it managing providers, doing the auto-search across multiple providers, showing provider info, saving release info to the database, and clearing saved release info from the database. It also contains events for when a release has been saved or cleared, when a auto-search has been started/completed, and when providers have been updated. The service can be used to switch between sequential mode — running each provider in a loop in priority order until we find a match or exhaust the list — or parallel mode — running all providers in parallel and selecting the highest valid priority release, view all providers, enable or disable providers, re-order the priority of the providers.
- Added a new `"AniDB"` release info provider (`AnidbReleaseProvider`), hooking into the existing AniDB UDP lookup logic. As a side-effect of the change in the lookup process have the MyList support in the existing UDP lookup logic has been stripped out, and we now rely entirely on the `IVideoReleaseService` and MyList sync job to add new files and/or or pull watched state from the MyList.
- Added a `IHashProvider<TConfig>` interface to create an explicit relation between a hash provider and a configuration. This information is also available to plugins on the `HashProviderInfo` class retrieved by the `IVideoHashingService` and to RESTful clients in the APIv3 (e.g. for the web ui to act on the information).
- Added a `IReleaseInfoProvider<TConfig>` in the same way as the `IHashProvider<TConfig>`, but for release info providers.
- Added `IReadOnlyList<IHashDigest> Hashes`, `string? SHA256`, `string? SHA512` to `IHashes`, to list all hashes stored for a `IVideo` that may not necessarily by strongly typed and to expose all hash types supported by the `CoreHashProvider` (`"Core"` in the UI) as strongly typed hashes. The existing strongly typed hash types have been converted to helpers; retrieving the first stored hash from the list of the given type.
- `AniDB_File`, `AniDB_FileUpdate`, `AniDB_ReleaseGroup`, `CrossRef_Languages_AniDB_File`, & `CrossRef_Subtitles_AniDB_File` models/tables/repos are gone, and their functionality replaced by the new `StoredReleaseInfo` & `StoredReleaseInfo_MatchAttempt`. The AniDB file has also been removed from the abstraction.
- Video file hashes — except the `"ED2K"` hash — has been moved to only being stored in the new `VideoLocal_HashDigest` table, but the `"ED2K"` is still stored on the video itself in addition to the new table.
- Currently I've assigned every existing link as a "manual link", because the user is now able to edit every link we store if they so desire, and this was the simplest way to show all the links in the current Web UI.
- Added a new plugin to simply export/import release info (`Shoko.Plugin.ReleaseExporter`). This is both my test case for the plugin system in addition to a small handy provider if you ever need to re-index your collection from scratch and don't want to do the AniDB UDP dance, or if you want to transcode your collection to a newer format at some point and want to preserve the release info in the process.
- Fixed an issue naming issue with renaming the `Tmdb_Show_Network` in MySQL/Maria with case-insensitive table names. Closes #1291.
- Migrated the `Shoko.Plugin.Abstraction` NuGet package to use `Shoko.Abstractions`, and refactored it's structure. The `Shoko.Plugin.Abstraction` NuGet package is now deprecated and will be removed in a future version.
- Lots more!
### Changes in APIv1:
- All file linking/unlinking in APIv1 has been soft deprecated. Use APIv3 instead. By soft deprecated I mean the client can still make the requests, but will only get an error message back from the server.
- Release info has been migrated to use the new format, but only for releases provided by the `"AniDB"` provider.
- Release groups have been migrated to use the new format, but only for release groups with `"AniDB"` as a source.
### Changes in APIv2:
- Release info has been migrated to use the new format, but only for releases provided by the `"AniDB"` provider.
- Release groups have been migrated to use the new format, but only for release groups with `"AniDB"` as a source.
### Changes in APIv3:
- The `RenamerController` have been replaced by the `RelocationController`. They're not quite the same, but very similar. The new relocation controller allows clients to interact with the upgraded `IRelocationService` to do everything you can do with the service in the API.
- All the models related to renamers have been reworked as a result of the transition to the newer relocation base system.
- Release info has been migrated to a new API model.
- `File.Hashes` has been changed from a dict of well known, nullable hashes to a list of hash digests, where only the ED2K hash is guaranteed to be included in the list.
- `File.AniDB` has been replaced with `File.Release`, which now uses the new release info model. The `includeDataFrom=AniDB` query parameter for file endpoints has been replaced with `include=ReleaseInfo`.
- Added a new `HashingController` (mounted at `/api/v3/Hashing`), allowing RESTful clients to also interact with the newly added `IVideoHashingService`. You can do anything you can do with the service in the API.
- Added a new `ReleaseInfoController` (mounted at `/api/v3/ReleaseInfo`), allowing RESTful clients to also interact with the newly added `IVideoReleaseService`. You can do anything you can do with the service in the API.
- File linking in APIv3 have been converted to use the new service, and as a result the artificial limit of not allowing the user to remove AniDB releases is gone. A release is simply a release now, regardless of provider.