From 1611721c9b9bb00015730259fd91dfd6c5aed824 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 3 Oct 2023 12:20:11 +0200 Subject: Fix feed audio file mimetype --- packages/tests/src/feeds/feeds.ts | 32 ++++++++++++++++------ server/server/controllers/feeds/video-feeds.ts | 7 +++-- .../controllers/feeds/video-podcast-feeds.ts | 8 ++---- server/server/initializers/constants.ts | 3 +- server/server/lib/video-file.ts | 10 ++++++- .../server/models/redundancy/video-redundancy.ts | 14 +++++++--- .../video/formatter/video-activity-pub-format.ts | 12 +++++--- 7 files changed, 58 insertions(+), 28 deletions(-) diff --git a/packages/tests/src/feeds/feeds.ts b/packages/tests/src/feeds/feeds.ts index 7587bb34e..ed833ffd1 100644 --- a/packages/tests/src/feeds/feeds.ts +++ b/packages/tests/src/feeds/feeds.ts @@ -46,13 +46,7 @@ describe('Test syndication feeds', () => { // Run servers servers = await createMultipleServers(2) - serverHLSOnly = await createSingleServer(3, { - transcoding: { - enabled: true, - web_videos: { enabled: false }, - hls: { enabled: true } - } - }) + serverHLSOnly = await createSingleServer(3) await setAccessTokensToServers([ ...servers, serverHLSOnly ]) await setDefaultChannelAvatar(servers[0]) @@ -60,6 +54,7 @@ describe('Test syndication feeds', () => { await doubleFollow(servers[0], servers[1]) await servers[0].config.enableLive({ allowReplay: false, transcoding: false }) + await serverHLSOnly.config.enableTranscoding({ webVideo: false, hls: true, with0p: true }) { const user = await servers[0].users.getMyInfo() @@ -397,9 +392,9 @@ describe('Test syndication feeds', () => { const jsonObj = JSON.parse(json) expect(jsonObj.items.length).to.be.equal(1) expect(jsonObj.items[0].attachments).to.exist - expect(jsonObj.items[0].attachments.length).to.be.eq(4) + expect(jsonObj.items[0].attachments.length).to.be.eq(6) - for (let i = 0; i < 4; i++) { + for (let i = 0; i < 6; i++) { expect(jsonObj.items[0].attachments[i].mime_type).to.be.eq('application/x-bittorrent') expect(jsonObj.items[0].attachments[i].size_in_bytes).to.be.greaterThan(0) expect(jsonObj.items[0].attachments[i].url).to.exist @@ -450,6 +445,25 @@ describe('Test syndication feeds', () => { await makeRawRequest({ url: imageUrl, expectedStatus: HttpStatusCode.OK_200 }) }) }) + + describe('XML feed', function () { + + it('Should correctly have video mime types feed with HLS only', async function () { + this.timeout(120000) + + const rss = await serverHLSOnly.feed.getXML({ feed: 'videos', ignoreCache: true }) + const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false }) + const xmlDoc = parser.parse(rss) + + for (const media of xmlDoc.rss.channel.item['media:group']['media:content']) { + if (media['@_height'] === 0) { + expect(media['@_type']).to.equal('audio/mp4') + } else { + expect(media['@_type']).to.equal('video/mp4') + } + } + }) + }) }) describe('Video comments feed', function () { diff --git a/server/server/controllers/feeds/video-feeds.ts b/server/server/controllers/feeds/video-feeds.ts index fc0e6e0f7..679d73fff 100644 --- a/server/server/controllers/feeds/video-feeds.ts +++ b/server/server/controllers/feeds/video-feeds.ts @@ -3,9 +3,9 @@ import { extname } from 'path' import { Feed } from '@peertube/feed' import { cacheRouteFactory } from '@server/middlewares/index.js' import { VideoModel } from '@server/models/video/video.js' -import { VideoInclude } from '@peertube/peertube-models' +import { VideoInclude, VideoResolution } from '@peertube/peertube-models' import { buildNSFWFilter } from '../../helpers/express-utils.js' -import { MIMETYPES, ROUTE_CACHE_LIFETIME, WEBSERVER } from '../../initializers/constants.js' +import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../../initializers/constants.js' import { asyncMiddleware, commonVideosFiltersValidator, @@ -17,6 +17,7 @@ import { videoSubscriptionFeedsValidator } from '../../middlewares/index.js' import { buildFeedMetadata, getCommonVideoFeedAttributes, getVideosForFeeds, initFeed, sendFeed } from './shared/index.js' +import { getVideoFileMimeType } from '@server/lib/video-file.js' const videoFeedsRouter = express.Router() @@ -137,7 +138,7 @@ function addVideosToFeed (feed: Feed, videos: VideoModel[]) { const videoFiles = formattedVideoFiles.map(videoFile => { return { - type: MIMETYPES.VIDEO.EXT_MIMETYPE[extname(videoFile.fileUrl)], + type: getVideoFileMimeType(extname(videoFile.fileUrl), videoFile.resolution.id === VideoResolution.H_NOVIDEO), medium: 'video', height: videoFile.resolution.id, fileSize: videoFile.size, diff --git a/server/server/controllers/feeds/video-podcast-feeds.ts b/server/server/controllers/feeds/video-podcast-feeds.ts index 84d5acadd..ad694affa 100644 --- a/server/server/controllers/feeds/video-podcast-feeds.ts +++ b/server/server/controllers/feeds/video-podcast-feeds.ts @@ -15,6 +15,7 @@ import { asyncMiddleware, setFeedPodcastContentType, videoFeedsPodcastValidator import { VideoModel } from '../../models/video/video.js' import { VideoCaptionModel } from '../../models/video/video-caption.js' import { buildFeedMetadata, getCommonVideoFeedAttributes, getVideosForFeeds, initFeed } from './shared/index.js' +import { getVideoFileMimeType } from '@server/lib/video-file.js' const videoPodcastFeedsRouter = express.Router() @@ -243,11 +244,6 @@ async function addLivePodcastItem (options: { // --------------------------------------------------------------------------- function buildVODWebVideoFile (video: MVideo, videoFile: VideoFile) { - const isAudio = videoFile.resolution.id === VideoResolution.H_NOVIDEO - const type = isAudio - ? MIMETYPES.AUDIO.EXT_MIMETYPE[extname(videoFile.fileUrl)] - : MIMETYPES.VIDEO.EXT_MIMETYPE[extname(videoFile.fileUrl)] - const sources = [ { uri: videoFile.fileUrl }, { uri: videoFile.torrentUrl, contentType: 'application/x-bittorrent' } @@ -258,7 +254,7 @@ function buildVODWebVideoFile (video: MVideo, videoFile: VideoFile) { } return { - type, + type: getVideoFileMimeType(extname(videoFile.fileUrl), videoFile.resolution.id === VideoResolution.H_NOVIDEO), title: videoFile.resolution.label, length: videoFile.size, bitrate: videoFile.size / video.duration * 8, diff --git a/server/server/initializers/constants.ts b/server/server/initializers/constants.ts index 38322aece..121433caf 100644 --- a/server/server/initializers/constants.ts +++ b/server/server/initializers/constants.ts @@ -630,9 +630,10 @@ const MIMETYPES = { 'audio/vnd.dlna.adts': '.aac', 'audio/aac': '.aac', + // Keep priority for preferred mime type 'audio/m4a': '.m4a', - 'audio/mp4': '.m4a', 'audio/x-m4a': '.m4a', + 'audio/mp4': '.m4a', 'audio/vnd.dolby.dd-raw': '.ac3', 'audio/ac3': '.ac3' diff --git a/server/server/lib/video-file.ts b/server/server/lib/video-file.ts index 9d8a6e8fc..ccc5668a2 100644 --- a/server/server/lib/video-file.ts +++ b/server/server/lib/video-file.ts @@ -8,6 +8,7 @@ import { ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamFPS, isAudi import { lTags } from './object-storage/shared/index.js' import { generateHLSVideoFilename, generateWebVideoFilename } from './paths.js' import { VideoPathManager } from './video-path-manager.js' +import { MIMETYPES } from '@server/initializers/constants.js' async function buildNewFile (options: { path: string @@ -130,6 +131,12 @@ async function buildFileMetadata (path: string, existingProbe?: FfprobeData) { return new VideoFileMetadata(metadata) } +function getVideoFileMimeType (extname: string, isAudio: boolean) { + return isAudio && extname === '.mp4' // We use .mp4 even for audio file only + ? MIMETYPES.AUDIO.EXT_MIMETYPE['.m4a'] + : MIMETYPES.VIDEO.EXT_MIMETYPE[extname] +} + // --------------------------------------------------------------------------- export { @@ -140,5 +147,6 @@ export { removeAllWebVideoFiles, removeWebVideoFile, - buildFileMetadata + buildFileMetadata, + getVideoFileMimeType } diff --git a/server/server/models/redundancy/video-redundancy.ts b/server/server/models/redundancy/video-redundancy.ts index 26089594d..3a5e7bf33 100644 --- a/server/server/models/redundancy/video-redundancy.ts +++ b/server/server/models/redundancy/video-redundancy.ts @@ -15,6 +15,7 @@ import { UpdatedAt } from 'sequelize-typescript' import { + ActivityVideoUrlObject, CacheFileObject, FileRedundancyInformation, StreamingPlaylistRedundancyInformation, @@ -31,7 +32,7 @@ import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, M import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc.js' import { logger } from '../../helpers/logger.js' import { CONFIG } from '../../initializers/config.js' -import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants.js' +import { CONSTRAINTS_FIELDS } from '../../initializers/constants.js' import { ActorModel } from '../actor/actor.js' import { ServerModel } from '../server/server.js' import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../shared/index.js' @@ -40,6 +41,7 @@ import { VideoChannelModel } from '../video/video-channel.js' import { VideoFileModel } from '../video/video-file.js' import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist.js' import { VideoModel } from '../video/video.js' +import { getVideoFileMimeType } from '@server/lib/video-file.js' export enum ScopeNames { WITH_VIDEO = 'WITH_VIDEO' @@ -733,15 +735,19 @@ export class VideoRedundancyModel extends Model