首页 > 解决方案 > 如何提供具有字节范围的播客(用于 iTunes 连接)

问题描述

我为 NodeJS 服务器添加了代码以提供播客剧集。我没有对字节范围请求进行编程,iTunes Connect 也没有对其进行验证。我使用包构建了 XML 文件rss,尽管我可以手动编写它。相关代码为:

/**
 * GET /podcast/:dayId
 *
 * Serve podcast episodes.
 */
router.get('/podcast/:dayId', async (req, res) => {

  let dayIdStr = req.params.dayId;

  let filepath = path.join(__dirname, "../podcast/" + dayIdStr + ".mp3");
  if (!fs.existsSync(filepath)) {
    console.log("Error in serving podcast episode " + dayIdStr);
    res.redirect("/404");
  }
  res.sendFile(filepath);
});


/**
 *
 * GET /podcast
 *
 * Serve RSS feed to podcast
 */
router.get('/podcast', async (req, res) => {

  let filepath = path.join(__dirname, `../podcast/podcast.xml`)
  //res.contentType('application/xml');
  res.sendFile(filepath);

});

我知道我需要添加一个 header Accept-Ranges: bytes,将响应 header 设置为206 Partial Response,并发送 header 请求的字节。相关线程iTunes Range 请求;播客被拒绝只回答第二个。express-range包仅包含 JSON 对象的文档。

如何在 NodeJS 中编写这三个条件?

标签: node.jsbytepodcast

解决方案


来自 Express 的本机支持

ExpresssendFile支持开箱即用的字节范围请求(如send评论中所述)。您只需要添加 206 状态码:

res.status(206);
res.sendFile(filepath);

调试

要调试您的服务器是否正常工作,请执行以下步骤:

  • 通过请求 20 个字节来验证服务器是否支持字节范围请求;如果结果是 21,那么你得到了正确的字节数(取自ServerFault):
$ curl --silent --range 20-40 https://emotionathletes.org/podcast/1.mp3 | wc -c
21
  • 请求整个文件时测试头部的响应代码:
$ curl --head https://emotionathletes.org/podcast/1.mp3
HTTP/1.1 206 Partial Content
X-Powered-By: Express
Accept-Ranges: bytes
...
  • 请求范围时测试响应代码和字节范围(这个可能是多余的)
$ curl -H Range:bytes=20-40 -I https://emotionathletes.org/podcast/1.mp3
HTTP/1.1 206 Partial Content
X-Powered-By: Express
Accept-Ranges: bytes
...

您也可以使用wget而不是进行测试curl,例如:

wget --header="Range: bytes=20-40" -t 1 https://emotionathletes.org/podcast/1.mp3

您可以wget使用macOS.brew

仍然失败?检查 XML 提要

如果这一切正常,但您仍然在 iTunes 连接上遇到验证失败,则问题可能出在 XML 提要上。考虑复制适用于 iTunes 连接(例如的)的 XML 提要的结构,或寻求具体帮助。

就我而言,我正在提供 podcast 标识,/podcast/1而 iTunes 甚至没有尝试下载它们,因为它们缺少扩展名。iTunes 通过用户代理iTMS和请求提要Jakarta,但没有请求剧集。与成功的提要相比,我意识到我需要一个扩展,所以我使用了它。

在 NodeJS 中构建 XML 提要

您还可以使用该rss包从 NodeJS 构建 RSS 提要。属性有点棘手,例如iTunes 需要一个带有 url 的附件,所以我podcast.js在这里复制我的部分。为简单起见,我将其编译为带有 的静态 XML 文件node podcast.js,然后像任何其他静态文件一样提供服务。

const fs = require('fs');
const path = require('path');
const RSS = require('rss');
 
/* lets create an rss feed */
var feed = new RSS({
    itunesExplicit: false,
    title: 'Ginja',
    description: 'Um curso virtual que desenvolve a inteligência emocional nas crianças',
    feed_url: 'https://emocoes.org/podcast-feed',
    site_url: 'https://emocoes.org',
    image_url: 'http://emocoes.org/images/logo_podcast.png',
    managingEditor: 'Miguel Morin',
    webMaster: 'Miguel Morin',
    copyright: '2020 Atletismo Emocional',
    language: 'pt',
    categories: ['Education for Kids','Stories for Kids','Mental Health'],
    pubDate: 'October 20, 2020 22:00:00 GMT',
    ttl: '60',
    custom_namespaces: {
      'itunes': 'http://www.itunes.com/dtds/podcast-1.0.dtd'
    },
  custom_elements: [
    {'itunes:explicit': 'no'},
      {'itunes:subtitle': 'Atletismo Emocional para crianças'},
      {'itunes:author': 'Ginja'},
      {'itunes:summary': 'Olá crianças! Eu sou o Ginja, o guru das emoções! Ao longo de várias aventuras com os meus amigos, vamos aprender a reconhecer, aceitar e utilizar as nossas emoções. E no fim, vamos ser uns atletas emocionais e chegar à meta da felicidade.\n\nVisitem a minha página: https://emocoes.org\n\nSigam-me no Facebook: https://facebook.com/emotionathletes\n\nSigam-me no Instagram: https://instagram.com/emotionathletes\n\nSigam-me no Twitter: https://twitter.com/emotionathletes'},
      {'itunes:owner': [
        {'itunes:name': 'Ginja'},
        {'itunes:email': 'ginja@emocoes.org'}
      ]},
      {'itunes:image': {
        _attr: {
          href: 'http://emocoes.org/images/logo_podcast.png'
        }
      }},
      {'itunes:category': [
        {_attr: {
          text: 'Kids & Family'
        }},
        {'itunes:category': {
          _attr: {
            text: 'Education for Kids'
          }
        }}
      ]}
    ]
});
 
/* loop over data and add to feed */
feed.item({
    title:  '1: Bem-vindos!',
    description: 'O Ginja explica que as emoções são como as ondas do mar. (Mensagem principal: As emoções são como as ondas do mar: vêm e vão, não as podemos parar.)',
    url: 'https://emocoes.org/podcast/1.mp3', // link to the item
    date: 'October 20, 2020 22:00:00 GMT', // any format that js Date can parse.
    custom_elements: [
      {'itunes:author': 'Ginja'},
      {'itunes:subtitle': 'O Ginja explica que as emoções são como as ondas do mar. (Mensagem principal: As emoções são como as ondas do mar: vêm e vão, não as podemos parar.)'},
      {'itunes:duration': '4:23'},
      {'enclosure url="https://emocoes.org/podcast/1.mp3" length="8520238" type="audio/mpeg"': {}}
    ]
});

var xml = feed.xml({indent: true});
let filepath = path.join(__dirname, `./public/podcast.xml`)
fs.writeFile(filepath, xml, (err) => {if (err) throw err;});

其他选项

nginx作为播客服务器提到的评论。我找不到如何做到这一点,并在ServerFault上询问了它。


推荐阅读