no message

This commit is contained in:
SpaceMonkey 2025-03-01 23:46:36 +00:00
parent f236e80fd6
commit e07e71d324
2 changed files with 160 additions and 131 deletions

View file

@ -2,93 +2,108 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
function splitByGroup(filePath) { function splitByGroup(filePath) {
const content = fs.readFileSync(filePath, 'utf8'); const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n'); const lines = content.split('\n');
// Create groups directory if it doesn't exist // Create groups directory if it doesn't exist
const groupsDir = path.join(path.dirname(filePath), 'countries'); const groupsDir = path.join(path.dirname(filePath), 'countries');
if (!fs.existsSync(groupsDir)) { if (!fs.existsSync(groupsDir)) {
fs.mkdirSync(groupsDir); fs.mkdirSync(groupsDir);
}
// Get existing country files
const existingFiles = fs.readdirSync(groupsDir)
.filter(file => file.endsWith('.m3u'))
.map(file => file.toLowerCase());
const groups = {};
// First line should be #EXTM3U
const header = lines[0];
if (!header.startsWith('#EXTM3U')) {
throw new Error('Invalid M3U file: Missing #EXTM3U header');
}
// Process the file line by line to handle multi-line entries
let currentGroupTitle = null;
let currentEntry = [];
let isInEntry = false;
for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) continue;
if (line.startsWith('#EXTINF')) {
// Start of a new entry
isInEntry = true;
currentEntry = [line];
// Extract the group title
const groupMatch = line.match(/group-title="([^"]*)"/);
currentGroupTitle = groupMatch ? groupMatch[1] : 'Unknown';
// Initialize the group if it doesn't exist
if (!groups[currentGroupTitle]) {
groups[currentGroupTitle] = ['#EXTM3U'];
}
} else if (isInEntry) {
// Add the line to the current entry
currentEntry.push(line);
// If this is a URL line (doesn't start with #), this completes the entry
if (!line.startsWith('#')) {
// Add all lines of the entry to the appropriate group
groups[currentGroupTitle].push(...currentEntry);
// Reset for the next entry
isInEntry = false;
currentEntry = [];
}
} }
}
// Get existing country files // Get list of current group files that should exist
const existingFiles = fs.readdirSync(groupsDir) const currentGroupFiles = Object.keys(groups).map(groupTitle =>
.filter(file => file.endsWith('.m3u')) `${groupTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.m3u`
.map(file => file.toLowerCase()); );
const groups = {}; // Remove files for groups that no longer exist
let currentExtinf = null; existingFiles.forEach(existingFile => {
if (!currentGroupFiles.includes(existingFile)) {
// First line should be #EXTM3U const fileToRemove = path.join(groupsDir, existingFile);
const header = lines[0]; fs.unlinkSync(fileToRemove);
if (!header.startsWith('#EXTM3U')) { console.log(`Removed obsolete group playlist: ${existingFile}`);
throw new Error('Invalid M3U file: Missing #EXTM3U header');
} }
});
// Process each line // Write each group to a separate file
lines.forEach(line => { Object.entries(groups).forEach(([groupTitle, groupLines]) => {
line = line.trim(); const safeGroupTitle = groupTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase();
if (!line) return; const groupFilePath = path.join(groupsDir, `${safeGroupTitle}.m3u`);
fs.writeFileSync(groupFilePath, groupLines.join('\n') + '\n');
console.log(`Created/updated group playlist: ${groupFilePath}`);
});
if (line.startsWith('#EXTINF')) { // Create a summary of the split
currentExtinf = line; // Count entries properly by counting #EXTINF lines
const groupMatch = line.match(/group-title="([^"]*)"/); const summary = Object.entries(groups).map(([group, lines]) => {
const groupTitle = groupMatch ? groupMatch[1] : 'Unknown'; const channelCount = lines.filter(line => line.startsWith('#EXTINF')).length;
return `${group}: ${channelCount} channels`;
});
if (!groups[groupTitle]) { console.log('\nPlaylist split summary:');
groups[groupTitle] = ['#EXTM3U']; console.log(summary.join('\n'));
}
groups[groupTitle].push(line);
} else if (currentExtinf && !line.startsWith('#')) {
// This is a URL line
const groupMatch = currentExtinf.match(/group-title="([^"]*)"/);
const groupTitle = groupMatch ? groupMatch[1] : 'Unknown';
groups[groupTitle].push(line);
currentExtinf = null;
}
});
// Get list of current group files that should exist
const currentGroupFiles = Object.keys(groups).map(groupTitle =>
`${groupTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.m3u`
);
// Remove files for groups that no longer exist
existingFiles.forEach(existingFile => {
if (!currentGroupFiles.includes(existingFile)) {
const fileToRemove = path.join(groupsDir, existingFile);
fs.unlinkSync(fileToRemove);
console.log(`Removed obsolete group playlist: ${existingFile}`);
}
});
// Write each group to a separate file
Object.entries(groups).forEach(([groupTitle, groupLines]) => {
const safeGroupTitle = groupTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase();
const groupFilePath = path.join(groupsDir, `${safeGroupTitle}.m3u`);
fs.writeFileSync(groupFilePath, groupLines.join('\n') + '\n');
console.log(`Created/updated group playlist: ${groupFilePath}`);
});
// Create a summary of the split
const summary = Object.entries(groups).map(([group, lines]) => {
const channelCount = (lines.length - 1) / 2; // Subtract header and divide by 2 (EXTINF + URL)
return `${group}: ${channelCount} channels`;
});
console.log('\nPlaylist split summary:');
console.log(summary.join('\n'));
} }
const filePath = process.argv[2]; const filePath = process.argv[2];
if (!filePath) { if (!filePath) {
console.error('Please provide the path to mystique.m3u'); console.error('Please provide the path to mystique.m3u');
process.exit(1); process.exit(1);
} }
try { try {
splitByGroup(filePath); splitByGroup(filePath);
} catch (error) { } catch (error) {
console.error('Error splitting mystique.m3u:', error.message); console.error('Error splitting mystique.m3u:', error.message);
process.exit(1); process.exit(1);
} }

View file

@ -1,67 +1,81 @@
const fs = require('fs'); const fs = require('fs');
function sortM3uByGroupTitle(filePath) { function sortM3uByGroupTitle(filePath) {
// Read the file // Read the file
const content = fs.readFileSync(filePath, 'utf8'); const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n'); const lines = content.split('\n');
// First line should be #EXTM3U // First line should be #EXTM3U
const header = lines[0]; const header = lines[0];
if (!header.startsWith('#EXTM3U')) { if (!header.startsWith('#EXTM3U')) {
throw new Error('Invalid M3U file: Missing #EXTM3U header'); throw new Error('Invalid M3U file: Missing #EXTM3U header');
} }
// Group the entries // Group the entries
const entries = []; const entries = [];
let currentEntry = []; let currentEntry = [];
for (let i = 1; i < lines.length; i++) { for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim(); const line = lines[i].trim();
if (!line) continue; if (!line) continue;
if (line.startsWith('#EXTINF')) { if (line.startsWith('#EXTINF')) {
if (currentEntry.length === 2) { // If we have a previous entry that's complete (has a URL), add it
entries.push(currentEntry); if (currentEntry.length > 0 && !currentEntry[currentEntry.length - 1].startsWith('#')) {
}
currentEntry = [line];
} else if (currentEntry.length === 1) {
currentEntry.push(line);
}
}
// Add the last entry if exists
if (currentEntry.length === 2) {
entries.push(currentEntry); entries.push(currentEntry);
currentEntry = [];
}
// Start a new entry
currentEntry = [line];
} else if (line.startsWith('#')) {
// This is another directive line (like #EXTVLCOPT), add it to the current entry
if (currentEntry.length > 0) {
currentEntry.push(line);
}
} else {
// This is a URL line, add it to complete the current entry
if (currentEntry.length > 0) {
currentEntry.push(line);
entries.push(currentEntry);
currentEntry = [];
}
} }
}
// Sort entries by group-title // Add the last entry if complete
entries.sort((a, b) => { if (currentEntry.length > 0 && !currentEntry[currentEntry.length - 1].startsWith('#')) {
const groupTitleA = (a[0].match(/group-title="([^"]*)"/) || [])[1] || ''; entries.push(currentEntry);
const groupTitleB = (b[0].match(/group-title="([^"]*)"/) || [])[1] || ''; }
return groupTitleA.localeCompare(groupTitleB);
});
// Rebuild the file content // Sort entries by group-title
const sortedContent = [ entries.sort((a, b) => {
header, const groupTitleA = (a[0].match(/group-title="([^"]*)"/) || [])[1] || '';
...entries.flatMap(entry => entry) const groupTitleB = (b[0].match(/group-title="([^"]*)"/) || [])[1] || '';
].join('\n'); return groupTitleA.localeCompare(groupTitleB);
});
// Write back to file // Rebuild the file content
fs.writeFileSync(filePath, sortedContent); const sortedContent = [
console.log('M3U file has been sorted by group-title'); header,
...entries.flatMap(entry => entry)
].join('\n');
// Write back to file
fs.writeFileSync(filePath, sortedContent);
console.log('M3U file has been sorted by group-title');
} }
// Get file path from command line argument // Get file path from command line argument
const filePath = process.argv[2]; const filePath = process.argv[2];
if (!filePath) { if (!filePath) {
console.error('Please provide a file path'); console.error('Please provide a file path');
process.exit(1); process.exit(1);
} }
try { try {
sortM3uByGroupTitle(filePath); sortM3uByGroupTitle(filePath);
} catch (error) { } catch (error) {
console.error('Error:', error.message); console.error('Error:', error.message);
process.exit(1); process.exit(1);
} }