LANCommander/LANCommander.Playnite.Extension/ImportController.cs

384 lines
13 KiB
C#

using LANCommander.PlaynitePlugin.Extensions;
using LANCommander.SDK;
using LANCommander.SDK.Extensions;
using LANCommander.SDK.Helpers;
using Playnite.SDK;
using Playnite.SDK.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace LANCommander.PlaynitePlugin
{
public class ImportController
{
public static readonly ILogger Logger = LogManager.GetLogger();
private LANCommanderLibraryPlugin Plugin;
private IGameDatabaseAPI Database;
private IEnumerable<Genre> Genres;
private IEnumerable<Tag> Tags;
private IEnumerable<Company> Publishers;
private IEnumerable<Company> Developers;
private IEnumerable<Category> Collections;
private string ImageCachePath;
public ImportController(LANCommanderLibraryPlugin plugin)
{
Plugin = plugin;
Database = Plugin.PlayniteApi.Database;
ImageCachePath = Path.Combine(plugin.GetPluginUserDataPath(), "Cache");
if (!Directory.Exists(ImageCachePath))
Directory.CreateDirectory(ImageCachePath);
}
public IEnumerable<Game> ImportGames()
{
var playSessions = Plugin.LANCommanderClient.Profile.GetPlaySessions().Where(ps => ps.Start != null && ps.End != null).OrderByDescending(ps => ps.End);
var manifests = Plugin.LANCommanderClient.Games.Get();
Genres = ImportGenres(manifests.Where(m => m.Genre != null).SelectMany(m => m.Genre).Distinct());
Tags = ImportTags(manifests.Where(m => m.Tags != null).SelectMany(m => m.Tags).Distinct());
Publishers = ImportCompanies(manifests.Where(m => m.Publishers != null).SelectMany(m => m.Publishers).Distinct());
Developers = ImportCompanies(manifests.Where(m => m.Developers != null).SelectMany(m => m.Developers).Distinct());
Collections = ImportCollections(manifests.Where(m => m.Collections != null).SelectMany(m => m.Collections).Distinct());
var games = new List<Game>();
Parallel.ForEach(manifests, new ParallelOptions {
MaxDegreeOfParallelism = 8,
}, (manifest) =>
{
Game game = null;
try
{
var gamePlaySessions = playSessions.Where(ps => ps.GameId == manifest.Id);
game = ImportGame(manifest, gamePlaySessions);
}
catch (Exception ex)
{
Logger?.Error(ex, $"Game {manifest.Title} could not be imported!");
}
if (game != null)
games.Add(game);
});
#region Cleanup
// Clean up any games we don't have access to
var gamesToRemove = Database.Games.Where(g => g.PluginId == Plugin.Id && !manifests.Any(lg => lg.Id == g.Id)).ToList();
Database.Games.Remove(gamesToRemove);
#endregion
return games;
}
public Game ImportGame(GameManifest manifest, IEnumerable<SDK.Models.PlaySession> playSessions)
{
bool exists = false;
var game = Database.Games.Get(manifest.Id);
if (game == null)
game = new Game();
else
exists = true;
game.Id = manifest.Id;
game.GameId = manifest.Id.ToString();
game.PluginId = Plugin.Id;
game.Name = manifest.Title;
game.SortingName = manifest.SortTitle;
game.Description = manifest.Description;
game.ReleaseDate = new ReleaseDate(manifest.ReleasedOn);
if (game.IsInstalled && game.Version != manifest.Version)
{
if (!game.Name.EndsWith(" - Update Available"))
game.Name += " - Update Available";
}
#region Play Sessions
if (playSessions.Count() > 0)
{
game.LastActivity = playSessions.First().End;
game.PlayCount = (ulong)playSessions.Count();
game.Playtime = (ulong)playSessions.Sum(ps => ps.End.Value.Subtract(ps.Start.Value).TotalSeconds);
}
#endregion
#region Actions
if (game.GameActions == null)
game.GameActions = new ObservableCollection<Playnite.SDK.Models.GameAction>();
else
game.GameActions.Clear();
if (manifest.Actions == null)
{
Logger?.Warn($"Game {manifest.Title} does not have any actions defined and may not be playable");
return null;
}
foreach (var action in game.GameActions.Where(a => a.IsPlayAction))
game.GameActions.Remove(action);
if (game.IsInstalled || game.IsInstalling)
foreach (var action in manifest.Actions.OrderBy(a => a.SortOrder).Where(a => !a.IsPrimaryAction))
{
var actionPath = action.Path?.ExpandEnvironmentVariables(game.InstallDirectory);
var actionWorkingDir = String.IsNullOrWhiteSpace(action.WorkingDirectory) ? game.InstallDirectory : action.WorkingDirectory.ExpandEnvironmentVariables(game.InstallDirectory);
var actionArguments = action.Arguments?.ExpandEnvironmentVariables(game.InstallDirectory);
if (actionPath.StartsWith(actionWorkingDir))
actionPath = actionPath.Substring(actionWorkingDir.Length).TrimStart(Path.DirectorySeparatorChar);
game.GameActions.Add(new Playnite.SDK.Models.GameAction()
{
Name = action.Name,
Arguments = action.Arguments,
Path = actionPath,
WorkingDir = actionWorkingDir,
IsPlayAction = false
});
}
#endregion
// Genres
if (manifest.Genre != null)
game.GenreIds = Genres.Where(g => manifest.Genre.Contains(g.Name)).Select(g => g.Id).ToList();
// Tags
if (manifest.Tags != null)
game.TagIds = Tags.Where(t => manifest.Tags.Contains(t.Name)).Select(t => t.Id).ToList();
// Publishers
if (manifest.Publishers != null)
game.PublisherIds = Publishers.Where(p => manifest.Publishers.Contains(p.Name)).Select(p => p.Id).ToList();
// Developers
if (manifest.Developers != null)
game.DeveloperIds = Developers.Where(d => manifest.Developers.Contains(d.Name)).Select(d => d.Id).ToList();
// Collections
if (manifest.Collections != null)
game.CategoryIds = Collections.Where(c => manifest.Collections.Contains(c.Name)).Select(c => c.Id).ToList();
// Media
if (manifest.Media != null && manifest.Media.Any(m => m.Type == SDK.Enums.MediaType.Icon))
{
var task = ImportMedia(manifest.Media.First(m => m.Type == SDK.Enums.MediaType.Icon));
task.Wait();
if (game.Icon != task.Result)
game.Icon = task.Result;
}
if (manifest.Media != null && manifest.Media.Any(m => m.Type == SDK.Enums.MediaType.Cover))
{
var task = ImportMedia(manifest.Media.First(m => m.Type == SDK.Enums.MediaType.Cover));
task.Wait();
if (game.CoverImage != task.Result)
game.CoverImage = task.Result;
}
if (manifest.Media != null && manifest.Media.Any(m => m.Type == SDK.Enums.MediaType.Background))
{
var task = ImportMedia(manifest.Media.First(m => m.Type == SDK.Enums.MediaType.Background));
task.Wait();
if (game.BackgroundImage != task.Result)
game.BackgroundImage = task.Result;
}
// Features
var features = ImportFeatures(manifest);
game.FeatureIds = features.Select(f => f.Id).ToList();
if (exists)
Database.Games.Update(game);
else
Database.Games.Add(game);
return game;
}
private IEnumerable<Genre> ImportGenres(IEnumerable<string> genreNames)
{
foreach (var genreName in genreNames)
{
var genre = Database.Genres.FirstOrDefault(g => g.Name == genreName);
if (genre == null)
{
genre = new Genre(genreName);
Database.Genres.Add(genre);
}
yield return genre;
}
}
private IEnumerable<Tag> ImportTags(IEnumerable<string> tagNames)
{
foreach (var tagName in tagNames)
{
var tag = Database.Tags.FirstOrDefault(t => t.Name == tagName);
if (tag == null)
{
tag = new Tag(tagName);
Database.Tags.Add(tag);
}
yield return tag;
}
}
private IEnumerable<Company> ImportCompanies(IEnumerable<string> companyNames)
{
foreach (var companyName in companyNames)
{
var company = Database.Companies.FirstOrDefault(c => c.Name == companyName);
if (company == null)
{
company = new Company(companyName);
Database.Companies.Add(company);
}
yield return company;
}
}
private IEnumerable<Category> ImportCollections(IEnumerable<string> collectionNames)
{
foreach (var collectionName in collectionNames)
{
var category = Database.Categories.FirstOrDefault(c => c.Name == collectionName);
if (category == null)
{
category = new Category(collectionName);
Database.Categories.Add(category);
}
yield return category;
}
}
private IEnumerable<GameFeature> ImportFeatures(GameManifest manifest)
{
if (manifest.Singleplayer)
{
var featureName = $"Singleplayer";
var feature = Database.Features.FirstOrDefault(f => f.Name == featureName);
if (feature == null)
{
feature = new GameFeature(featureName);
Database.Features.Add(feature);
}
yield return feature;
}
if (manifest.LocalMultiplayer != null || manifest.LanMultiplayer != null || manifest.OnlineMultiplayer != null)
{
var featureName = $"Multiplayer";
var feature = Database.Features.FirstOrDefault(f => f.Name == featureName);
if (feature == null)
{
feature = new GameFeature(featureName);
Database.Features.Add(feature);
}
yield return feature;
}
if (manifest.LocalMultiplayer != null)
{
var featureName = $"Local Multiplayer {manifest.LocalMultiplayer.GetPlayerCount()}".Trim();
var feature = Database.Features.FirstOrDefault(f => f.Name == featureName);
if (feature == null)
{
feature = new GameFeature(featureName);
Database.Features.Add(feature);
}
yield return feature;
}
if (manifest.LanMultiplayer != null)
{
var featureName = $"LAN Multiplayer {manifest.LanMultiplayer.GetPlayerCount()}".Trim();
var feature = Database.Features.FirstOrDefault(f => f.Name == featureName);
if (feature == null)
{
feature = new GameFeature(featureName);
Database.Features.Add(feature);
}
yield return feature;
}
if (manifest.OnlineMultiplayer != null)
{
var featureName = $"LAN Multiplayer {manifest.OnlineMultiplayer.GetPlayerCount()}".Trim();
var feature = Database.Features.FirstOrDefault(f => f.Name == featureName);
if (feature == null)
{
feature = new GameFeature(featureName);
Database.Features.Add(feature);
}
yield return feature;
}
}
private async Task<string> ImportMedia(SDK.Models.Media media)
{
var cacheFilePath = Path.Combine(ImageCachePath, $"{media.FileId}-{media.Crc32}.cache");
if (!File.Exists(cacheFilePath))
{
var staleFiles = Directory.EnumerateFiles(ImageCachePath, $"{media.FileId}-*.cache");
foreach (var staleFile in staleFiles)
File.Delete(staleFile);
await Plugin.LANCommanderClient.Media.Download(media, cacheFilePath);
}
return cacheFilePath;
}
}
}