├── CHANGELOG.MD ├── OculusLibrary ├── Resources │ └── oculusicon.png ├── extension.yaml ├── OS │ ├── IPathNormaliser.cs │ ├── WMODrive.cs │ ├── IWindowsManagementObjectQueryProvider.cs │ ├── IRegistryValueProvider.cs │ ├── WMODriveQueryProvider.cs │ ├── RegistryValueProvider.cs │ └── PathNormaliser.cs ├── packages.config ├── DataExtraction │ ├── IOculusPathSniffer.cs │ ├── OculusWebsiteAggregateRating.cs │ ├── OculusWebsiteJson.cs │ ├── OculusWebsiteScraper.cs │ └── OculusPathSniffer.cs ├── ManifestParseException.cs ├── OculusManifest.cs ├── Properties │ └── AssemblyInfo.cs ├── OculusLibrary.csproj └── OculusLibraryPlugin.cs ├── README.md ├── .github └── workflows │ ├── main.yml │ └── release.yml ├── OculusLibrary.Tests ├── Properties │ └── AssemblyInfo.cs ├── packages.config ├── OculusWebsiteParserTests.cs ├── PathNormaliserTests.cs ├── OculusLibrary.Tests.csproj └── demo1.html ├── OculusLibrary.sln ├── .gitattributes └── .gitignore /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.0.0 4 | 5 | Initial Release -------------------------------------------------------------------------------- /OculusLibrary/Resources/oculusicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shawson/Playnite-OculusLibrary/HEAD/OculusLibrary/Resources/oculusicon.png -------------------------------------------------------------------------------- /OculusLibrary/extension.yaml: -------------------------------------------------------------------------------- 1 | Name: Oculus Library Importer 2 | Author: Shaw Young 3 | Version: 1.0.0 4 | Module: OculusLibrary.dll 5 | Type: GameLibrary 6 | Icon: Resources\oculusicon.png -------------------------------------------------------------------------------- /OculusLibrary/OS/IPathNormaliser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OculusLibrary.OS 4 | { 5 | public interface IPathNormaliser: IDisposable 6 | { 7 | string Normalise(string path); 8 | } 9 | } -------------------------------------------------------------------------------- /OculusLibrary/OS/WMODrive.cs: -------------------------------------------------------------------------------- 1 | namespace OculusLibrary.OS 2 | { 3 | public class WMODrive 4 | { 5 | public string DeviceId { get; set; } 6 | public string DriveLetter { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /OculusLibrary/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /OculusLibrary/DataExtraction/IOculusPathSniffer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace OculusLibrary.DataExtraction 4 | { 5 | public interface IOculusPathSniffer 6 | { 7 | List GetOculusLibraryLocations(); 8 | } 9 | } -------------------------------------------------------------------------------- /OculusLibrary/DataExtraction/OculusWebsiteAggregateRating.cs: -------------------------------------------------------------------------------- 1 | namespace OculusLibrary.DataExtraction 2 | { 3 | public class OculusWebsiteAggregateRating { 4 | public int RatingCount { get; set; } 5 | public decimal RatingValue { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /OculusLibrary/OS/IWindowsManagementObjectQueryProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Management; 3 | 4 | namespace OculusLibrary.OS 5 | { 6 | public interface IWMODriveQueryProvider 7 | { 8 | List GetDriveData(); 9 | } 10 | } -------------------------------------------------------------------------------- /OculusLibrary/DataExtraction/OculusWebsiteJson.cs: -------------------------------------------------------------------------------- 1 | namespace OculusLibrary.DataExtraction 2 | { 3 | public class OculusWebsiteJson 4 | { 5 | public string Name { get; set; } 6 | public string Description { get; set; } 7 | public OculusWebsiteAggregateRating AggregateRating { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /OculusLibrary/OS/IRegistryValueProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Win32; 3 | 4 | namespace OculusLibrary.OS 5 | { 6 | public interface IRegistryValueProvider 7 | { 8 | List GetSubKeysForPath(RegistryView platform, RegistryHive hive, string path); 9 | string GetValueForPath(RegistryView platform, RegistryHive hive, string path, string keyName); 10 | } 11 | } -------------------------------------------------------------------------------- /OculusLibrary/ManifestParseException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace OculusLibrary 4 | { 5 | [Serializable] 6 | public class ManifestParseException : Exception 7 | { 8 | public ManifestParseException() { } 9 | public ManifestParseException(string message) : base(message) { } 10 | public ManifestParseException(string message, Exception inner) : base(message, inner) { } 11 | protected ManifestParseException( 12 | System.Runtime.Serialization.SerializationInfo info, 13 | System.Runtime.Serialization.StreamingContext context) : base(info, context) { } 14 | } 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Playnite-OculusLibrary 2 | 3 | ![alt text](https://github.com/Shawson/Playnite-OculusLibrary/workflows/CI/badge.svg "CI Build Status") 4 | ![alt text](https://github.com/Shawson/Playnite-OculusLibrary/workflows/Release%20Build/badge.svg "Release Build Status") 5 | 6 | Plugin for Playnite to add support for importing games from the Oculus store. 7 | 8 | ## Installation ## 9 | 10 | Download the latest release zip file and extract to your Playnite/Extensions folder. 11 | 12 | ## Configuration ## 13 | 14 | Currently, none. The plugin should pickup the installation location from the Windows registry and automatically scan for your installed games. 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | name: CI 7 | 8 | jobs: 9 | build: 10 | runs-on: [windows-2019] 11 | steps: 12 | - name: checkout 13 | uses: actions/checkout@v1 14 | 15 | - name: Setup Nuget.exe 16 | uses: warrenbuckley/Setup-Nuget@v1 17 | 18 | - name: Nuget Restore 19 | run: nuget restore .\OculusLibrary.sln 20 | 21 | - name: Build (DotNET4.6.2) 22 | run: | 23 | cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\" 24 | .\MSBuild.exe $Env:GITHUB_WORKSPACE\OculusLibrary.sln -p:Configuration=Release -restore 25 | -------------------------------------------------------------------------------- /OculusLibrary.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("OculusLibrary.Tests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("OculusLibrary.Tests")] 10 | [assembly: AssemblyCopyright("Copyright © 2019")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("0017fa7b-c646-4e68-bd18-9026c4c2b9e2")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /OculusLibrary.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /OculusLibrary/OS/WMODriveQueryProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Management; 3 | 4 | namespace OculusLibrary.OS 5 | { 6 | public class WMODriveQueryProvider : IWMODriveQueryProvider 7 | { 8 | public List GetDriveData() 9 | { 10 | var result = new List(); 11 | ManagementObjectSearcher ms = new ManagementObjectSearcher("Select DeviceId, DriveLetter from Win32_Volume"); 12 | 13 | foreach(var o in ms.Get()) 14 | { 15 | result.Add(new WMODrive { 16 | DeviceId = o["DeviceId"]?.ToString() ?? string.Empty, 17 | DriveLetter = o["DriveLetter"]?.ToString() ?? string.Empty 18 | }); 19 | } 20 | 21 | return result; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /OculusLibrary/OculusManifest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace OculusLibrary 5 | { 6 | internal class OculusManifest 7 | { 8 | public string AppId { get; set; } 9 | public string LaunchFile { get; set; } 10 | public string LaunchParameters { get; set; } 11 | public string CanonicalName { get; set; } 12 | 13 | public static OculusManifest Parse(string json) 14 | { 15 | if (string.IsNullOrWhiteSpace(json)) 16 | { 17 | throw new ArgumentException("JSON string cannot be null and empty"); 18 | } 19 | 20 | var manifest = JsonConvert.DeserializeObject(json); 21 | 22 | if (manifest == null) 23 | { 24 | throw new ManifestParseException("Could not deserialise json"); 25 | } 26 | 27 | manifest.LaunchFile = manifest?.LaunchFile?.Replace("/", @"\"); 28 | 29 | return manifest; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /OculusLibrary/OS/RegistryValueProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace OculusLibrary.OS 9 | { 10 | public class RegistryValueProvider : IRegistryValueProvider 11 | { 12 | public RegistryValueProvider() { } 13 | 14 | public List GetSubKeysForPath( 15 | RegistryView platform, 16 | RegistryHive hive, 17 | string path) 18 | { 19 | RegistryKey rootKey = RegistryKey.OpenBaseKey(hive, platform); 20 | 21 | return rootKey 22 | .OpenSubKey(path) 23 | .GetSubKeyNames() 24 | .ToList(); 25 | } 26 | 27 | public string GetValueForPath( 28 | RegistryView platform, 29 | RegistryHive hive, 30 | string path, 31 | string keyName) 32 | { 33 | RegistryKey rootKey = RegistryKey.OpenBaseKey(hive, platform); 34 | 35 | return rootKey 36 | .OpenSubKey(path) 37 | .GetValue(keyName) 38 | .ToString(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /OculusLibrary.Tests/OculusWebsiteParserTests.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute; 2 | using NUnit.Framework; 3 | using OculusLibrary.DataExtraction; 4 | using Playnite.SDK; 5 | using System.IO; 6 | 7 | namespace OculusLibrary.Tests 8 | { 9 | public class OculusWebsiteParserTests 10 | { 11 | private IWebView fakeWebView; 12 | private OculusWebsiteScraper subject; 13 | 14 | [SetUp] 15 | public void Setup() 16 | { 17 | var testHtml = File.ReadAllText($"{TestContext.CurrentContext.TestDirectory}\\demo1.html"); 18 | 19 | fakeWebView = Substitute.For(); 20 | 21 | fakeWebView.GetPageSource() 22 | .Returns(testHtml); 23 | 24 | var fakeLogger = Substitute.For(); 25 | 26 | subject = new OculusWebsiteScraper(fakeLogger); 27 | } 28 | 29 | [TearDown] 30 | public void TearDown() { 31 | fakeWebView = null; 32 | subject = null; 33 | } 34 | 35 | [Test] 36 | public void Game_Name_Correctly_Extracted() 37 | { 38 | var result = subject.ScrapeDataForApplicationId(fakeWebView, "123"); 39 | 40 | Assert.AreEqual("Test Game", result.Name); 41 | } 42 | 43 | [Test] 44 | public void Game_Description_Correctly_Extracted() 45 | { 46 | var result = subject.ScrapeDataForApplicationId(fakeWebView, "123"); 47 | 48 | Assert.AreEqual("This is a test description", result.Description); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /OculusLibrary/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("OculusLibrary")] 9 | [assembly: AssemblyDescription("Playnite Library provider for Oculus")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Shaw Young")] 12 | [assembly: AssemblyProduct("OculusLibrary")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("499d6789-da1d-44b8-9cf3-7ec07666fd6a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /OculusLibrary.Tests/PathNormaliserTests.cs: -------------------------------------------------------------------------------- 1 | using NSubstitute; 2 | using NUnit.Framework; 3 | using OculusLibrary.OS; 4 | using System.Collections.Generic; 5 | 6 | namespace OculusLibrary.Tests 7 | { 8 | public class PathNormaliserTests 9 | { 10 | [Test] 11 | public void Get_Drive_Letter_From_DeviceId() 12 | { 13 | var path = @"\\?\Volume{a0bf4e34-c90f-4005-815c-9a5a485e40ad}\Oculus\Software"; 14 | 15 | var fakeWMOProvider = Substitute.For(); 16 | fakeWMOProvider.GetDriveData().Returns(new List { 17 | new WMODrive { 18 | DeviceId = @"\\?\Volume{DCBDB210-C414-409E-B108-C2BFA7395E1F}\", 19 | DriveLetter = "X:" 20 | }, 21 | new WMODrive { 22 | DeviceId = @"\\?\Volume{DCBDB210-C414-409E-B108-C2BFA7395E1F}\", 23 | DriveLetter = "" 24 | }, 25 | new WMODrive { 26 | DeviceId = @"\\?\Volume{a0bf4e34-c90f-4005-815c-9a5a485e40ad}\", 27 | DriveLetter = "D:" 28 | }, 29 | new WMODrive { 30 | DeviceId = @"\\?\Volume{ECBDB210-D414-509E-B108-C2BFA7395E1F}\", 31 | DriveLetter = "C:" 32 | }, 33 | }); 34 | 35 | using (var subject = new PathNormaliser(fakeWMOProvider)) 36 | { 37 | var normalisedPath = subject.Normalise(path); 38 | Assert.AreEqual(@"D:\Oculus\Software", normalisedPath); 39 | } 40 | 41 | 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 5 | 6 | name: Release Build 7 | 8 | jobs: 9 | build: 10 | runs-on: [windows-2019] 11 | steps: 12 | - name: checkout 13 | uses: actions/checkout@v1 14 | 15 | - name: Setup Nuget.exe 16 | uses: warrenbuckley/Setup-Nuget@v1 17 | 18 | - name: Nuget Restore 19 | run: nuget restore .\OculusLibrary.sln 20 | 21 | - name: Build (DotNET4.6.2) 22 | run: | 23 | cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\" 24 | .\MSBuild.exe $Env:GITHUB_WORKSPACE\OculusLibrary.sln -p:Configuration=Release -restore 25 | 26 | - name: Zip Build artifacts 27 | run: | 28 | Compress-Archive -Path $Env:GITHUB_WORKSPACE\OculusLibrary\bin\Release\* -DestinationPath $Env:GITHUB_WORKSPACE\Oculus.zip 29 | 30 | - name: Create Release 31 | id: create_release 32 | uses: actions/create-release@v1 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 35 | with: 36 | tag_name: ${{ github.ref }} 37 | release_name: Release ${{ github.ref }} 38 | body: | 39 | Changes in this Release 40 | - First Change 41 | - Second Change 42 | draft: false 43 | prerelease: false 44 | 45 | - name: Upload Release Asset 46 | id: upload-release-asset 47 | uses: actions/upload-release-asset@v1.0.1 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | with: 51 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 52 | asset_path: .\Oculus.zip 53 | asset_name: Oculus.zip 54 | asset_content_type: application/zip -------------------------------------------------------------------------------- /OculusLibrary/DataExtraction/OculusWebsiteScraper.cs: -------------------------------------------------------------------------------- 1 | using Playnite.SDK; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | using System.Web.Script.Serialization; 10 | 11 | namespace OculusLibrary.DataExtraction 12 | { 13 | public class OculusWebsiteScraper 14 | { 15 | private readonly JavaScriptSerializer serialiser; 16 | private readonly ILogger logger; 17 | 18 | public OculusWebsiteScraper(ILogger logger) 19 | { 20 | serialiser = new JavaScriptSerializer(); 21 | this.logger = logger; 22 | } 23 | 24 | public OculusWebsiteJson ScrapeDataForApplicationId(IWebView view, string appId) 25 | { 26 | logger.Debug($"Trying to scrape {appId}"); 27 | 28 | // robo recall 1081190428622821 29 | try 30 | { 31 | view.NavigateAndWait($"https://www.oculus.com/experiences/rift/{appId}/"); 32 | var source = view.GetPageSource(); 33 | 34 | // get the json block from the source which contains the games meta data 35 | 36 | Regex regex = new Regex(@" 12 | 13 | 14 | 15 | 16 | 17 | --------------------------------------------------------------------------------