编写种子详情页

2026-02-21 71 浏览 0 评论

在前面的章节中,我们已经完成了种子数据的采集、存储以及搜索索引的构建。有了这些基础能力之后,一个完整的磁力搜索或资源聚合系统,必然还需要一个 种子详情页 ,用于展示单个种子的完整信息,并为用户提供下载入口和相关推荐。

本文将围绕「种子详情页」展开,介绍后端 Controller 的实现思路,以及前端 EJS 模板如何组织和渲染数据。整体实现基于 Egg.js + EJS 模板,力求结构清晰、逻辑直观。


1. 种子详情页 Controller

详情页的核心职责很明确:

  1. 根据 URL 中的 hash 参数查询对应的种子信息
  2. 如果数据不存在,返回 404
  3. 查询一定数量的随机种子,用于 相关链接 推荐
  4. 将数据传递给视图模板进行渲染

下面是 app/controller/torrent.js 中的实现代码:

// app/controller/torrent.js

'use strict';

const Controller = require('egg').Controller;

module.exports = class extends Controller {
  async index() {
    const torrent = await this.ctx.service.torrent.getByHash(this.ctx.params.hash);
    if (!torrent.id) {
      this.ctx.status = 404;
      return;
    }
    const random = await this.ctx.service.torrent.random(10);
    await this.ctx.helper.renderView('torrent.ejs', {
      torrent,
      random,
      title: torrent.name,
    });
  }

  async delete() {
    const body = this.ctx.request.body;
    // 从数据库删除
    await this.ctx.service.torrent.delete(body.id);

    // 从 manticore 中删除
    await this.ctx.service.search.delete(body.id);

    this.ctx.body = {
      code: 0,
      message: '删除成功',
      success: true,
      data: '',
    };
  }
};

关键点说明

  • 通过 hash 查询种子
    getByHash 通常是一个基于唯一索引的查询,这样可以保证访问 /torrent/:hash 时的性能。
  • 404 处理
    如果查询结果不存在,直接设置 ctx.status = 404 并返回,避免后续渲染报错。
  • 随机推荐数据
    random(10) 用于生成 相关链接 ,即便不是真正意义上的相似推荐,也能有效提升页面的浏览深度。
  • renderView 封装
    通过 ctx.helper.renderView 统一渲染逻辑,通常可以在 helper 中处理公共变量(SEO、站点信息等)。
  • 删除接口
    删除操作不仅需要清理数据库记录,同时也要同步删除搜索引擎(如 Manticore / Sphinx)中的索引数据,保证搜索结果的准确性。

2. 种子详情页视图模板

详情页采用 EJS 模板进行渲染,主要包含以下几个区域:

  • 种子基础信息(名称、大小、时间、热度等)
  • 磁力链接展示与复制
  • 文件列表
  • 相关链接推荐

对应的模板文件为 app/view/torrent.ejs

<!-- // app/view/torrent.ejs -->

<!DOCTYPE html>
<html lang="en">
  <%-include('head.ejs')%>
  <body>
    <%-include('header.ejs')%>
    <div class="main">
      <div class="container">
        <div class="row">
          <div class="col-md-8 col-md-offset-2">
            <div class="torSingle">
              <h1><%-torrent.name%></h1>
              <div class="torMeta">
                <span>大小:<%-ctx.helper.formatBytes(torrent.length)%></span>
                <span>收录时间:<%-ctx.helper.date('Y-m-d H:i', torrent.add_date)%></span>
                <span>文件数:<%-torrent.file_count%></span>
                <span>热度:<%-torrent.download_count+1%>℃</span>
              </div>
              <h2>磁力链接</h2>
              <textarea rows="6" class="form-control" readonly id="magnet">
magnet:?xt=urn:btih:<%-torrent.hash%>&dn=<%-torrent.name%>&xl=<%-torrent.length%></textarea
              >
              <div class="torBtns">
                <a
                  class="btn btn-primary"
                  href="magnet:?xt=urn:btih:<%-torrent.hash%>&dn=<%-torrent.name%>&xl=<%-torrent.length%>"
                >
                  下载资源
                </a>
                <a href="javascript:" class="btn btn-info" id="clipboard" data-clipboard-target="#magnet"> 复制链接 </a>
              </div>
              <h2>文件列表</h2>
              <div class="torFiles">
                <% torrent.files.forEach(function(file, index){ %>
                <p>
                  <span class="torFileName"><%-file.name%></span>
                  <span class="torSize"><%-ctx.helper.formatBytes(file.length)%></span>
                </p>
                <% }) %>
                <!--  -->
                <% if(torrent.file_count>50){ %>
                <p class="torMoreInfo">-----仅列出部分文件,更多信息请下载后查看-----</p>
                <% } %>
              </div>

              <h1 style="margin-top: 30px">相关链接</h1>
              <ul class="torRela">
                <% random.forEach(function(item){ %>
                <li>
                  <a title="<%-item.name%>" href="/torrent/<%-item.hash%>">
                    <span class="relTorName"><%-item.name%></span>
                    <span><%-ctx.helper.formatBytes(item.length)%></span>
                    <span><%-ctx.helper.date('Y-m-d H:i', item.add_date)%></span>
                  </a>
                </li>
                <% }) %>
              </ul>
            </div>
          </div>
        </div>
      </div>
    </div>
    <%-include('footer.ejs')%>
    <script>
      new ClipboardJS('#clipboard');
    </script>
  </body>
</html>

模板设计要点

  • 使用 <%- %> 输出
    EJS 中 <%- %> 表示不进行 HTML 转义,适合输出已经可信的数据(如服务端生成的字段)。
  • 磁力链接拼接
    btih 对应种子的 hash, dn 表示显示名称, xl 表示文件大小,这三个参数基本可以满足绝大多数下载器的识别需求。
  • 文件列表展示优化
    当文件数量过多时,仅展示部分列表,并给出提示,避免页面过长或渲染性能问题。
  • 复制功能
    通过 ClipboardJS 实现一键复制磁力链接,这是一个非常轻量但体验提升明显的功能。
  • 相关链接
    随机种子列表可以有效提高站内跳转率,为后续加入 相似度推荐 标签推荐 等功能预留空间。

3. 小结

至此,一个完整的种子详情页就实现了。从后端数据查询、404 处理,到前端模板渲染、磁力链接生成与复制,整个流程清晰且易于扩展。

在此基础上,你还可以继续优化:

  • 增加访问统计与热度算法
  • 对文件列表进行懒加载或折叠展示
  • 引入真正的相似推荐(基于关键词或全文检索)
  • 为搜索引擎添加结构化数据(SEO)

这些能力叠加起来,才能让一个磁力资源站点逐步从 能用 ,走向 好用 。


发布评论

发布评论前请先 登录
取消
0 评论
点赞
收藏

评论列表 0

暂无评论