mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-18 18:17:43 +01:00
YouTube Extension with API Auth (& verification) as well as "Search Videos" command (working)
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
src/modules/cmdpal/Exts/YouTubeExtension/Assets/StoreLogo.png
Normal file
BIN
src/modules/cmdpal/Exts/YouTubeExtension/Assets/StoreLogo.png
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace YouTubeExtension.Helper;
|
||||
|
||||
internal sealed partial class LinkAction : InvokableCommand
|
||||
{
|
||||
private readonly YouTubeVideo _video;
|
||||
|
||||
internal LinkAction(YouTubeVideo video)
|
||||
{
|
||||
this._video = video;
|
||||
this.Name = "Open link";
|
||||
this.Icon = new("\uE8A7");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(_video.Link) { UseShellExecute = true });
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YouTubeExtension.Helper;
|
||||
|
||||
internal sealed class YouTubeHelper
|
||||
{
|
||||
internal static string StateJsonPath()
|
||||
{
|
||||
// Get the path to our exe
|
||||
var path = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
|
||||
// Get the directory of the exe
|
||||
var directory = Path.GetDirectoryName(path) ?? string.Empty;
|
||||
|
||||
// now, the state is just next to the exe
|
||||
return Path.Combine(directory, "state.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YouTubeExtension.Helper;
|
||||
|
||||
public sealed class YouTubeVideo
|
||||
{
|
||||
public string Title { get; init; } = string.Empty;
|
||||
|
||||
public string Link { get; init; } = string.Empty;
|
||||
|
||||
public string Author { get; init; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap uap3 rescap">
|
||||
|
||||
<Identity
|
||||
Name="YouTubeExtension"
|
||||
Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
||||
Version="0.0.1.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>YouTube extension for cmdpal</DisplayName>
|
||||
<PublisherDisplayName>A Lone Developer</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="YouTube extension for cmdpal"
|
||||
Description="YouTube extension for cmdpal"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
Square44x44Logo="Assets\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer Executable="YouTubeExtension.exe" Arguments="-RegisterProcessAsComServer" DisplayName="ClementineExtensionApp">
|
||||
<com:Class Id="95696eff-5c44-41ad-8f64-c2182ec9e3fd" DisplayName="YouTube extension for cmdpal" />
|
||||
</com:ExeServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
<uap3:Extension Category="windows.appExtension">
|
||||
<uap3:AppExtension Name="com.microsoft.windows.commandpalette"
|
||||
Id="PG-SP-ID"
|
||||
PublicFolder="Public"
|
||||
DisplayName="YouTube extension for cmdpal"
|
||||
Description="YouTube extension for cmdpal">
|
||||
<uap3:Properties>
|
||||
<CmdPalProvider>
|
||||
<Activation>
|
||||
<CreateInstance ClassId="95696eff-5c44-41ad-8f64-c2182ec9e3fd" />
|
||||
</Activation>
|
||||
<SupportedInterfaces>
|
||||
<Commands/>
|
||||
</SupportedInterfaces>
|
||||
</CmdPalProvider>
|
||||
</uap3:Properties>
|
||||
</uap3:AppExtension>
|
||||
</uap3:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
using YouTubeExtension.Helper;
|
||||
|
||||
namespace YouTubeExtension.Pages;
|
||||
|
||||
internal sealed partial class YouTubeAPIForm : Form
|
||||
{
|
||||
public override string TemplateJson()
|
||||
{
|
||||
var json = $$"""
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.5",
|
||||
"body": [
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"style": "text",
|
||||
"id": "apiKey",
|
||||
"label": "API Key",
|
||||
"isRequired": true,
|
||||
"errorMessage": "API Key required"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save",
|
||||
"data": {
|
||||
"apiKey": "apiKey"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
return json;
|
||||
}
|
||||
|
||||
public override string DataJson() => throw new NotImplementedException();
|
||||
|
||||
public override string StateJson() => throw new NotImplementedException();
|
||||
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var formInput = JsonNode.Parse(payload);
|
||||
if (formInput == null)
|
||||
{
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
|
||||
// get the name and url out of the values
|
||||
var formApiKey = formInput["apiKey"] ?? string.Empty;
|
||||
|
||||
// Construct a new json blob with the name and url
|
||||
var json = $$"""
|
||||
{
|
||||
"apiKey": "{{formApiKey}}"
|
||||
}
|
||||
""";
|
||||
|
||||
File.WriteAllText(YouTubeHelper.StateJsonPath(), json);
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
|
||||
namespace YouTubeExtension.Pages;
|
||||
|
||||
internal sealed partial class YouTubeAPIPage : FormPage
|
||||
{
|
||||
private readonly YouTubeAPIForm apiForm = new();
|
||||
|
||||
public override IForm[] Forms() => [apiForm];
|
||||
|
||||
public YouTubeAPIPage()
|
||||
{
|
||||
Name = "YouTube API";
|
||||
Icon = new("https://www.youtube.com/favicon.ico");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
using YouTubeExtension.Helper;
|
||||
|
||||
namespace YouTubeExtension;
|
||||
|
||||
internal sealed partial class YouTubeVideosPage : DynamicListPage
|
||||
{
|
||||
public YouTubeVideosPage()
|
||||
{
|
||||
Icon = new("https://www.youtube.com/favicon.ico");
|
||||
Name = "YouTube";
|
||||
}
|
||||
|
||||
public override ISection[] GetItems(string query)
|
||||
{
|
||||
return DoGetItems(query).GetAwaiter().GetResult(); // Fetch and await the task synchronously
|
||||
}
|
||||
|
||||
private async Task<ISection[]> DoGetItems(string query)
|
||||
{
|
||||
// Fetch YouTube videos based on the query
|
||||
List<YouTubeVideo> items = await GetYouTubeVideos(query);
|
||||
|
||||
// Create a section and populate it with the video results
|
||||
var section = new ListSection()
|
||||
{
|
||||
Title = "Search Results",
|
||||
Items = items.Select(video => new ListItem(new LinkAction(video))
|
||||
{
|
||||
Title = video.Title,
|
||||
Subtitle = $"{video.Author}",
|
||||
}).ToArray(),
|
||||
};
|
||||
|
||||
return new[] { section }; // Properly return an array of sections
|
||||
}
|
||||
|
||||
// Method to fetch videos from YouTube API
|
||||
private static async Task<List<YouTubeVideo>> GetYouTubeVideos(string query)
|
||||
{
|
||||
var state = File.ReadAllText(YouTubeHelper.StateJsonPath());
|
||||
var jsonState = JsonNode.Parse(state);
|
||||
var apiKey = jsonState["apiKey"]?.ToString() ?? string.Empty;
|
||||
|
||||
var videos = new List<YouTubeVideo>();
|
||||
|
||||
using HttpClient client = new HttpClient();
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await client.GetStringAsync($"https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q={query}&key={apiKey}");
|
||||
var json = JsonNode.Parse(response);
|
||||
|
||||
if (json?["items"] is JsonArray itemsArray)
|
||||
{
|
||||
foreach (var item in itemsArray)
|
||||
{
|
||||
videos.Add(new YouTubeVideo
|
||||
{
|
||||
Title = item["snippet"]?["title"]?.ToString() ?? string.Empty,
|
||||
Link = $"https://www.youtube.com/watch?v={item["id"]?["videoId"]?.ToString()}",
|
||||
Author = item["snippet"]?["channelTitle"]?.ToString() ?? string.Empty,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Handle any errors from the API call or parsing
|
||||
videos.Add(new YouTubeVideo
|
||||
{
|
||||
Title = "Error fetching data",
|
||||
Link = string.Empty,
|
||||
Author = $"Error: {ex.Message}",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return videos;
|
||||
}
|
||||
}
|
||||
36
src/modules/cmdpal/Exts/YouTubeExtension/Program.cs
Normal file
36
src/modules/cmdpal/Exts/YouTubeExtension/Program.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
|
||||
namespace YouTubeExtension;
|
||||
|
||||
public class Program
|
||||
{
|
||||
[MTAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0 && args[0] == "-RegisterProcessAsComServer")
|
||||
{
|
||||
using ExtensionServer server = new();
|
||||
var extensionDisposedEvent = new ManualResetEvent(false);
|
||||
var extensionInstance = new SampleExtension(extensionDisposedEvent);
|
||||
|
||||
// We are instantiating an extension instance once above, and returning it every time the callback in RegisterExtension below is called.
|
||||
// This makes sure that only one instance of SampleExtension is alive, which is returned every time the host asks for the IExtension object.
|
||||
// If you want to instantiate a new instance each time the host asks, create the new instance inside the delegate.
|
||||
server.RegisterExtension(() => extensionInstance);
|
||||
|
||||
// This will make the main thread wait until the event is signalled by the extension class.
|
||||
// Since we have single instance of the extension object, we exit as sooon as it is disposed.
|
||||
extensionDisposedEvent.WaitOne();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Not being launched as a Extension... exiting.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"profiles": {
|
||||
"YouTubeExtension (Package)": {
|
||||
"commandName": "MsixPackage",
|
||||
"commandLineArgs": "-RegisterProcessAsComServer"
|
||||
},
|
||||
"YouTubeExtension (Unpackaged)": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/modules/cmdpal/Exts/YouTubeExtension/SampleExtension.cs
Normal file
39
src/modules/cmdpal/Exts/YouTubeExtension/SampleExtension.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
|
||||
namespace YouTubeExtension;
|
||||
|
||||
[ComVisible(true)]
|
||||
[Guid("95696eff-5c44-41ad-8f64-c2182ec9e3fd")]
|
||||
[ComDefaultInterface(typeof(IExtension))]
|
||||
public sealed partial class SampleExtension : IExtension
|
||||
{
|
||||
private readonly ManualResetEvent _extensionDisposedEvent;
|
||||
|
||||
public SampleExtension(ManualResetEvent extensionDisposedEvent)
|
||||
{
|
||||
this._extensionDisposedEvent = extensionDisposedEvent;
|
||||
}
|
||||
|
||||
public object GetProvider(ProviderType providerType)
|
||||
{
|
||||
switch (providerType)
|
||||
{
|
||||
case ProviderType.Commands:
|
||||
return new YouTubeExtensionActionsProvider();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this._extensionDisposedEvent.Set();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>YouTubeExtension</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
|
||||
<UseWinUI>false</UseWinUI>
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CmdPal.Extensions.Helpers\Microsoft.CmdPal.Extensions.Helpers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
Tools extension to be activated for this project even if the Windows App SDK Nuget
|
||||
package has not yet been restored.
|
||||
-->
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Data\" />
|
||||
<Folder Include="Forms\" />
|
||||
<Folder Include="Commands\" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
|
||||
Explorer "Package and Publish" context menu entry to be enabled for this project even if
|
||||
the Windows App SDK Nuget package has not yet been restored.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CmdPal.Extensions;
|
||||
using Microsoft.CmdPal.Extensions.Helpers;
|
||||
using Windows.UI.ApplicationSettings;
|
||||
using YouTubeExtension.Helper;
|
||||
using YouTubeExtension.Pages;
|
||||
|
||||
namespace YouTubeExtension;
|
||||
|
||||
public partial class YouTubeExtensionActionsProvider : ICommandProvider
|
||||
{
|
||||
public string DisplayName => $"YouTube";
|
||||
|
||||
public IconDataType Icon => new(string.Empty);
|
||||
|
||||
private readonly IListItem[] _commands = [
|
||||
new ListItem(new YouTubeVideosPage()) { Title = "Search Videos", Subtitle = "YouTube" },
|
||||
];
|
||||
|
||||
private readonly YouTubeAPIPage apiPage = new();
|
||||
|
||||
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
|
||||
public void Dispose() => throw new NotImplementedException();
|
||||
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
|
||||
|
||||
public IListItem[] TopLevelCommands()
|
||||
{
|
||||
return TopLevelCommandsAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task<IListItem[]> TopLevelCommandsAsync()
|
||||
{
|
||||
var settingsPath = YouTubeHelper.StateJsonPath();
|
||||
|
||||
// Check if the settings file exists
|
||||
if (!File.Exists(settingsPath))
|
||||
{
|
||||
return new[] { new ListItem(apiPage) { Title = "YouTube", Subtitle = "Enter your API key." } };
|
||||
}
|
||||
|
||||
// Read the file and parse the API key
|
||||
var state = File.ReadAllText(settingsPath);
|
||||
var jsonState = System.Text.Json.Nodes.JsonNode.Parse(state);
|
||||
var apiKey = jsonState?["apiKey"]?.ToString() ?? string.Empty;
|
||||
|
||||
// Validate the API key using YouTube API
|
||||
if (string.IsNullOrWhiteSpace(apiKey) || !await IsApiKeyValid(apiKey))
|
||||
{
|
||||
return new[] { new ListItem(apiPage) { Title = "YouTube", Subtitle = "Current API Invalid. Please enter a new API key." } };
|
||||
}
|
||||
|
||||
// If file exists and API key is valid, return commands
|
||||
return _commands;
|
||||
}
|
||||
|
||||
// Method to check if the API key is valid by making a simple request to the YouTube API
|
||||
private static async Task<bool> IsApiKeyValid(string apiKey)
|
||||
{
|
||||
using HttpClient client = new HttpClient();
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make a simple request to verify the API key, such as fetching a video
|
||||
var response = await client.GetAsync($"https://www.googleapis.com/youtube/v3/search?part=snippet&type=video&q=test&key={apiKey}");
|
||||
|
||||
// If the response status code is 200, the API key is valid
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Optionally, handle other status codes and log errors
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If any exception occurs (e.g., network error), consider the API key invalid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/modules/cmdpal/Exts/YouTubeExtension/app.manifest
Normal file
19
src/modules/cmdpal/Exts/YouTubeExtension/app.manifest
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="HackerNewsExtension.app"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
|
||||
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
|
||||
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
@@ -42,6 +42,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SSHKeychainExtension", "ext
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SamplePagesExtension", "exts\SamplePagesExtension\SamplePagesExtension.csproj", "{399B53F1-5AA6-4FE0-8C3C-66E07B84E6F1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YouTubeExtension", "Exts\YouTubeExtension\YouTubeExtension.csproj", "{276243F6-4B25-411C-B110-1E7DB575F13D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -198,6 +200,18 @@ Global
|
||||
{399B53F1-5AA6-4FE0-8C3C-66E07B84E6F1}.Release|x64.ActiveCfg = Release|x64
|
||||
{399B53F1-5AA6-4FE0-8C3C-66E07B84E6F1}.Release|x64.Build.0 = Release|x64
|
||||
{399B53F1-5AA6-4FE0-8C3C-66E07B84E6F1}.Release|x64.Deploy.0 = Release|x64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Debug|x64.Build.0 = Debug|x64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Release|x64.ActiveCfg = Release|x64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Release|x64.Build.0 = Release|x64
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D}.Release|x64.Deploy.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -219,6 +233,7 @@ Global
|
||||
{7F6796A4-4233-4CEC-914F-95EC7A5283A0} = {272D0E9A-8FC3-49F5-8FAD-79ABAE8AB1E4}
|
||||
{77D99BE0-F69C-4F27-8153-951CEC5110FE} = {B7FF739F-7716-4FC3-B622-705486187B87}
|
||||
{399B53F1-5AA6-4FE0-8C3C-66E07B84E6F1} = {B7FF739F-7716-4FC3-B622-705486187B87}
|
||||
{276243F6-4B25-411C-B110-1E7DB575F13D} = {B7FF739F-7716-4FC3-B622-705486187B87}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {BC94BFC2-A741-4978-B6A4-9E01B7660E6B}
|
||||
|
||||
Reference in New Issue
Block a user