LANCommander/LANCommander.Playnite.Extension/ImportController.cs

364 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 games = Plugin.LANCommanderClient.Games.Get();
Genres = ImportGenres(games.SelectMany(g => g.Genres).Select(g => g.Name).Distinct());
Tags = ImportTags(games.SelectMany(g => g.Tags).Select(t => t.Name).Distinct());
Publishers = ImportCompanies(games.SelectMany(g => g.Publishers).Select(c => c.Name).Distinct());
Developers = ImportCompanies(games.SelectMany(g => g.Developers).Select(c => c.Name).Distinct());
Collections = ImportCollections(games.SelectMany(g => g.Collections).Select(c => c.Name).Distinct());
var playniteGames = new List<Game>();
Parallel.ForEach(games, 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)
playniteGames.Add(game);
});
#region Cleanup
// Clean up any games we don't have access to
var gamesToRemove = Database.Games.Where(g => g.PluginId == Plugin.Id && !games.Any(lg => lg.Id == g.Id)).ToList();
Database.Games.Remove(gamesToRemove);
#endregion
return playniteGames;
}
public Game ImportGame(LANCommander.SDK.Models.Game game, IEnumerable<SDK.Models.PlaySession> playSessions)
{
bool exists = false;
var playniteGame = Database.Games.Get(game.Id);
if (playniteGame == null)
playniteGame = new Game();
else
exists = true;
playniteGame.Id = game.Id;
playniteGame.GameId = game.Id.ToString();
playniteGame.PluginId = Plugin.Id;
playniteGame.Name = game.Title;
playniteGame.SortingName = game.SortTitle;
playniteGame.Description = game.Description;
playniteGame.Notes = game.Notes;
playniteGame.ReleaseDate = new ReleaseDate(game.ReleasedOn);
if (playniteGame.IsInstalled)
{
var updateAvailable = false;
var manifests = Plugin.GetGameManifests(playniteGame);
foreach (var mani in manifests)
{
if (game.DependentGames.Any(g => g != null && g.Id == mani.Id && playniteGame.Version != game.Archives.OrderByDescending(a => a.CreatedOn).FirstOrDefault()?.Version))
updateAvailable = true;
}
if (updateAvailable && playniteGame.Name.EndsWith(ResourceProvider.GetString("LOCLANCommanderUpdateAvailableSuffix")))
playniteGame.Name += ResourceProvider.GetString("LOCLANCommanderUpdateAvailableSuffix");
}
#region Play Sessions
if (playSessions.Count() > 0)
{
playniteGame.LastActivity = playSessions.First().End;
playniteGame.PlayCount = (ulong)playSessions.Count();
playniteGame.Playtime = (ulong)playSessions.Sum(ps => ps.End.Value.Subtract(ps.Start.Value).TotalSeconds);
}
#endregion
#region Actions
if (playniteGame.GameActions == null)
playniteGame.GameActions = new ObservableCollection<Playnite.SDK.Models.GameAction>();
else
playniteGame.GameActions.Clear();
if (game.Actions == null)
{
Logger?.Warn($"Game {game.Title} does not have any actions defined and may not be playable");
return null;
}
foreach (var action in playniteGame.GameActions.Where(a => a.IsPlayAction))
playniteGame.GameActions.Remove(action);
if (playniteGame.IsInstalled || playniteGame.IsInstalling)
foreach (var action in game.Actions.OrderBy(a => a.SortOrder).Where(a => !a.IsPrimaryAction))
{
var actionPath = action.Path?.ExpandEnvironmentVariables(playniteGame.InstallDirectory);
var actionWorkingDir = String.IsNullOrWhiteSpace(action.WorkingDirectory) ? playniteGame.InstallDirectory : action.WorkingDirectory.ExpandEnvironmentVariables(playniteGame.InstallDirectory);
var actionArguments = action.Arguments?.ExpandEnvironmentVariables(playniteGame.InstallDirectory);
if (actionPath.StartsWith(actionWorkingDir))
actionPath = actionPath.Substring(actionWorkingDir.Length).TrimStart(Path.DirectorySeparatorChar);
playniteGame.GameActions.Add(new Playnite.SDK.Models.GameAction()
{
Name = action.Name,
Arguments = action.Arguments,
Path = actionPath,
WorkingDir = actionWorkingDir,
IsPlayAction = false
});
}
#endregion
// Genres
if (game.Genres != null)
playniteGame.GenreIds = Genres.Where(g => game.Genres.Any(gg => gg.Name == g.Name)).Select(g => g.Id).ToList();
// Tags
if (game.Tags != null)
playniteGame.TagIds = Tags.Where(t => game.Tags.Any(gt => gt.Name == t.Name)).Select(t => t.Id).ToList();
// Publishers
if (game.Publishers != null)
playniteGame.PublisherIds = Publishers.Where(c => game.Publishers.Any(gp => gp.Name == c.Name)).Select(p => p.Id).ToList();
// Developers
if (game.Developers != null)
playniteGame.DeveloperIds = Developers.Where(c => game.Developers.Any(gd => gd.Name == c.Name)).Select(d => d.Id).ToList();
// Collections
if (game.Collections != null)
playniteGame.CategoryIds = Collections.Where(c => game.Collections.Any(gc => gc.Name == c.Name)).Select(c => c.Id).ToList();
// Media
if (game.Media != null && game.Media.Any(m => m.Type == SDK.Enums.MediaType.Icon))
{
var task = ImportMedia(game.Media.First(m => m.Type == SDK.Enums.MediaType.Icon));
task.Wait();
if (playniteGame.Icon != task.Result)
playniteGame.Icon = task.Result;
}
if (game.Media != null && game.Media.Any(m => m.Type == SDK.Enums.MediaType.Cover))
{
var task = ImportMedia(game.Media.First(m => m.Type == SDK.Enums.MediaType.Cover));
task.Wait();
if (playniteGame.CoverImage != task.Result)
playniteGame.CoverImage = task.Result;
}
if (game.Media != null && game.Media.Any(m => m.Type == SDK.Enums.MediaType.Background))
{
var task = ImportMedia(game.Media.First(m => m.Type == SDK.Enums.MediaType.Background));
task.Wait();
if (playniteGame.BackgroundImage != task.Result)
playniteGame.BackgroundImage = task.Result;
}
// Features
var features = ImportFeatures(game);
playniteGame.FeatureIds = features.Select(f => f.Id).ToList();
if (exists)
Database.Games.Update(playniteGame);
else
Database.Games.Add(playniteGame);
return playniteGame;
}
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(SDK.Models.Game game)
{
if (game.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 (game.MultiplayerModes != null && game.MultiplayerModes.Any())
{
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;
}
foreach (var mode in game.MultiplayerModes)
{
var featureName = $"{mode.Type} Multiplayer {mode.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;
}
}
}