From 5a6b3d8e6158730cd7dede97234884d9137c1285 Mon Sep 17 00:00:00 2001 From: "peter.kottke" Date: Wed, 1 Apr 2026 21:05:48 -0400 Subject: [PATCH 1/3] updates to allow share t argument to over-ride server stored position --- client/pages/share/_slug.vue | 4 +++- server/controllers/ShareController.js | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/client/pages/share/_slug.vue b/client/pages/share/_slug.vue index 64c099632..15748b48f 100644 --- a/client/pages/share/_slug.vue +++ b/client/pages/share/_slug.vue @@ -363,7 +363,9 @@ export default { console.log('Loaded media item share', this.mediaItemShare) } - const startTime = this.playbackSession.currentTime || 0 + const startTime = this.$route.query.t && !isNaN(this.$route.query.t) + ? parseFloat(this.$route.query.t) + : (this.playbackSession.currentTime || 0) this.localAudioPlayer.set(null, this.audioTracks, false, startTime, false) this.localAudioPlayer.on('stateChange', this.playerStateChange.bind(this)) this.localAudioPlayer.on('timeupdate', this.playerTimeUpdate.bind(this)) diff --git a/server/controllers/ShareController.js b/server/controllers/ShareController.js index 3e7ea1deb..f7dd36f83 100644 --- a/server/controllers/ShareController.js +++ b/server/controllers/ShareController.js @@ -20,7 +20,7 @@ const ShareManager = require('../managers/ShareManager') */ class ShareController { - constructor() {} + constructor() { } /** * Public route @@ -53,6 +53,10 @@ class ShareController { if (playbackSession) { if (mediaItemShare.id === playbackSession.mediaItemShareId) { Logger.debug(`[ShareController] Found share playback session ${req.cookies.share_session_id}`) + // If ?t was provided, override the cached currentTime + if (startTime > 0) { + playbackSession.currentTime = startTime + } mediaItemShare.playbackSession = playbackSession.toJSONForClient() return res.json(mediaItemShare) } else { From 3ccdcaec1a1a035bbcb1140d1057f98dfef035ae Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 25 Apr 2026 16:46:54 -0500 Subject: [PATCH 2/3] Implement SSRF filter for podcast episode downloads --- server/utils/ffmpegHelpers.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/utils/ffmpegHelpers.js b/server/utils/ffmpegHelpers.js index 80832cc77..7ad2a3aee 100644 --- a/server/utils/ffmpegHelpers.js +++ b/server/utils/ffmpegHelpers.js @@ -1,4 +1,5 @@ const axios = require('axios') +const ssrfFilter = require('ssrf-req-filter') const Ffmpeg = require('../libs/fluentFfmpeg') const ffmpgegUtils = require('../libs/fluentFfmpeg/utils') const fs = require('../libs/fsExtra') @@ -97,6 +98,8 @@ async function resizeImage(filePath, outputPath, width, height) { module.exports.resizeImage = resizeImage /** + * Download podcast episode + * Uses SSRF filter to prevent internal URLs * * @param {import('../objects/PodcastEpisodeDownload')} podcastEpisodeDownload * @returns {Promise<{success: boolean, isRequestError?: boolean}>} @@ -121,7 +124,9 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => { Accept: '*/*', 'User-Agent': userAgent }, - timeout: global.PodcastDownloadTimeout + timeout: global.PodcastDownloadTimeout, + httpAgent: global.DisableSsrfRequestFilter?.(podcastEpisodeDownload.url) ? null : ssrfFilter(podcastEpisodeDownload.url), + httpsAgent: global.DisableSsrfRequestFilter?.(podcastEpisodeDownload.url) ? null : ssrfFilter(podcastEpisodeDownload.url) }) Logger.debug(`[ffmpegHelpers] Successfully connected with User-Agent: ${userAgent}`) From 928051744aa419f8ac311153419a3756997f42d2 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 25 Apr 2026 17:13:22 -0500 Subject: [PATCH 3/3] ShareController check ?t param is less than duration, revert frontend mounted usage of param --- client/pages/share/_slug.vue | 5 ++--- server/controllers/ShareController.js | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/client/pages/share/_slug.vue b/client/pages/share/_slug.vue index 15748b48f..fdcbbe500 100644 --- a/client/pages/share/_slug.vue +++ b/client/pages/share/_slug.vue @@ -363,9 +363,8 @@ export default { console.log('Loaded media item share', this.mediaItemShare) } - const startTime = this.$route.query.t && !isNaN(this.$route.query.t) - ? parseFloat(this.$route.query.t) - : (this.playbackSession.currentTime || 0) + const startTime = this.playbackSession.currentTime || 0 + this.localAudioPlayer.set(null, this.audioTracks, false, startTime, false) this.localAudioPlayer.on('stateChange', this.playerStateChange.bind(this)) this.localAudioPlayer.on('timeupdate', this.playerTimeUpdate.bind(this)) diff --git a/server/controllers/ShareController.js b/server/controllers/ShareController.js index f7dd36f83..73da84a8a 100644 --- a/server/controllers/ShareController.js +++ b/server/controllers/ShareController.js @@ -20,7 +20,7 @@ const ShareManager = require('../managers/ShareManager') */ class ShareController { - constructor() { } + constructor() {} /** * Public route @@ -54,7 +54,7 @@ class ShareController { if (mediaItemShare.id === playbackSession.mediaItemShareId) { Logger.debug(`[ShareController] Found share playback session ${req.cookies.share_session_id}`) // If ?t was provided, override the cached currentTime - if (startTime > 0) { + if (startTime > 0 && startTime < playbackSession.duration) { playbackSession.currentTime = startTime } mediaItemShare.playbackSession = playbackSession.toJSONForClient()