mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-24 04:03:19 +03:00
Added ability to generate RSS feeds from downloads
This commit is contained in:
@@ -111,6 +111,37 @@ paths:
|
|||||||
$ref: '#/components/schemas/GetAllFilesResponse'
|
$ref: '#/components/schemas/GetAllFilesResponse'
|
||||||
security:
|
security:
|
||||||
- Auth query parameter: []
|
- Auth query parameter: []
|
||||||
|
/api/rss:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- files
|
||||||
|
summary: Generates an RSS feed
|
||||||
|
description: Generates an RSS feed for downloaded files
|
||||||
|
operationId: get-rss
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: params
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/GetAllFilesRequest'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
uuid:
|
||||||
|
type: string
|
||||||
|
description: user uid
|
||||||
|
default: null
|
||||||
|
style: form
|
||||||
|
explode: true
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
text/plain:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: RSS feed
|
||||||
|
security:
|
||||||
|
- Auth query parameter: []
|
||||||
/api/getFile:
|
/api/getFile:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
@@ -1755,32 +1786,39 @@ components:
|
|||||||
description: Two elements allowed, start index and end index
|
description: Two elements allowed, start index and end index
|
||||||
minItems: 2
|
minItems: 2
|
||||||
maxItems: 2
|
maxItems: 2
|
||||||
|
default: null
|
||||||
text_search:
|
text_search:
|
||||||
type: string
|
type: string
|
||||||
description: Filter files by title
|
description: Filter files by title
|
||||||
|
default: null
|
||||||
file_type_filter:
|
file_type_filter:
|
||||||
$ref: '#/components/schemas/FileTypeFilter'
|
$ref: '#/components/schemas/FileTypeFilter'
|
||||||
favorite_filter:
|
favorite_filter:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: If set to true, only gets favorites
|
description: If set to true, only gets favorites
|
||||||
|
default: false
|
||||||
sub_id:
|
sub_id:
|
||||||
type: string
|
type: string
|
||||||
description: Include if you want to filter by subscription
|
description: Include if you want to filter by subscription
|
||||||
|
default: null
|
||||||
Sort:
|
Sort:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
by:
|
by:
|
||||||
type: string
|
type: string
|
||||||
description: Property to sort by
|
description: Property to sort by
|
||||||
|
default: registered
|
||||||
order:
|
order:
|
||||||
type: number
|
type: number
|
||||||
description: 1 for ascending, -1 for descending
|
description: 1 for ascending, -1 for descending
|
||||||
|
default: -1
|
||||||
FileTypeFilter:
|
FileTypeFilter:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
- audio_only
|
- audio_only
|
||||||
- video_only
|
- video_only
|
||||||
- both
|
- both
|
||||||
|
default: both
|
||||||
GetAllFilesResponse:
|
GetAllFilesResponse:
|
||||||
required:
|
required:
|
||||||
- files
|
- files
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const URL = require('url').URL;
|
|||||||
const CONSTS = require('./consts')
|
const CONSTS = require('./consts')
|
||||||
const read_last_lines = require('read-last-lines');
|
const read_last_lines = require('read-last-lines');
|
||||||
const ps = require('ps-node');
|
const ps = require('ps-node');
|
||||||
|
const Feed = require('feed').Feed;
|
||||||
|
|
||||||
// needed if bin/details somehow gets deleted
|
// needed if bin/details somehow gets deleted
|
||||||
if (!fs.existsSync(CONSTS.DETAILS_BIN_PATH)) fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, {"version":"2000.06.06","path":"node_modules\\youtube-dl\\bin\\youtube-dl.exe","exec":"youtube-dl.exe","downloader":"youtube-dl"})
|
if (!fs.existsSync(CONSTS.DETAILS_BIN_PATH)) fs.writeJSONSync(CONSTS.DETAILS_BIN_PATH, {"version":"2000.06.06","path":"node_modules\\youtube-dl\\bin\\youtube-dl.exe","exec":"youtube-dl.exe","downloader":"youtube-dl"})
|
||||||
@@ -700,7 +701,7 @@ app.use(function(req, res, next) {
|
|||||||
next();
|
next();
|
||||||
} else if (req.query.apiKey && config_api.getConfigItem('ytdl_use_api_key') && req.query.apiKey === config_api.getConfigItem('ytdl_api_key')) {
|
} else if (req.query.apiKey && config_api.getConfigItem('ytdl_use_api_key') && req.query.apiKey === config_api.getConfigItem('ytdl_api_key')) {
|
||||||
next();
|
next();
|
||||||
} else if (req.path.includes('/api/stream/') || req.path.includes('/api/thumbnail/')) {
|
} else if (req.path.includes('/api/stream/') || req.path.includes('/api/thumbnail/') || req.path.includes('/api/rss')) {
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
logger.verbose(`Rejecting request - invalid API use for endpoint: ${req.path}. API key received: ${req.query.apiKey}`);
|
logger.verbose(`Rejecting request - invalid API use for endpoint: ${req.path}. API key received: ${req.query.apiKey}`);
|
||||||
@@ -923,7 +924,6 @@ app.post('/api/getFile', optionalJwt, async function (req, res) {
|
|||||||
|
|
||||||
app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
|
app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
|
||||||
// these are returned
|
// these are returned
|
||||||
let files = null;
|
|
||||||
const sort = req.body.sort;
|
const sort = req.body.sort;
|
||||||
const range = req.body.range;
|
const range = req.body.range;
|
||||||
const text_search = req.body.text_search;
|
const text_search = req.body.text_search;
|
||||||
@@ -932,31 +932,7 @@ app.post('/api/getAllFiles', optionalJwt, async function (req, res) {
|
|||||||
const sub_id = req.body.sub_id;
|
const sub_id = req.body.sub_id;
|
||||||
const uuid = req.isAuthenticated() ? req.user.uid : null;
|
const uuid = req.isAuthenticated() ? req.user.uid : null;
|
||||||
|
|
||||||
const filter_obj = {user_uid: uuid};
|
const {files, file_count} = await db_api.getAllFiles(sort, range, text_search, file_type_filter, favorite_filter, sub_id, uuid);
|
||||||
const regex = true;
|
|
||||||
if (text_search) {
|
|
||||||
if (regex) {
|
|
||||||
filter_obj['title'] = {$regex: `.*${text_search}.*`, $options: 'i'};
|
|
||||||
} else {
|
|
||||||
filter_obj['$text'] = { $search: utils.createEdgeNGrams(text_search) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (favorite_filter) {
|
|
||||||
filter_obj['favorite'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sub_id) {
|
|
||||||
filter_obj['sub_id'] = sub_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_type_filter === 'audio_only') filter_obj['isAudio'] = true;
|
|
||||||
else if (file_type_filter === 'video_only') filter_obj['isAudio'] = false;
|
|
||||||
|
|
||||||
files = await db_api.getRecords('files', filter_obj, false, sort, range, text_search);
|
|
||||||
const file_count = await db_api.getRecords('files', filter_obj, true);
|
|
||||||
|
|
||||||
files = JSON.parse(JSON.stringify(files));
|
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
files: files,
|
files: files,
|
||||||
@@ -2043,6 +2019,56 @@ app.post('/api/deleteAllNotifications', optionalJwt, async (req, res) => {
|
|||||||
res.send({success: success});
|
res.send({success: success});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// rss feed
|
||||||
|
|
||||||
|
app.get('/api/rss', async function (req, res) {
|
||||||
|
if (!config_api.getConfigItem('ytdl_enable_rss_feed')) {
|
||||||
|
logger.error('RSS feed is disabled! It must be enabled in the settings before it can be generated.');
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// these are returned
|
||||||
|
const sort = req.query.sort;
|
||||||
|
const range = req.query.range;
|
||||||
|
const text_search = req.query.text_search;
|
||||||
|
const file_type_filter = req.query.file_type_filter;
|
||||||
|
const favorite_filter = req.query.favorite_filter;
|
||||||
|
const sub_id = req.query.sub_id;
|
||||||
|
const uuid = req.query.uuid;
|
||||||
|
|
||||||
|
const {files} = await db_api.getAllFiles(sort, range, text_search, file_type_filter, favorite_filter, sub_id, uuid);
|
||||||
|
|
||||||
|
const feed = new Feed({
|
||||||
|
title: 'Downloads',
|
||||||
|
description: 'YoutubeDL-Material downloads',
|
||||||
|
id: utils.getBaseURL(),
|
||||||
|
link: utils.getBaseURL(),
|
||||||
|
image: 'https://github.com/Tzahi12345/YoutubeDL-Material/blob/master/src/assets/images/logo_128px.png',
|
||||||
|
favicon: 'https://raw.githubusercontent.com/Tzahi12345/YoutubeDL-Material/master/src/favicon.ico',
|
||||||
|
generator: 'YoutubeDL-Material'
|
||||||
|
});
|
||||||
|
|
||||||
|
files.forEach(file => {
|
||||||
|
feed.addItem({
|
||||||
|
title: file.title,
|
||||||
|
link: `${utils.getBaseURL()}/#/player;uid=${file.uid}`,
|
||||||
|
description: file.description,
|
||||||
|
author: [
|
||||||
|
{
|
||||||
|
name: file.uploader,
|
||||||
|
link: file.url
|
||||||
|
}
|
||||||
|
],
|
||||||
|
contributor: [],
|
||||||
|
date: file.timestamp,
|
||||||
|
// https://stackoverflow.com/a/45415677/8088021
|
||||||
|
image: file.thumbnailURL.replace('&', '&')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
res.send(feed.rss2());
|
||||||
|
});
|
||||||
|
|
||||||
// web server
|
// web server
|
||||||
|
|
||||||
app.use(function(req, res, next) {
|
app.use(function(req, res, next) {
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ const DEFAULT_CONFIG = {
|
|||||||
"enable_notifications": true,
|
"enable_notifications": true,
|
||||||
"enable_all_notifications": true,
|
"enable_all_notifications": true,
|
||||||
"allowed_notification_types": [],
|
"allowed_notification_types": [],
|
||||||
|
"enable_rss_feed": false,
|
||||||
},
|
},
|
||||||
"API": {
|
"API": {
|
||||||
"use_API_key": false,
|
"use_API_key": false,
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ exports.CONFIG_ITEMS = {
|
|||||||
'key': 'ytdl_allowed_notification_types',
|
'key': 'ytdl_allowed_notification_types',
|
||||||
'path': 'YoutubeDLMaterial.Extra.allowed_notification_types'
|
'path': 'YoutubeDLMaterial.Extra.allowed_notification_types'
|
||||||
},
|
},
|
||||||
|
'ytdl_enable_rss_feed': {
|
||||||
|
'key': 'ytdl_enable_rss_feed',
|
||||||
|
'path': 'YoutubeDLMaterial.Extra.enable_rss_feed'
|
||||||
|
},
|
||||||
|
|
||||||
// API
|
// API
|
||||||
'ytdl_use_api_key': {
|
'ytdl_use_api_key': {
|
||||||
|
|||||||
@@ -538,8 +538,32 @@ exports.getVideo = async (file_uid) => {
|
|||||||
return await exports.getRecord('files', {uid: file_uid});
|
return await exports.getRecord('files', {uid: file_uid});
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getFiles = async (uuid = null) => {
|
exports.getAllFiles = async (sort, range, text_search, file_type_filter, favorite_filter, sub_id, uuid) => {
|
||||||
return await exports.getRecords('files', {user_uid: uuid});
|
const filter_obj = {user_uid: uuid};
|
||||||
|
const regex = true;
|
||||||
|
if (text_search) {
|
||||||
|
if (regex) {
|
||||||
|
filter_obj['title'] = {$regex: `.*${text_search}.*`, $options: 'i'};
|
||||||
|
} else {
|
||||||
|
filter_obj['$text'] = { $search: utils.createEdgeNGrams(text_search) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (favorite_filter) {
|
||||||
|
filter_obj['favorite'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub_id) {
|
||||||
|
filter_obj['sub_id'] = sub_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_type_filter === 'audio_only') filter_obj['isAudio'] = true;
|
||||||
|
else if (file_type_filter === 'video_only') filter_obj['isAudio'] = false;
|
||||||
|
|
||||||
|
const files = JSON.parse(JSON.stringify(await exports.getRecords('files', filter_obj, false, sort, range, text_search)));
|
||||||
|
const file_count = await exports.getRecords('files', filter_obj, true);
|
||||||
|
|
||||||
|
return {files, file_count};
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.setVideoProperty = async (file_uid, assignment_obj) => {
|
exports.setVideoProperty = async (file_uid, assignment_obj) => {
|
||||||
|
|||||||
21
backend/package-lock.json
generated
21
backend/package-lock.json
generated
@@ -1024,6 +1024,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
|
||||||
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
|
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
|
||||||
},
|
},
|
||||||
|
"feed": {
|
||||||
|
"version": "4.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz",
|
||||||
|
"integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==",
|
||||||
|
"requires": {
|
||||||
|
"xml-js": "^1.6.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@@ -2607,6 +2615,11 @@
|
|||||||
"sparse-bitfield": "^3.0.3"
|
"sparse-bitfield": "^3.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sax": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||||
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.7.1",
|
"version": "5.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
@@ -3112,6 +3125,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
},
|
},
|
||||||
|
"xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"requires": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"xmlbuilder2": {
|
"xmlbuilder2": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-3.0.2.tgz",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"config": "^3.2.3",
|
"config": "^3.2.3",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
|
"feed": "^4.2.2",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"fs-extra": "^9.0.0",
|
"fs-extra": "^9.0.0",
|
||||||
"gotify": "^1.1.0",
|
"gotify": "^1.1.0",
|
||||||
|
|||||||
@@ -297,6 +297,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
<div *ngIf="new_config" class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 mt-3">
|
||||||
|
<h6>RSS Feed</h6>
|
||||||
|
<mat-checkbox color="accent" [(ngModel)]="new_config['Extra']['enable_rss_feed']" ><ng-container i18n="Enable RSS Feed setting">Enable RSS Feed</ng-container></mat-checkbox>
|
||||||
|
<p><ng-container i18n="RSS Feed prefix">Be careful enabling this with multi-user mode! User data may be exposed.</ng-container></p>
|
||||||
|
<p><a target="_blank" href="https://github.com/Tzahi12345/YoutubeDL-Material/wiki/RSS-Feed"><ng-container i18n="RSS feed documentation">See documentation here.</ng-container></a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
<div *ngIf="new_config" class="container-fluid">
|
<div *ngIf="new_config" class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 mt-3">
|
<div class="col-12 mt-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user