Instancia transcoder t1: cambios locales

This commit is contained in:
Joaquin
2026-07-13 15:19:13 +00:00
parent 0e26a90dae
commit bb189a472e
27 changed files with 2161 additions and 496 deletions
+66 -24
View File
@@ -1,6 +1,14 @@
const express = require('express');
const router = express.Router();
const { getDb } = require('../db/sqlite');
const { sources } = require('../db');
const { requireAuth } = require('../auth');
const {
getAccessibleSourceIds,
assertCanUseSourceById
} = require('../sourceAccess');
router.use(requireAuth);
// Helper to map API item types to DB types and tables
function mapItemType(apiType) {
@@ -21,27 +29,44 @@ router.get('/hidden', async (req, res) => {
const { sourceId } = req.query;
const db = getDb();
let hidden = [];
let allowedIds = null;
if (req.user.role !== 'admin') {
allowedIds = await getAccessibleSourceIds(req, sources);
if (allowedIds.length === 0) {
return res.json([]);
}
}
const resultFormat = (row, itemType) => ({
source_id: row.source_id,
item_type: itemType,
item_id: itemType.includes('category') || itemType === 'group' ? row.category_id : row.item_id
});
// Query Categories
let catQuery = `SELECT source_id, category_id, type FROM categories WHERE is_hidden = 1`;
let itemQuery = `SELECT source_id, item_id, type FROM playlist_items WHERE is_hidden = 1`;
let catParams = [];
let itemParams = [];
const params = [];
if (sourceId) {
if (!(await assertCanUseSourceById(req, res, sources, sourceId))) return;
const sid = parseInt(sourceId, 10);
catQuery += ` AND source_id = ?`;
itemQuery += ` AND source_id = ?`;
const sid = parseInt(sourceId);
params.push(sid);
catParams.push(sid);
itemParams.push(sid);
} else if (allowedIds) {
const ph = allowedIds.map(() => '?').join(',');
catQuery += ` AND source_id IN (${ph})`;
itemQuery += ` AND source_id IN (${ph})`;
catParams = [...allowedIds];
itemParams = [...allowedIds];
}
const hiddenCats = db.prepare(catQuery).all(...params);
const hiddenItems = db.prepare(itemQuery).all(...params);
const hiddenCats = db.prepare(catQuery).all(...catParams);
const hiddenItems = db.prepare(itemQuery).all(...itemParams);
const hidden = [];
hiddenCats.forEach(row => {
let apiType;
@@ -75,6 +100,7 @@ router.post('/hide', async (req, res) => {
const mapping = mapItemType(itemType);
if (!mapping) return res.status(400).json({ error: 'Invalid item type' });
if (!(await assertCanUseSourceById(req, res, sources, sourceId))) return;
const db = getDb();
const idCol = mapping.table === 'categories' ? 'category_id' : 'item_id';
@@ -101,6 +127,7 @@ router.post('/show', async (req, res) => {
const mapping = mapItemType(itemType);
if (!mapping) return res.status(400).json({ error: 'Invalid item type' });
if (!(await assertCanUseSourceById(req, res, sources, sourceId))) return;
const db = getDb();
const idCol = mapping.table === 'categories' ? 'category_id' : 'item_id';
@@ -127,6 +154,8 @@ router.get('/hidden/check', async (req, res) => {
const mapping = mapItemType(itemType);
if (!mapping) return res.json({ hidden: false });
if (!(await assertCanUseSourceById(req, res, sources, sourceId))) return;
const db = getDb();
const idCol = mapping.table === 'categories' ? 'category_id' : 'item_id';
@@ -148,13 +177,16 @@ router.post('/hide/bulk', async (req, res) => {
const { items } = req.body;
if (!Array.isArray(items)) return res.status(400).json({ error: 'items array required' });
const uniqueIds = [...new Set(items.map(i => i.sourceId))];
for (const sid of uniqueIds) {
if (!(await assertCanUseSourceById(req, res, sources, sid))) return;
}
const db = getDb();
// Prepare statements once
const hideCat = db.prepare('UPDATE categories SET is_hidden = 1 WHERE source_id = ? AND type = ? AND category_id = ?');
const hideItem = db.prepare('UPDATE playlist_items SET is_hidden = 1 WHERE source_id = ? AND type = ? AND item_id = ?');
// Cascading statements (hide all children of a category)
const hideCatChildren = db.prepare('UPDATE playlist_items SET is_hidden = 1 WHERE source_id = ? AND type = ? AND category_id = ?');
const runBulk = db.transaction((list) => {
@@ -162,12 +194,9 @@ router.post('/hide/bulk', async (req, res) => {
const mapping = mapItemType(item.itemType);
if (mapping) {
if (mapping.table === 'categories') {
// Hide the category
hideCat.run(item.sourceId, mapping.type, item.itemId);
// Cascade to children
hideCatChildren.run(item.sourceId, mapping.type, item.itemId);
} else {
// Hide individual item
hideItem.run(item.sourceId, mapping.type, item.itemId);
}
}
@@ -191,13 +220,16 @@ router.post('/show/bulk', async (req, res) => {
const { items } = req.body;
if (!Array.isArray(items)) return res.status(400).json({ error: 'items array required' });
const uniqueIds = [...new Set(items.map(i => i.sourceId))];
for (const sid of uniqueIds) {
if (!(await assertCanUseSourceById(req, res, sources, sid))) return;
}
const db = getDb();
// Prepare statements once
const showCat = db.prepare('UPDATE categories SET is_hidden = 0 WHERE source_id = ? AND type = ? AND category_id = ?');
const showItem = db.prepare('UPDATE playlist_items SET is_hidden = 0 WHERE source_id = ? AND type = ? AND item_id = ?');
// Cascading statements (show all children of a category)
const showCatChildren = db.prepare('UPDATE playlist_items SET is_hidden = 0 WHERE source_id = ? AND type = ? AND category_id = ?');
const runBulk = db.transaction((list) => {
@@ -205,12 +237,9 @@ router.post('/show/bulk', async (req, res) => {
const mapping = mapItemType(item.itemType);
if (mapping) {
if (mapping.table === 'categories') {
// Show the category
showCat.run(item.sourceId, mapping.type, item.itemId);
// Cascade to children
showCatChildren.run(item.sourceId, mapping.type, item.itemId);
} else {
// Show individual item
showItem.run(item.sourceId, mapping.type, item.itemId);
}
}
@@ -234,14 +263,15 @@ router.post('/show/all', async (req, res) => {
const { sourceId, contentType } = req.body;
if (!sourceId) return res.status(400).json({ error: 'sourceId required' });
if (!(await assertCanUseSourceById(req, res, sources, sourceId))) return;
const db = getDb();
let catCount = 0;
let itemCount = 0;
// Determine which types to update based on contentType
const types = contentType === 'movies' ? ['movie']
: contentType === 'series' ? ['series']
: ['live']; // default to channels
: ['live'];
for (const type of types) {
const catResult = db.prepare(`UPDATE categories SET is_hidden = 0 WHERE source_id = ? AND type = ?`).run(sourceId, type);
@@ -264,14 +294,15 @@ router.post('/hide/all', async (req, res) => {
const { sourceId, contentType } = req.body;
if (!sourceId) return res.status(400).json({ error: 'sourceId required' });
if (!(await assertCanUseSourceById(req, res, sources, sourceId))) return;
const db = getDb();
let catCount = 0;
let itemCount = 0;
// Determine which types to update based on contentType
const types = contentType === 'movies' ? ['movie']
: contentType === 'series' ? ['series']
: ['live']; // default to channels
: ['live'];
for (const type of types) {
const catResult = db.prepare(`UPDATE categories SET is_hidden = 1 WHERE source_id = ? AND type = ?`).run(sourceId, type);
@@ -296,6 +327,18 @@ router.get('/recent', async (req, res) => {
return res.status(400).json({ error: 'Valid type (movie or series) is required' });
}
let accessibleFilter = '';
const params = [type];
if (req.user.role !== 'admin') {
const ids = await getAccessibleSourceIds(req, sources);
if (ids.length === 0) {
return res.json([]);
}
accessibleFilter = ` AND p.source_id IN (${ids.map(() => '?').join(',')})`;
params.push(...ids);
}
params.push(parseInt(limit, 10));
const db = getDb();
const recentItems = db.prepare(`
SELECT * FROM playlist_items p
@@ -308,11 +351,11 @@ router.get('/recent', async (req, res) => {
AND c.type = p.type
AND c.is_hidden = 1
)
${accessibleFilter}
ORDER BY p.added_at DESC
LIMIT ?
`).all(type, parseInt(limit));
`).all(...params);
// Parse JSON data for each item
const formatted = recentItems.map(item => ({
...item,
data: JSON.parse(item.data)
@@ -326,4 +369,3 @@ router.get('/recent', async (req, res) => {
});
module.exports = router;