+ const alternateEnclosure = xmlDoc.rss.channel.item['podcast:alternateEnclosure']
+ expect(alternateEnclosure).to.exist
+
+ expect(alternateEnclosure['@_type']).to.equal('video/webm')
+ expect(alternateEnclosure['@_length']).to.equal(218910)
+ expect(alternateEnclosure['@_lang']).to.equal('zh')
+ expect(alternateEnclosure['@_title']).to.equal('720p')
+ expect(alternateEnclosure['@_default']).to.equal(true)
+
+ expect(alternateEnclosure['podcast:source'][0]['@_uri']).to.contain('-720.webm')
+ expect(alternateEnclosure['podcast:source'][0]['@_uri']).to.equal(enclosure['@_url'])
+ expect(alternateEnclosure['podcast:source'][1]['@_uri']).to.contain('-720.torrent')
+ expect(alternateEnclosure['podcast:source'][1]['@_contentType']).to.equal('application/x-bittorrent')
+ expect(alternateEnclosure['podcast:source'][2]['@_uri']).to.contain('magnet:?')
+ })
+
+ it('Should contain a valid podcast:alternateEnclosure with HLS only', async function () {
+ const rss = await serverHLSOnly.feed.getPodcastXML({ ignoreCache: false, channelId: rootChannelId })
+ expect(XMLValidator.validate(rss)).to.be.true
+
+ const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false })
+ const xmlDoc = parser.parse(rss)
+
+ const itemGuid = xmlDoc.rss.channel.item.guid
+ expect(itemGuid).to.exist
+ expect(itemGuid['@_isPermaLink']).to.equal(true)
+
+ const enclosure = xmlDoc.rss.channel.item.enclosure
+ const alternateEnclosure = xmlDoc.rss.channel.item['podcast:alternateEnclosure']
+ expect(alternateEnclosure).to.exist
+
+ expect(alternateEnclosure['@_type']).to.equal('application/x-mpegURL')
+ expect(alternateEnclosure['@_lang']).to.equal('zh')
+ expect(alternateEnclosure['@_title']).to.equal('HLS')
+ expect(alternateEnclosure['@_default']).to.equal(true)
+
+ expect(alternateEnclosure['podcast:source']['@_uri']).to.contain('-master.m3u8')
+ expect(alternateEnclosure['podcast:source']['@_uri']).to.equal(enclosure['@_url'])
+ })
+
+ it('Should contain a valid podcast:socialInteract', async function () {
+ const rss = await servers[0].feed.getPodcastXML({ ignoreCache: false, channelId: rootChannelId })
+ expect(XMLValidator.validate(rss)).to.be.true
+
+ const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false })
+ const xmlDoc = parser.parse(rss)
+
+ const item = xmlDoc.rss.channel.item
+ const socialInteract = item['podcast:socialInteract']
+ expect(socialInteract).to.exist
+ expect(socialInteract['@_protocol']).to.equal('activitypub')
+ expect(socialInteract['@_uri']).to.exist
+ expect(socialInteract['@_accountUrl']).to.exist
+ })
+
+ it('Should contain a valid support custom tags for plugins', async function () {
+ const rss = await servers[0].feed.getPodcastXML({ ignoreCache: false, channelId: userChannelId })
+ expect(XMLValidator.validate(rss)).to.be.true
+
+ const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false })
+ const xmlDoc = parser.parse(rss)
+
+ const fooTag = xmlDoc.rss.channel.fooTag
+ expect(fooTag).to.exist
+ expect(fooTag['@_bar']).to.equal('baz')
+ expect(fooTag['#text']).to.equal(42)
+
+ const bizzBuzzItem = xmlDoc.rss.channel['biz:buzzItem']
+ expect(bizzBuzzItem).to.exist
+
+ let nestedTag = bizzBuzzItem.nestedTag
+ expect(nestedTag).to.exist
+ expect(nestedTag).to.equal('example nested tag')
+
+ const item = xmlDoc.rss.channel.item
+ const fizzTag = item.fizzTag
+ expect(fizzTag).to.exist
+ expect(fizzTag['@_bar']).to.equal('baz')
+ expect(fizzTag['#text']).to.equal(21)
+
+ const bizzBuzz = item['biz:buzz']
+ expect(bizzBuzz).to.exist
+
+ nestedTag = bizzBuzz.nestedTag
+ expect(nestedTag).to.exist
+ expect(nestedTag).to.equal('example nested tag')
+ })
+
+ it('Should contain a valid podcast:liveItem for live streams', async function () {
+ this.timeout(120000)
+
+ const { uuid } = await servers[0].live.create({
+ fields: {
+ name: 'live-0',
+ privacy: VideoPrivacy.PUBLIC,
+ channelId: rootChannelId,
+ permanentLive: false
+ }
+ })
+ liveId = uuid
+
+ const ffmpeg = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveId, copyCodecs: true, fixtureName: 'video_short.mp4' })
+ await servers[0].live.waitUntilPublished({ videoId: liveId })
+
+ const rss = await servers[0].feed.getPodcastXML({ ignoreCache: false, channelId: rootChannelId })
+ expect(XMLValidator.validate(rss)).to.be.true
+
+ const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false })
+ const xmlDoc = parser.parse(rss)
+ const liveItem = xmlDoc.rss.channel['podcast:liveItem']
+ expect(liveItem.title).to.equal('live-0')
+ expect(liveItem.guid['@_isPermaLink']).to.equal(false)
+ expect(liveItem.guid['#text']).to.contain(`${uuid}_`)
+ expect(liveItem['@_status']).to.equal('live')
+
+ const enclosure = liveItem.enclosure
+ const alternateEnclosure = liveItem['podcast:alternateEnclosure']
+ expect(alternateEnclosure).to.exist
+ expect(alternateEnclosure['@_type']).to.equal('application/x-mpegURL')
+ expect(alternateEnclosure['@_title']).to.equal('HLS live stream')
+ expect(alternateEnclosure['@_default']).to.equal(true)
+
+ expect(alternateEnclosure['podcast:source']['@_uri']).to.contain('/master.m3u8')
+ expect(alternateEnclosure['podcast:source']['@_uri']).to.equal(enclosure['@_url'])
+
+ await stopFfmpeg(ffmpeg)
+
+ await servers[0].live.waitUntilEnded({ videoId: liveId })