streamflow/backend/utils/m3uParser.js

95 lines
2.9 KiB
JavaScript
Raw Normal View History

const axios = require('axios');
const fs = require('fs');
const path = require('path');
const { db } = require('../database/db');
const logger = require('./logger');
const parseM3U = async (source, playlistId, isFile = false) => {
try {
let content;
if (isFile) {
content = fs.readFileSync(source, 'utf8');
} else {
const response = await axios.get(source, { timeout: 30000 });
content = response.data;
}
const lines = content.split('\n').map(line => line.trim());
const channels = [];
for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('#EXTINF:')) {
const info = lines[i];
const url = lines[i + 1];
if (!url || url.startsWith('#')) continue;
const nameMatch = info.match(/,(.+)$/);
const name = nameMatch ? nameMatch[1].trim() : 'Unknown';
const tvgIdMatch = info.match(/tvg-id="([^"]*)"/);
const tvgNameMatch = info.match(/tvg-name="([^"]*)"/);
const tvgLogoMatch = info.match(/tvg-logo="([^"]*)"/);
const groupTitleMatch = info.match(/group-title="([^"]*)"/);
const languageMatch = info.match(/tvg-language="([^"]*)"/);
const countryMatch = info.match(/tvg-country="([^"]*)"/);
const isRadio = info.toLowerCase().includes('radio') ||
groupTitleMatch?.[1]?.toLowerCase().includes('radio') ||
url.toLowerCase().includes('radio');
channels.push({
playlistId,
name,
url,
logo: tvgLogoMatch ? tvgLogoMatch[1] : null,
groupName: groupTitleMatch ? groupTitleMatch[1] : 'Uncategorized',
tvgId: tvgIdMatch ? tvgIdMatch[1] : null,
tvgName: tvgNameMatch ? tvgNameMatch[1] : null,
language: languageMatch ? languageMatch[1] : null,
country: countryMatch ? countryMatch[1] : null,
isRadio: isRadio ? 1 : 0
});
i++; // Skip the URL line
}
}
// Insert channels in batches
const batchSize = 100;
for (let i = 0; i < channels.length; i += batchSize) {
const batch = channels.slice(i, i + batchSize);
const stmt = db.prepare(`
INSERT INTO channels (playlist_id, name, url, logo, group_name, tvg_id, tvg_name, language, country, is_radio)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
for (const channel of batch) {
stmt.run(
channel.playlistId,
channel.name,
channel.url,
channel.logo,
channel.groupName,
channel.tvgId,
channel.tvgName,
channel.language,
channel.country,
channel.isRadio
);
}
stmt.finalize();
}
logger.info(`Parsed ${channels.length} channels for playlist ${playlistId}`);
return channels.length;
} catch (error) {
logger.error('M3U parsing error:', error);
throw error;
}
};
module.exports = { parseM3U };