217 lines
5.4 KiB
JavaScript
217 lines
5.4 KiB
JavaScript
|
|
const express = require('express');
|
||
|
|
const router = express.Router();
|
||
|
|
const { authenticate } = require('../middleware/auth');
|
||
|
|
const { readLimiter, modifyLimiter } = require('../middleware/rateLimiter');
|
||
|
|
const { db } = require('../database/db');
|
||
|
|
const logger = require('../utils/logger');
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Record watch history
|
||
|
|
*/
|
||
|
|
router.post('/', modifyLimiter, authenticate, (req, res) => {
|
||
|
|
const { channel_id, duration, profile_id } = req.body;
|
||
|
|
const user_id = req.user.userId;
|
||
|
|
|
||
|
|
if (!channel_id) {
|
||
|
|
return res.status(400).json({ error: 'channel_id is required' });
|
||
|
|
}
|
||
|
|
|
||
|
|
db.run(
|
||
|
|
`INSERT INTO watch_history (user_id, profile_id, channel_id, duration)
|
||
|
|
VALUES (?, ?, ?, ?)`,
|
||
|
|
[user_id, profile_id || null, channel_id, duration || 0],
|
||
|
|
function(err) {
|
||
|
|
if (err) {
|
||
|
|
logger.error('Error recording watch history:', err);
|
||
|
|
return res.status(500).json({ error: 'Failed to record watch history' });
|
||
|
|
}
|
||
|
|
res.json({ message: 'Watch history recorded', id: this.lastID });
|
||
|
|
}
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get user watch history
|
||
|
|
*/
|
||
|
|
router.get('/', readLimiter, authenticate, (req, res) => {
|
||
|
|
const user_id = req.user.userId;
|
||
|
|
const { limit = 50, offset = 0, profile_id } = req.query;
|
||
|
|
|
||
|
|
let query = `
|
||
|
|
SELECT
|
||
|
|
wh.id,
|
||
|
|
wh.watched_at,
|
||
|
|
wh.duration,
|
||
|
|
c.id as channel_id,
|
||
|
|
c.name as channel_name,
|
||
|
|
c.logo,
|
||
|
|
c.custom_logo,
|
||
|
|
c.group_name,
|
||
|
|
c.is_radio
|
||
|
|
FROM watch_history wh
|
||
|
|
INNER JOIN channels c ON wh.channel_id = c.id
|
||
|
|
WHERE wh.user_id = ?
|
||
|
|
`;
|
||
|
|
|
||
|
|
const params = [user_id];
|
||
|
|
|
||
|
|
if (profile_id) {
|
||
|
|
query += ' AND wh.profile_id = ?';
|
||
|
|
params.push(profile_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
query += ' ORDER BY wh.watched_at DESC LIMIT ? OFFSET ?';
|
||
|
|
params.push(parseInt(limit), parseInt(offset));
|
||
|
|
|
||
|
|
db.all(query, params, (err, rows) => {
|
||
|
|
if (err) {
|
||
|
|
logger.error('Error fetching watch history:', err);
|
||
|
|
return res.status(500).json({ error: 'Failed to fetch watch history' });
|
||
|
|
}
|
||
|
|
res.json(rows);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get most watched channels (recommendations)
|
||
|
|
*/
|
||
|
|
router.get('/top-channels', readLimiter, authenticate, (req, res) => {
|
||
|
|
const user_id = req.user.userId;
|
||
|
|
const { limit = 10, profile_id, days = 30 } = req.query;
|
||
|
|
|
||
|
|
let query = `
|
||
|
|
SELECT
|
||
|
|
c.id,
|
||
|
|
c.name,
|
||
|
|
c.logo,
|
||
|
|
c.custom_logo,
|
||
|
|
c.group_name,
|
||
|
|
c.is_radio,
|
||
|
|
c.url,
|
||
|
|
COUNT(wh.id) as watch_count,
|
||
|
|
SUM(wh.duration) as total_duration,
|
||
|
|
MAX(wh.watched_at) as last_watched
|
||
|
|
FROM watch_history wh
|
||
|
|
INNER JOIN channels c ON wh.channel_id = c.id
|
||
|
|
WHERE wh.user_id = ?
|
||
|
|
AND wh.watched_at >= datetime('now', '-' || ? || ' days')
|
||
|
|
`;
|
||
|
|
|
||
|
|
const params = [user_id, days];
|
||
|
|
|
||
|
|
if (profile_id) {
|
||
|
|
query += ' AND wh.profile_id = ?';
|
||
|
|
params.push(profile_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
query += `
|
||
|
|
GROUP BY c.id
|
||
|
|
ORDER BY watch_count DESC, total_duration DESC
|
||
|
|
LIMIT ?
|
||
|
|
`;
|
||
|
|
params.push(parseInt(limit));
|
||
|
|
|
||
|
|
db.all(query, params, (err, rows) => {
|
||
|
|
if (err) {
|
||
|
|
logger.error('Error fetching top channels:', err);
|
||
|
|
return res.status(500).json({ error: 'Failed to fetch top channels' });
|
||
|
|
}
|
||
|
|
res.json(rows);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get recommended channels based on viewing patterns
|
||
|
|
*/
|
||
|
|
router.get('/recommendations', readLimiter, authenticate, (req, res) => {
|
||
|
|
const user_id = req.user.userId;
|
||
|
|
const { limit = 10, profile_id } = req.query;
|
||
|
|
|
||
|
|
// Get channels from same groups as user's most watched channels
|
||
|
|
let query = `
|
||
|
|
WITH user_favorite_groups AS (
|
||
|
|
SELECT DISTINCT c.group_name, COUNT(wh.id) as watch_count
|
||
|
|
FROM watch_history wh
|
||
|
|
INNER JOIN channels c ON wh.channel_id = c.id
|
||
|
|
WHERE wh.user_id = ?
|
||
|
|
AND wh.watched_at >= datetime('now', '-30 days')
|
||
|
|
AND c.group_name IS NOT NULL
|
||
|
|
`;
|
||
|
|
|
||
|
|
const params = [user_id];
|
||
|
|
|
||
|
|
if (profile_id) {
|
||
|
|
query += ' AND wh.profile_id = ?';
|
||
|
|
params.push(profile_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
query += `
|
||
|
|
GROUP BY c.group_name
|
||
|
|
ORDER BY watch_count DESC
|
||
|
|
LIMIT 5
|
||
|
|
)
|
||
|
|
SELECT DISTINCT
|
||
|
|
c.id,
|
||
|
|
c.name,
|
||
|
|
c.logo,
|
||
|
|
c.custom_logo,
|
||
|
|
c.group_name,
|
||
|
|
c.is_radio,
|
||
|
|
c.url,
|
||
|
|
c.health_status
|
||
|
|
FROM channels c
|
||
|
|
INNER JOIN user_favorite_groups ufg ON c.group_name = ufg.group_name
|
||
|
|
WHERE c.id NOT IN (
|
||
|
|
SELECT channel_id
|
||
|
|
FROM watch_history
|
||
|
|
WHERE user_id = ?
|
||
|
|
AND watched_at >= datetime('now', '-7 days')
|
||
|
|
)
|
||
|
|
AND c.is_active = 1
|
||
|
|
AND c.health_status != 'dead'
|
||
|
|
ORDER BY RANDOM()
|
||
|
|
LIMIT ?
|
||
|
|
`;
|
||
|
|
|
||
|
|
params.push(user_id, parseInt(limit));
|
||
|
|
|
||
|
|
db.all(query, params, (err, rows) => {
|
||
|
|
if (err) {
|
||
|
|
logger.error('Error fetching recommendations:', err);
|
||
|
|
return res.status(500).json({ error: 'Failed to fetch recommendations' });
|
||
|
|
}
|
||
|
|
res.json(rows);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clear watch history
|
||
|
|
*/
|
||
|
|
router.delete('/', modifyLimiter, authenticate, (req, res) => {
|
||
|
|
const user_id = req.user.userId;
|
||
|
|
const { profile_id, days } = req.query;
|
||
|
|
|
||
|
|
let query = 'DELETE FROM watch_history WHERE user_id = ?';
|
||
|
|
const params = [user_id];
|
||
|
|
|
||
|
|
if (profile_id) {
|
||
|
|
query += ' AND profile_id = ?';
|
||
|
|
params.push(profile_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (days) {
|
||
|
|
query += ' AND watched_at < datetime("now", "-" || ? || " days")';
|
||
|
|
params.push(days);
|
||
|
|
}
|
||
|
|
|
||
|
|
db.run(query, params, function(err) {
|
||
|
|
if (err) {
|
||
|
|
logger.error('Error clearing watch history:', err);
|
||
|
|
return res.status(500).json({ error: 'Failed to clear watch history' });
|
||
|
|
}
|
||
|
|
res.json({ message: 'Watch history cleared', deleted: this.changes });
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
module.exports = router;
|