是否厌倦了在DVD和蓝光光盘之间切换?或者在旅行时无法使用智能电视的DLNA服务?本文将介绍如何创建一个命令行工具,它能够为选择的电视剧创建一个可通过Web浏览的界面。这个工具使用themoviedb.org的REST API来获取电视剧的信息,可以通过浏览电视剧根目录下的index.html文件来访问所有视频,包括系列、季节和剧集信息。
一些读者可能会认为这个工具可以用来浏览他们非法获取的媒体内容。虽然无法阻止如何使用这个工具,但不鼓励这种行为。实际上,支付想要的内容通常比运行VPN更便宜。
需要一台能够构建Visual Studio .NET C#控制台应用程序项目的机器。
需要从themoviedb.org获取一个授权令牌。创建一个免费账户,登录后,进入账户菜单设置|API以创建或获取授权令牌。不要将其与API密钥混淆。
增加了配置文件支持。修复了错误。
这个项目的大部分内容都是使用ASP.NET页面绑定到从REST API返回的JSON。csppg项目用于将.aspx文件转换为可以从应用程序中执行的C#代码。csppg可执行文件在预构建事件中引用。
为了便于数据绑定到JSON,使用了这种代码的变体。
有一个核心的Tmdb对象,它处理应用程序的一些核心功能,比如执行REST查询或下载文件。
为了处理命令行解析和其他细节,这个项目使用了Program.Base项目。
在下面的例子中,AUTH_TOKEN应该是从themoviedb网站获得的大丑哈希值。
搜索所有包含"burn"的电视剧名称,并将它们列出:
tv2html "burn" AUTH_TOKEN
搜索"Burn Notice" - 将生成到"TV"子目录,因为它将是唯一的匹配项:
tv2html "burn notice" AUTH_TOKEN /output TV
生成电视剧ID 2919 (Burn Notice),并将结果写入当前目录。
tv2html #2919 AUTH_TOKEN
请注意,可以通过在可执行文件的工作目录中提供一个tv2html.config文件来省略命令行上的认证令牌。配置文件类似于.NET框架项目中的App.config,但为了跨平台原因,不包括可执行文件扩展名:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="authToken" value="AUTH_TOKEN"/>
</appSettings>
</configuration>
将AUTH_TOKEN替换为认证令牌。解决方案文件夹中提供了一个模板。
大部分工作都是由"aspx"文件完成的。这些不是真正的ASP.NET页面。它们不支持相同的指令,首先,它们只支持C#。此外,没有涉及Web服务器。相反,根解决方案文件夹中的csppg工具用于将这些aspx风格的文件转换为可以从应用程序中调用的C#代码。使用这种方法,以及.aspx扩展名,是为了获得T4引擎不提供的智能感知,而且这个工具不需要像T4那样的大量投入。
主要的Program.cs的Run()方法只是处理最后一英里的参数逻辑和初始获取,以及运行模板。
series.index.aspx负责顶级的index.html。
season.index.aspx负责每个季节的index.html。
episode.aspx负责每个剧集的html文件。
如果查看这些文件的内容,会看到大量使用dynamic关键字。这允许C#几乎像它们是C#中的本地静态类型对象一样,对JSON元素进行后期绑定。
否则,索引文件只是灵活的网格布局,包含季节或剧集数据。每个页面都有一个弹出式导航侧边栏。剧集页面有一个视频,或者如果找不到视频,就有一个静态图像。
特别值得注意的是Tmdb.cs文件,它处理一些关键的应用程序功能,包括将JSON元素作为后期可绑定对象公开,与REST服务器接口,以及下载文件。在介绍中链接的文章中介绍了后期绑定到JSON,但在这里发布了该文件的其他功能:
public static string GetSafeFilename(string file)
{
var result = "";
var inv = Path.GetInvalidFileNameChars();
var sb = new StringBuilder();
sb.Clear();
for (int j = 0; j < file.Length; j++)
{
if (Array.IndexOf(inv, file[j]) > -1)
{
sb.Append('_');
}
else
{
sb.Append(file[j]);
}
}
result = Path.Combine(result, sb.ToString());
return result;
}
public static JsonDocument GetJson(string url)
{
JsonDocument result;
using (var msg = new HttpRequestMessage(HttpMethod.Get, url))
{
msg.Headers.Clear();
msg.Headers.Add("accept", "application/json");
msg.Headers.Add("Authorization", "bearer " + AuthToken);
using (var resp = _client.Send(msg))
{
result = JsonDocument.Parse(resp.Content.ReadAsStream());
}
}
return result;
}
public static void Download(string url, string path, bool overwrite = false)
{
if (overwrite || !File.Exists(path))
{
using (var msg = new HttpRequestMessage(HttpMethod.Get, url))
{
using (var resp = _client.Send(msg))
{
var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir!);
}
using (var outstm = File.OpenWrite(path))
{
resp.Content.ReadAsStream().CopyTo(outstm);
}
}
}
}
}
public static object GetObject(string url)
{
return new TmdbElement(GetJson(url).RootElement);
}
这里没有什么特别复杂的。GetSafeFilename()函数只是将无效的文件名字符转换为下划线。其余的更加不言自明。
现在主要的混乱是ASPX文件,它们处理所有的逻辑,并调用上面的函数。它们的格式并不适合在这里放置源代码,但可以看看它们。如果熟悉ASP.NET,理解起来应该不会太困难。