diff --git a/server/core/controllers/feeds/comment-feeds.ts b/server/core/controllers/feeds/comment-feeds.ts
index 105ae27..d4ae72d 100644
--- a/server/core/controllers/feeds/comment-feeds.ts
+++ b/server/core/controllers/feeds/comment-feeds.ts
@@ -56,7 +56,7 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res
 
   const { name, description, imageUrl, link } = await buildFeedMetadata({ video, account, videoChannel })
 
-  const feed = initFeed({
+  const feed = await initFeed({
     name,
     description,
     imageUrl,
diff --git a/server/core/controllers/feeds/shared/common-feed-utils.ts b/server/core/controllers/feeds/shared/common-feed-utils.ts
index 7196364..2076317 100644
--- a/server/core/controllers/feeds/shared/common-feed-utils.ts
+++ b/server/core/controllers/feeds/shared/common-feed-utils.ts
@@ -8,8 +8,9 @@ import { WEBSERVER } from '@server/initializers/constants.js'
 import { UserModel } from '@server/models/user/user.js'
 import { MAccountDefault, MChannelBannerAccountDefault, MUser, MVideoFullLight } from '@server/types/models/index.js'
 import express from 'express'
+import { PluginModel } from '@server/models/server/plugin.js'
 
-export function initFeed (parameters: {
+export async function initFeed (parameters: {
   name: string
   description: string
   imageUrl: string
@@ -33,6 +34,13 @@ export function initFeed (parameters: {
   const webserverUrl = WEBSERVER.URL
   const { name, description, link, imageUrl, isPodcast, resourceType, queryString, medium } = parameters
 
+  let generator = `Toraifōsu`; // ^.~
+
+  const peerhubPlugin = await PluginModel.loadByNpmName('peertube-plugin-peerhub');
+  if (peerhubPlugin && peerhubPlugin.enabled && !peerhubPlugin.uninstalled) {
+    generator = `Peerhub v${peerhubPlugin.version}`
+  }
+
   return new Feed({
     title: name,
     description: mdToOneLinePlainText(description),
@@ -43,7 +51,7 @@ export function initFeed (parameters: {
     favicon: webserverUrl + '/client/assets/images/favicon.png',
     copyright: `All rights reserved, unless otherwise specified in the terms specified at ${webserverUrl}/about` +
     ` and potential licenses granted by each content's rightholder.`,
-    generator: `PeerTube - ${webserverUrl}`,
+    generator,
     medium: medium || 'video',
     feedLinks: {
       json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
diff --git a/server/core/controllers/feeds/video-feeds.ts b/server/core/controllers/feeds/video-feeds.ts
index 679d73f..ae7133a 100644
--- a/server/core/controllers/feeds/video-feeds.ts
+++ b/server/core/controllers/feeds/video-feeds.ts
@@ -19,6 +19,12 @@ import {
 import { buildFeedMetadata, getCommonVideoFeedAttributes, getVideosForFeeds, initFeed, sendFeed } from './shared/index.js'
 import { getVideoFileMimeType } from '@server/lib/video-file.js'
 
+import { PluginModel } from '@server/models/server/plugin.js'
+import { PluginType } from '@peertube/peertube-models'
+
+const rssDisabledStorageFieldName = 'htrssdis'; // localStorage
+
+
 const videoFeedsRouter = express.Router()
 
 const { middleware: cacheRouteMiddleware } = cacheRouteFactory({
@@ -63,7 +69,7 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
 
   const { name, description, imageUrl, accountImageUrl, link, accountLink } = await buildFeedMetadata({ videoChannel, account })
 
-  const feed = initFeed({
+  const feed = await initFeed({
     name,
     description,
     link,
@@ -85,15 +91,32 @@ async function generateVideoFeed (req: express.Request, res: express.Response) {
 
   addVideosToFeed(feed, data)
 
-  // Now the feed generation is done, let's send it!
-  return sendFeed(feed, req, res)
+  // Now the feed generation is done, let's send it, if not disabled (peerhub only)!
+
+  const peerhubPlugin = await PluginModel.loadByNpmName('peertube-plugin-peerhub');
+
+  if (videoChannel && peerhubPlugin && peerhubPlugin.enabled && !peerhubPlugin.uninstalled) {
+    const rssDisabled = await PluginModel.getData(
+      'hive-tube', // use retro comp storage
+      PluginType.PLUGIN,
+      rssDisabledStorageFieldName + '-' + videoChannel.id
+    )
+
+    if (rssDisabled == 1) return res.redirect('/404');        
+      else return res.redirect('/feeds/podcast/videos.xml?videoChannelId='+videoChannel.id); // redirect to richer podcast feed
+
+  } else {
+    // no hive tube, standard peertube or it is not channel rss but account one
+    
+    return sendFeed(feed, req, res) // peertube native
+  }
 }
 
 async function generateVideoFeedForSubscriptions (req: express.Request, res: express.Response) {
   const account = res.locals.account
   const { name, description, imageUrl, link } = await buildFeedMetadata({ account })
 
-  const feed = initFeed({
+  const feed = await initFeed({
     name,
     description,
     link,
diff --git a/server/core/controllers/feeds/video-podcast-feeds.ts b/server/core/controllers/feeds/video-podcast-feeds.ts
index 7e0dca2..ab4c7ec 100644
--- a/server/core/controllers/feeds/video-podcast-feeds.ts
+++ b/server/core/controllers/feeds/video-podcast-feeds.ts
@@ -16,8 +16,17 @@ import { VideoCaptionModel } from '../../models/video/video-caption.js'
 import { VideoModel } from '../../models/video/video.js'
 import { buildFeedMetadata, getCommonVideoFeedAttributes, getVideosForFeeds, initFeed } from './shared/index.js'
 
+import { PluginModel } from '@server/models/server/plugin.js'
+import { PluginType } from '@peertube/peertube-models'
+
+import { RegisterServerSettingOptions} from '@peertube/peertube-models'
+
 const videoPodcastFeedsRouter = express.Router()
 
+const sriFieldName = 'htsri'; // localStorage
+const rssDisabledStorageFieldName = 'htrssdis'; // localStorage
+
+
 // ---------------------------------------------------------------------------
 
 const { middleware: podcastCacheRouteMiddleware, instance: podcastApiCache } = cacheRouteFactory({
@@ -82,7 +91,7 @@ async function generateVideoPodcastFeed (req: express.Request, res: express.Resp
     'filter:feed.podcast.rss.create-custom-xmlns.result'
   )
 
-  const feed = initFeed({
+  const feed = await initFeed({
     name,
     description,
     link,
@@ -103,8 +112,28 @@ async function generateVideoPodcastFeed (req: express.Request, res: express.Resp
 
   await addVideosToPodcastFeed(feed, data)
 
-  // Now the feed generation is done, let's send it!
-  return res.send(feed.podcast()).end()
+  let rssDisabled = 0; // default
+  const peerhubPlugin = await PluginModel.loadByNpmName('peertube-plugin-peerhub');
+  if (peerhubPlugin && peerhubPlugin.enabled && !peerhubPlugin.uninstalled) {
+    // only if hive tube installed
+    rssDisabled = await PluginModel.getData(
+      'hive-tube', // use retro comp storage
+      PluginType.PLUGIN,
+      rssDisabledStorageFieldName + '-' + videoChannel.id
+    )
+
+  }
+  
+  let resultPodcast = feed.podcast();
+
+  // fix itunes explicit field format directly into string, cause it's simpler than changing imported lib
+  // https://github.com/Podcast-Standards-Project/PSP-1-Podcast-RSS-Specification?tab=readme-ov-file#channel-itunes-explicit
+  let fixedResult = resultPodcast.replace(/itunes:explicit>no</g, "itunes:explicit>false<").replace(/itunes:explicit>yes</g, "itunes:explicit>true<");
+  
+  
+  // Now the feed generation is done, let's send it, if not disabled (hive tube only)!
+  if (rssDisabled == 1) return res.redirect('/404');
+    else return res.send(fixedResult).end()
 }
 
 type PodcastMedia =
@@ -153,7 +182,36 @@ async function generatePodcastItem (options: {
     const avatar = maxBy(account.Actor.Avatars, 'width')
     personImage = WEBSERVER.URL + avatar.getStaticPath()
   }
-
+  
+  // default peertube
+  let accountUrl = account.getClientUrl();
+  let socialInteract: any[] = [
+    {
+      uri: video.url,
+      protocol: 'activitypub',
+      accountUrl
+    }
+  ]
+  try {
+    const peerhubResponse = await fetch(`${WEBSERVER.URL}/plugins/peerhub/router/jsci?v=${video.id}`);
+    const comments = await peerhubResponse.json();
+    if (comments && (comments.length > 0)) {
+      socialInteract = []; // let's us our own correct format
+      comments.forEach((comment) => {
+        socialInteract.push({
+          priority: comment.priority,
+          protocol: 'activitypub',
+          uri: `${video.url}#${comment.commentId}`,
+          accountId: `@${comment.accountId}`,
+          accountUrl: comment.accountUrl         
+        });
+      });
+    }
+  
+  } catch(err) {
+    console.log(err); // safe
+  }
+  
   return {
     guid,
     ...commonAttributes,
@@ -171,13 +229,7 @@ async function generatePodcastItem (options: {
 
     media,
 
-    socialInteract: [
-      {
-        uri: video.url,
-        protocol: 'activitypub',
-        accountUrl: account.getClientUrl()
-      }
-    ],
+    socialInteract,
 
     customTags
   }
@@ -206,13 +258,22 @@ async function addVODPodcastItem (options: {
     .map(f => buildVODWebVideoFile(video, f))
     .sort(sortObjectComparator('bitrate', 'desc'))
 
-  const streamingPlaylistFiles = buildVODStreamingPlaylists(video)
+  const streamingPlaylistFiles = await buildVODStreamingPlaylists(video)
 
   // Order matters here, the first media URI will be the "default"
   // So web videos are default if enabled
   const media = [ ...webVideos, ...streamingPlaylistFiles ]
 
-  const videoCaptions = buildVODCaptions(video, captionsGroup[video.id])
+  let videoCaptions = buildVODCaptions(video, captionsGroup[video.id]) // original peertube
+  if (!videoCaptions || videoCaptions.length == 0) {
+    // always generate videoCaptions field even if not present, using video description
+    videoCaptions = [{
+      url: `${WEBSERVER.URL}/api/v1/videos/${video.id}/description`,
+      type: 'application/json',
+      language: video.language || 'en',
+      rel: 'captions'
+    }];
+  }  
   const item = await generatePodcastItem({ video, liveItem: false, media })
 
   feed.addPodcastItem({ ...item, subTitle: videoCaptions })
@@ -262,20 +323,139 @@ function buildVODWebVideoFile (video: MVideo, videoFile: VideoFile) {
   }
 }
 
-function buildVODStreamingPlaylists (video: MVideoFullLight) {
+async function buildVODStreamingPlaylists (video: MVideoFullLight) {
   const hls = video.getHLSPlaylist()
   if (!hls) return []
 
-  return [
-    {
-      type: 'application/x-mpegURL',
-      title: 'HLS',
-      sources: [
-        { uri: hls.getMasterPlaylistUrl(video) }
-      ],
-      language: video.language
+  let calculatedUri = hls.getMasterPlaylistUrl(video); // original peertube one, safe default
+  let torrentUri = '';
+  let integrity = '';
+
+  let magnetHash = '';
+  let torrentFileName = '';
+  let size = 0;
+  let resolution = 0;
+  let extname = '';
+
+  let calculatedType = 'application/x-mpegURL'; // original peertube one, safe default
+  let length = 0; // safe default
+  if (hls.VideoFiles && hls.VideoFiles.length > 0) {
+    let selected = 0; // choose highest resolution one, default first one
+    for (let i=0; i<hls.VideoFiles.length; i++) {
+      if (hls.VideoFiles[i].resolution > resolution) {
+        resolution = hls.VideoFiles[i].resolution;
+        selected = i;
+      }
+    }    
+    
+    if (hls.VideoFiles[selected].resolution > 0) {
+      // video
+      calculatedType = 'video/mp4';
+    } else {
+      // audio
+      calculatedType = 'audio/mpeg';
+   }
+    length = hls.VideoFiles[selected].size;
+    calculatedUri = WEBSERVER.URL + `/static/streaming-playlists/hls/${video.uuid}/${hls.VideoFiles[selected].filename}`;
+    torrentUri = WEBSERVER.URL + `/download/torrents/${hls.VideoFiles[selected].torrentFilename}`;
+  
+    integrity = await PluginModel.getData(
+      'hive-tube', // use retro comp storage
+      PluginType.PLUGIN,
+      sriFieldName + '-' + video.id
+    )
+    
+    magnetHash = hls.VideoFiles[selected].infoHash;
+    torrentFileName = hls.VideoFiles[selected].torrentFilename;
+    size = hls.VideoFiles[selected].size;
+    extname = hls.VideoFiles[selected].extname;
+    
+  }
+  
+  let sourcesUri = [
+    { uri: calculatedUri } // default source, standard peertube  
+  ];
+
+  if (torrentUri) { // torrent source, if found
+    sourcesUri.push({
+      uri: torrentUri
+    })
+  }  
+  
+  // this will work even if peerhub is not installed, in that case properties will be empty  
+  const additionalUriSettings = await PluginModel.getSettings(
+    'peerhub', PluginType.PLUGIN, ['podcast-onion', 'podcast-loki', 'podcast-i2p'], [<RegisterServerSettingOptions><unknown>'podcast-onion', <RegisterServerSettingOptions><unknown>'podcast-loki', <RegisterServerSettingOptions><unknown>'podcast-i2p']
+  )
+    
+  // 3 optional additional sources if peerhub installed and fields have values
+  if (additionalUriSettings) {
+    
+    if (additionalUriSettings['podcast-onion']) {
+      let onionUri = (additionalUriSettings['podcast-onion']).toString().trim();
+      if (onionUri.substr(-1) != '/') onionUri += '/'; // auto add trailing slash if missing
+      sourcesUri.push({
+        uri: onionUri+`static/streaming-playlists/hls/${video.uuid}/${hls.VideoFiles[0].filename}`
+      })
     }
-  ]
+
+    if (additionalUriSettings['podcast-loki']) {
+      let lokiUri = (additionalUriSettings['podcast-loki']).toString().trim();
+      if (lokiUri.substr(-1) != '/') lokiUri += '/'; // auto add trailing slash if missing
+      sourcesUri.push({
+        uri: lokiUri+`static/streaming-playlists/hls/${video.uuid}/${hls.VideoFiles[0].filename}`
+      })
+    }
+    
+    if (additionalUriSettings['podcast-i2p']) {
+      let itwopUri = (additionalUriSettings['podcast-i2p']).toString().trim();
+      if (itwopUri.substr(-1) != '/') itwopUri += '/'; // auto add trailing slash if missing
+      sourcesUri.push({
+        uri: itwopUri+`static/streaming-playlists/hls/${video.uuid}/${hls.VideoFiles[0].filename}`
+      })
+    }
+
+    
+  }  
+
+  const result: any = {
+    type: calculatedType,
+    title: 'HLS',
+    sources: sourcesUri,
+    language: video.language,
+    length
+  }
+ 
+  if (integrity) result.integrity = [{type:"sri", value:integrity}]; // add only if found
+  
+  const results = [];
+  
+  results.push(result);
+  
+  // now let's add optional magnet link
+  if (magnetHash) {
+    
+    const xs = `${WEBSERVER.URL}/lazy-static/torrents/${torrentFileName}`;
+    const name = video.name;
+    const announce = `${WEBSERVER.WS}://${WEBSERVER.HOSTNAME}:${WEBSERVER.PORT}/tracker/socket`;
+    const videoName = name.replace(/[/\\?%*:|"<>]/g, '-');
+    
+    const infoName = `${videoName} ${resolution}p${extname}`;
+    
+    let magnet = `magnet:?xs=${encodeURIComponent(xs)}&xt=urn:btih:${magnetHash}&dn=${encodeURIComponent(infoName)}&xl=${size}&tr=${encodeURIComponent(announce)}`;
+    
+    const magnetResult = {
+      type:"application/x-bittorrent",
+      title: "MAGNET",
+      sources: [
+        { uri: magnet }
+      ]
+      // length: 1024 // we don't have it easily at this step, this is the torrent size, not file one, it is optional
+    }    
+    results.push(magnetResult);
+    
+  }
+  
+  return results
 }
 
 function buildLiveStreamingPlaylists (video: MVideoFullLight) {
diff --git a/server/server.ts b/server/server.ts
index 603e35e..db9c41f 100644
--- a/server/server.ts
+++ b/server/server.ts
@@ -11,6 +11,10 @@ import { CONFIG } from './core/initializers/config.js'
 import { API_VERSION, WEBSERVER, loadLanguages } from './core/initializers/constants.js'
 import { logger } from './core/helpers/logger.js'
 
+import { PluginModel } from '@server/models/server/plugin.js'
+import { PluginType } from '@peertube/peertube-models'
+
+
 const missed = checkMissedConfig()
 if (missed.length !== 0) {
   logger.error('Your configuration files miss keys: ' + missed)
@@ -342,6 +346,16 @@ async function startApplication () {
   // Before PeerTubeSocket init
   PluginManager.Instance.registerWebSocketRouter()
 
+  // save patch version, to use from plugin, silent errors, failsafe
+  try {
+    await PluginModel.storeData(
+      'hive-tube', // use retro comp storage
+      PluginType.PLUGIN,
+      'peerhub-patch-version',
+      '6.3.0_peerhub-v2.patch'
+    )  
+  } catch(e) {}
+
   PeerTubeSocket.Instance.init(server)
   VideoViewsManager.Instance.init()
 
