[玩转系统] 如何创建反馈提供者
作者:精品下载站 日期:2024-12-14 02:56:31 浏览:14 分类:玩电脑
如何创建反馈提供者
PowerShell 7.4 引入了反馈提供者的概念。反馈提供程序是一个 PowerShell 模块,它实现了 IFeedbackProvider 接口,以根据用户命令执行尝试提供命令建议。当执行成功或失败时,将触发提供程序。反馈提供者使用成功或失败的信息来提供反馈。
先决条件
要创建反馈提供者,您必须满足以下先决条件:
安装 PowerShell 7.4 或更高版本
- 您必须启用
PSFeedbackProvider
实验性功能才能启用对反馈提供程序和预测器的支持。有关更多信息,请参阅使用实验功能。
- 您必须启用
安装 .NET 8 SDK - 8.0.0 或更高版本
- 请参阅下载 .NET 8.0 页面以获取最新版本的 SDK。
反馈提供者概述
反馈提供程序是一个实现 System.Management.Automation.Subsystem.Feedback.IFeedbackProvider 接口的 PowerShell 二进制模块。该接口声明了根据命令行输入获取反馈的方法。反馈接口可以根据用户调用的命令的成功或失败来提供建议。这些建议可以是您想要的任何内容。例如,您可能会建议解决错误的方法或更好的做法,例如避免使用别名。有关更多信息,请参阅什么是反馈提供者?博客文章。
下图显示了反馈提供者的架构:
以下示例将引导您完成创建简单反馈提供程序的过程。此外,您还可以使用命令预测器界面注册提供程序,以向命令行预测器体验添加反馈建议。有关预测器的更多信息,请参阅在 PSReadLine 中使用预测器和如何创建命令行预测器。
第 1 步 - 创建一个新的类库项目
使用以下命令在项目目录中创建一个新项目:
dotnet new classlib --name MyFeedbackProvider
将 System.Management.Automation
包的包引用添加到 .csproj
文件中。以下示例显示了更新后的 .csproj 文件:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Management.Automation" Version="7.4.0-preview.3">
<ExcludeAssets>contentFiles</ExcludeAssets>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
笔记
您应该更改 System.Management.Automation
程序集的版本,以匹配您所定位的 PowerShell 预览版的版本。最低版本是 7.4.0-preview.3。
第 2 步 - 添加提供商的类定义
更改 Class1.cs
文件的名称以匹配您的提供商的名称。此示例使用 myFeedbackProvider.cs
。该文件包含定义反馈提供者的两个主要类。以下示例显示了类定义的基本模板。
using System.Management.Automation;
using System.Management.Automation.Subsystem;
using System.Management.Automation.Subsystem.Feedback;
using System.Management.Automation.Subsystem.Prediction;
using System.Management.Automation.Language;
namespace myFeedbackProvider;
public sealed class myFeedbackProvider : IFeedbackProvider, ICommandPredictor
{
}
public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
{
}
第 3 步 - 实现 Init 类
Init
类向子系统管理器注册和取消注册反馈提供者。 OnImport()
方法在加载二进制模块时运行。 OnRemove()
方法在删除二进制模块时运行。此示例注册了反馈提供程序和命令预测器子系统。
public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
{
private const string Id = "<ADD YOUR GUID HERE>";
public void OnImport()
{
var feedback = new myFeedbackProvider(Id);
SubsystemManager.RegisterSubsystem(SubsystemKind.FeedbackProvider, feedback);
SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, feedback);
}
public void OnRemove(PSModuleInfo psModuleInfo)
{
SubsystemManager.UnregisterSubsystem<ICommandPredictor>(new Guid(Id));
SubsystemManager.UnregisterSubsystem<IFeedbackProvider>(new Guid(Id));
}
}
将 <ADD YOUR GUID HERE>
占位符值替换为唯一的 Guid。您可以使用 New-Guid
cmdlet 生成 Guid。
New-Guid
Guid 是您的提供商的唯一标识符。提供者必须有一个唯一的 ID 才能向子系统注册。
第 4 步 - 添加类成员并定义构造函数
以下代码实现接口中定义的属性,添加所需的类成员,并创建 myFeedbackProvider
类的构造函数。
/// <summary>
/// Gets the global unique identifier for the subsystem implementation.
/// </summary>
private readonly Guid _guid;
public Guid Id => _guid;
/// <summary>
/// Gets the name of a subsystem implementation, this will be the name displayed when triggered
/// </summary>
public string Name => "myFeedbackProvider";
/// <summary>
/// Gets the description of a subsystem implementation.
/// </summary>
public string Description => "This is very simple feedback provider";
/// <summary>
/// Default implementation. No function is required for a feedback provider.
/// </summary>
Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;
/// <summary>
/// Gets the types of trigger for this feedback provider.
/// </summary>
/// <remarks>
/// The default implementation triggers a feedback provider by <see cref="FeedbackTrigger.CommandNotFound"/> only.
/// </remarks>
public FeedbackTrigger Trigger => FeedbackTrigger.All;
/// <summary>
/// List of candidates from the feedback provider to be passed as predictor results
/// </summary>
private List<string>? _candidates;
/// <summary>
/// PowerShell session used to run PowerShell commands that help create suggestions.
/// </summary>
private PowerShell _powershell;
internal myFeedbackProvider(string guid)
{
_guid = new Guid(guid); // Save guid
_powershell = PowerShell.Create(); // Create PowerShell instance
}
第 5 步 - 创建 GetFeedback() 方法
GetFeedback
方法采用两个参数:context
和 token
。 context
参数接收有关触发器的信息,以便您可以决定如何响应建议。 token
参数用于取消。此函数返回包含建议的 FeedbackItem
。
/// <summary>
/// Gets feedback based on the given commandline and error record.
/// </summary>
/// <param name="context">The context for the feedback call.</param>
/// <param name="token">The cancellation token to cancel the operation.</param>
/// <returns>The feedback item.</returns>
public FeedbackItem? GetFeedback(FeedbackContext context, CancellationToken token)
{
// Target describes the different kinds of triggers to activate on,
var target = context.Trigger;
var commandLine = context.CommandLine;
var ast = context.CommandLineAst;
// defining the header and footer variables
string header;
string footer;
// List of the actions
List<string>? actions = new List<string>();
// Trigger on success code goes here
// Trigger on error code goes here
return null;
}
下图显示了如何在向用户显示的建议中使用这些字段。
为成功触发器创建建议
为了成功调用,我们希望扩展上次执行中使用的所有别名。使用 CommandLineAst,我们可以识别任何别名命令,并创建建议以使用完全限定的命令名称。
// Trigger on success
if (target == FeedbackTrigger.Success)
{
// Getting the commands from the AST and only finding those that are Commands
var astCmds = ast.FindAll((cAst) => cAst is CommandAst, true);
// Inspect each of the commands
foreach(var command in astCmds)
{
// Get the command name
var aliasedCmd = ((CommandAst) command).GetCommandName();
// Check if its an alias or not, if so then add it to the list of actions
if(TryGetAlias(aliasedCmd, out string commandString))
{
actions.Add($"{aliasedCmd} --> {commandString}");
}
}
// If no alias was found return null
if(actions.Count == 0)
{
return null;
}
// If aliases are found, set the header to a description and return a new FeedbackItem.
header = "You have used an aliased command:";
// Copy actions to _candidates for the predictor
_candidates = actions;
return new FeedbackItem(header, actions);
}
实现 TryGetAlias() 方法
TryGetAlias()
方法是一个私有辅助函数,它返回一个布尔值来指示该命令是否是别名。在类构造函数中,我们创建了一个可用于运行 PowerShell 命令的 PowerShell 实例。 TryGetAlias()
方法使用此 PowerShell 实例调用 GetCommand
方法来确定该命令是否是别名。 GetCommand
返回的 AliasInfo
对象包含别名命令的完整名称。
/// <summary>
/// Checks if a command is an alias.
/// </summary>
/// <param name="command">The command to check if alias</param>
/// <param name="targetCommand">The referenced command by the aliased command</param>
/// <returns>True if an alias and false if not</returns>
private bool TryGetAlias(string command, out string targetCommand)
{
// Create PowerShell runspace as a session state proxy to run GetCommand and check
// if its an alias
AliasInfo? pwshAliasInfo =
_powershell.Runspace.SessionStateProxy.InvokeCommand.GetCommand(command, CommandTypes.Alias) as AliasInfo;
// if its null then it is not an aliased command so just return false
if(pwshAliasInfo is null)
{
targetCommand = String.Empty;
return false;
}
// Set targetCommand to referenced command name
targetCommand = pwshAliasInfo.ReferencedCommand.Name;
return true;
}
为失败触发器创建建议
当命令执行失败时,我们希望建议用户Get-Help
来获取有关如何使用该命令的更多信息。
// Trigger on error
if (target == FeedbackTrigger.Error)
{
// Gets the command that caused the error.
var erroredCommand = context.LastError?.InvocationInfo.MyCommand;
if (erroredCommand is null)
{
return null;
}
header = $"You have triggered an error with the command {erroredCommand}. Try using the following command to get help:";
actions.Add($"Get-Help {erroredCommand}");
footer = $"You can also check online documentation at https://learn.microsoft.com/en-us/powershell/module/?term={erroredCommand}";
// Copy actions to _candidates for the predictor
_candidates = actions;
return new FeedbackItem(header, actions, footer, FeedbackDisplayLayout.Portrait);
}
第 6 步 - 向命令行预测器发送建议
反馈提供程序增强用户体验的另一种方法是向 ICommandPredictor 界面提供命令建议。有关创建命令行预测器的更多信息,请参阅如何创建命令行预测器。
以下代码实现 ICommandPredictor 接口所需的方法,以将预测器行为添加到反馈提供程序。
CanAcceptFeedback()
- 此方法返回一个布尔值,指示预测器是否接受特定类型的反馈。GetSuggestion()
- 此方法返回一个SuggestionPackage
对象,其中包含预测器要显示的建议。OnCommandLineAccepted()
- 当命令行被接受执行时调用此方法。
/// <summary>
/// Gets a value indicating whether the predictor accepts a specific kind of feedback.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="feedback">A specific type of feedback.</param>
/// <returns>True or false, to indicate whether the specific feedback is accepted.</returns>
public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback)
{
return feedback switch
{
PredictorFeedbackKind.CommandLineAccepted => true,
_ => false,
};
}
/// <summary>
/// Get the predictive suggestions. It indicates the start of a suggestion rendering session.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="context">The <see cref="PredictionContext"/> object to be used for prediction.</param>
/// <param name="cancellationToken">The cancellation token to cancel the prediction.</param>
/// <returns>An instance of <see cref="SuggestionPackage"/>.</returns>
public SuggestionPackage GetSuggestion(
PredictionClient client,
PredictionContext context,
CancellationToken cancellationToken)
{
if (_candidates is not null)
{
string input = context.InputAst.Extent.Text;
List<PredictiveSuggestion>? result = null;
foreach (string c in _candidates)
{
if (c.StartsWith(input, StringComparison.OrdinalIgnoreCase))
{
result ??= new List<PredictiveSuggestion>(_candidates.Count);
result.Add(new PredictiveSuggestion(c));
}
}
if (result is not null)
{
return new SuggestionPackage(result);
}
}
return default;
}
/// <summary>
/// A command line was accepted to execute.
/// The predictor can start processing early as needed with the latest history.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="history">History command lines provided as references for prediction.</param>
public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history)
{
// Reset the candidate state once the command is accepted.
_candidates = null;
}
第 7 步 - 构建反馈提供者
现在您已准备好构建并开始使用您的反馈提供程序!要构建项目,请运行以下命令:
dotnet build
此命令在项目文件夹的以下路径中将 PowerShell 模块创建为 DLL 文件:bin/Debug/net8.0/myFeedbackProvider
在 Windows 计算机上构建时,您可能会遇到错误 error NU1101: Unable to find package System.Management.Automation.
。要解决此问题,请将 nuget.config
文件添加到项目目录中并添加以下内容:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
</configuration>
使用反馈提供者
要测试新的反馈提供程序,请将编译的模块导入到 PowerShell 会话中。这可以通过导入构建成功后描述的文件夹来完成:
Import-Module ./bin/Debug/net8.0/myFeedbackProvider
一旦您对模块感到满意,您应该创建一个模块清单,将其发布到 PowerShell 库,并将其安装在您的 $env:PSModulePath
中。有关更多信息,请参阅如何创建模块清单。您可以将 Import-Module
命令添加到 $PROFILE
脚本中,以便该模块在 PowerShell 会话中可用。
您可以使用以下命令获取已安装的反馈提供程序的列表:
Get-PSSubsystem -Kind FeedbackProvider
Kind SubsystemType IsRegistered Implementations
---- ------------- ------------ ---------------
FeedbackProvider IFeedbackProvider True {general}
笔记
Get-PSSubsystem
是 PowerShell 7.1 中引入的实验性 cmdlet。您必须启用 PSSubsystemPluginModel
实验性功能才能使用此 cmdlet。有关更多信息,请参阅使用实验功能。
以下屏幕截图显示了新提供商的一些示例建议。
以下 GIF 显示了新提供商的预测器集成如何工作。
其他反馈提供者
我们创建了其他反馈提供程序,可以用作更深入示例的良好参考。
未找到命令
command-not-found
反馈提供程序利用 Linux 系统上的 command-not-found
实用工具在尝试运行本机命令但丢失时提供建议。您可以在 GitHub 存储库中找到代码,也可以在 PowerShell Gallery 上自行下载。
PowerShell 适配器
Microsoft.PowerShell.PowerShellAdapter
是一个反馈提供程序,可帮助您将本机命令的文本输出转换为 PowerShell 对象。它会检测您系统上的“适配器”,并建议您在使用本机命令时使用它们。您可以从 PowerShell 适配器反馈提供者博客文章中了解有关 PowerShell 适配器的更多信息。您还可以在 GitHub 存储库中找到代码,或者可以在 PowerShell Gallery 上自行下载。
附录 - 完整的实现代码
以下代码将前面的示例合并到提供程序类的 find 完整实现中。
using System.Management.Automation;
using System.Management.Automation.Subsystem;
using System.Management.Automation.Subsystem.Feedback;
using System.Management.Automation.Subsystem.Prediction;
using System.Management.Automation.Language;
namespace myFeedbackProvider;
public sealed class myFeedbackProvider : IFeedbackProvider, ICommandPredictor
{
/// <summary>
/// Gets the global unique identifier for the subsystem implementation.
/// </summary>
private readonly Guid _guid;
public Guid Id => _guid;
/// <summary>
/// Gets the name of a subsystem implementation, this will be the name displayed when triggered
/// </summary>
public string Name => "myFeedbackProvider";
/// <summary>
/// Gets the description of a subsystem implementation.
/// </summary>
public string Description => "This is very simple feedback provider";
/// <summary>
/// Default implementation. No function is required for a feedback provider.
/// </summary>
Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;
/// <summary>
/// Gets the types of trigger for this feedback provider.
/// </summary>
/// <remarks>
/// The default implementation triggers a feedback provider by <see cref="FeedbackTrigger.CommandNotFound"/> only.
/// </remarks>
public FeedbackTrigger Trigger => FeedbackTrigger.All;
/// <summary>
/// List of candidates from the feedback provider to be passed as predictor results
/// </summary>
private List<string>? _candidates;
/// <summary>
/// PowerShell session used to run PowerShell commands that help create suggestions.
/// </summary>
private PowerShell _powershell;
// Constructor
internal myFeedbackProvider(string guid)
{
_guid = new Guid(guid); // Save guid
_powershell = PowerShell.Create(); // Create PowerShell instance
}
#region IFeedbackProvider
/// <summary>
/// Gets feedback based on the given commandline and error record.
/// </summary>
/// <param name="context">The context for the feedback call.</param>
/// <param name="token">The cancellation token to cancel the operation.</param>
/// <returns>The feedback item.</returns>
public FeedbackItem? GetFeedback(FeedbackContext context, CancellationToken token)
{
// Target describes the different kinds of triggers to activate on,
var target = context.Trigger;
var commandLine = context.CommandLine;
var ast = context.CommandLineAst;
// defining the header and footer variables
string header;
string footer;
// List of the actions
List<string>? actions = new List<string>();
// Trigger on success
if (target == FeedbackTrigger.Success)
{
// Getting the commands from the AST and only finding those that are Commands
var astCmds = ast.FindAll((cAst) => cAst is CommandAst, true);
// Inspect each of the commands
foreach(var command in astCmds)
{
// Get the command name
var aliasedCmd = ((CommandAst) command).GetCommandName();
// Check if its an alias or not, if so then add it to the list of actions
if(TryGetAlias(aliasedCmd, out string commandString))
{
actions.Add($"{aliasedCmd} --> {commandString}");
}
}
// If no alias was found return null
if(actions.Count == 0)
{
return null;
}
// If aliases are found, set the header to a description and return a new FeedbackItem.
header = "You have used an aliased command:";
// Copy actions to _candidates for the predictor
_candidates = actions;
return new FeedbackItem(header, actions);
}
// Trigger on error
if (target == FeedbackTrigger.Error)
{
// Gets the command that caused the error.
var erroredCommand = context.LastError?.InvocationInfo.MyCommand;
if (erroredCommand is null)
{
return null;
}
header = $"You have triggered an error with the command {erroredCommand}. Try using the following command to get help:";
actions.Add($"Get-Help {erroredCommand}");
footer = $"You can also check online documentation at https://learn.microsoft.com/en-us/powershell/module/?term={erroredCommand}";
// Copy actions to _candidates for the predictor
_candidates = actions;
return new FeedbackItem(header, actions, footer, FeedbackDisplayLayout.Portrait);
}
return null;
}
/// <summary>
/// Checks if a command is an alias.
/// </summary>
/// <param name="command">The command to check if alias</param>
/// <param name="targetCommand">The referenced command by the aliased command</param>
/// <returns>True if an alias and false if not</returns>
private bool TryGetAlias(string command, out string targetCommand)
{
// Create PowerShell runspace as a session state proxy to run GetCommand and check
// if its an alias
AliasInfo? pwshAliasInfo =
_powershell.Runspace.SessionStateProxy.InvokeCommand.GetCommand(command, CommandTypes.Alias) as AliasInfo;
// if its null then it is not an aliased command so just return false
if(pwshAliasInfo is null)
{
targetCommand = String.Empty;
return false;
}
// Set targetCommand to referenced command name
targetCommand = pwshAliasInfo.ReferencedCommand.Name;
return true;
}
#endregion IFeedbackProvider
#region ICommandPredictor
/// <summary>
/// Gets a value indicating whether the predictor accepts a specific kind of feedback.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="feedback">A specific type of feedback.</param>
/// <returns>True or false, to indicate whether the specific feedback is accepted.</returns>
public bool CanAcceptFeedback(PredictionClient client, PredictorFeedbackKind feedback)
{
return feedback switch
{
PredictorFeedbackKind.CommandLineAccepted => true,
_ => false,
};
}
/// <summary>
/// Get the predictive suggestions. It indicates the start of a suggestion rendering session.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="context">The <see cref="PredictionContext"/> object to be used for prediction.</param>
/// <param name="cancellationToken">The cancellation token to cancel the prediction.</param>
/// <returns>An instance of <see cref="SuggestionPackage"/>.</returns>
public SuggestionPackage GetSuggestion(
PredictionClient client,
PredictionContext context,
CancellationToken cancellationToken)
{
if (_candidates is not null)
{
string input = context.InputAst.Extent.Text;
List<PredictiveSuggestion>? result = null;
foreach (string c in _candidates)
{
if (c.StartsWith(input, StringComparison.OrdinalIgnoreCase))
{
result ??= new List<PredictiveSuggestion>(_candidates.Count);
result.Add(new PredictiveSuggestion(c));
}
}
if (result is not null)
{
return new SuggestionPackage(result);
}
}
return default;
}
/// <summary>
/// A command line was accepted to execute.
/// The predictor can start processing early as needed with the latest history.
/// </summary>
/// <param name="client">Represents the client that initiates the call.</param>
/// <param name="history">History command lines provided as references for prediction.</param>
public void OnCommandLineAccepted(PredictionClient client, IReadOnlyList<string> history)
{
// Reset the candidate state once the command is accepted.
_candidates = null;
}
#endregion;
}
public class Init : IModuleAssemblyInitializer, IModuleAssemblyCleanup
{
private const string Id = "<ADD YOUR GUID HERE>";
public void OnImport()
{
var feedback = new myFeedbackProvider(Id);
SubsystemManager.RegisterSubsystem(SubsystemKind.FeedbackProvider, feedback);
SubsystemManager.RegisterSubsystem(SubsystemKind.CommandPredictor, feedback);
}
public void OnRemove(PSModuleInfo psModuleInfo)
{
SubsystemManager.UnregisterSubsystem<ICommandPredictor>(new Guid(Id));
SubsystemManager.UnregisterSubsystem<IFeedbackProvider>(new Guid(Id));
}
}
猜你还喜欢
- 03-30 [玩转系统] 如何用批处理实现关机,注销,重启和锁定计算机
- 02-14 [系统故障] Win10下报错:该文件没有与之关联的应用来执行该操作
- 01-07 [系统问题] Win10--解决锁屏后会断网的问题
- 01-02 [系统技巧] Windows系统如何关闭防火墙保姆式教程,超详细
- 12-15 [玩转系统] 如何在 Windows 10 和 11 上允许多个 RDP 会话
- 12-15 [玩转系统] 查找 Exchange/Microsoft 365 中不活动(未使用)的通讯组列表
- 12-15 [玩转系统] 如何在 Windows 上安装远程服务器管理工具 (RSAT)
- 12-15 [玩转系统] 如何在 Windows 上重置组策略设置
- 12-15 [玩转系统] 如何获取计算机上的本地管理员列表?
- 12-15 [玩转系统] 在 Visual Studio Code 中连接到 MS SQL Server 数据库
- 12-15 [玩转系统] 如何降级 Windows Server 版本或许可证
- 12-15 [玩转系统] 如何允许非管理员用户在 Windows 中启动/停止服务
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[影视] 黑道中人 Alto Knights(2025)剧情 犯罪 历史 电影
[古装剧] [七侠五义][全75集][WEB-MP4/76G][国语无字][1080P][焦恩俊经典]
[实用软件] 虚拟手机号 电话 验证码 注册
[电视剧] 安眠书店/你 第五季 You Season 5 (2025) 【全10集】
[电视剧] 棋士(2025) 4K 1080P【全22集】悬疑 犯罪 王宝强 陈明昊
[软件合集] 25年6月5日 精选软件22个
[软件合集] 25年6月4日 精选软件36个
[短剧] 2025年06月04日 精选+付费短剧推荐33部
[短剧] 2025年06月03日 精选+付费短剧推荐25部
[软件合集] 25年6月3日 精选软件44个
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电视剧] 欢乐颂.5部全 (2016-2024)
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[影视] 【稀有资源】香港老片 艺坛照妖镜之96应召名册 (1996)
[剧集] 神经风云(2023)(完结).4K
[剧集] [BT] [TVB] [黑夜彩虹(2003)] [全21集] [粤语中字] [TV-RMVB]
[实用软件] 虚拟手机号 电话 验证码 注册
[资源] B站充电视频合集,包含多位重量级up主,全是大佬真金白银买来的~【99GB】
[影视] 内地绝版高清录像带 [mpg]
[书籍] 古今奇书禁书三教九流资料大合集 猎奇必备珍藏资源PDF版 1.14G
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电影] 美国队长4 4K原盘REMUX 杜比视界 内封简繁英双语字幕 49G
[电影] 死神来了(1-6)大合集!
[软件合集] 25年05月13日 精选软件16个
[精品软件] 25年05月15日 精选软件18个
[绝版资源] 南与北 第1-2季 合集 North and South (1985) /美国/豆瓣: 8.8[1080P][中文字幕]
[软件] 25年05月14日 精选软件57个
[短剧] 2025年05月14日 精选+付费短剧推荐39部
[短剧] 2025年05月15日 精选+付费短剧推荐36部
- 最新评论
-
- 热门tag