no message
This commit is contained in:
parent
f236e80fd6
commit
e07e71d324
2 changed files with 160 additions and 131 deletions
|
@ -2,93 +2,108 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
|
||||
function splitByGroup(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
// Create groups directory if it doesn't exist
|
||||
const groupsDir = path.join(path.dirname(filePath), 'countries');
|
||||
if (!fs.existsSync(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;
|
||||
|
||||
// Create groups directory if it doesn't exist
|
||||
const groupsDir = path.join(path.dirname(filePath), 'countries');
|
||||
if (!fs.existsSync(groupsDir)) {
|
||||
fs.mkdirSync(groupsDir);
|
||||
}
|
||||
|
||||
// Get existing country files
|
||||
const existingFiles = fs.readdirSync(groupsDir)
|
||||
.filter(file => file.endsWith('.m3u'))
|
||||
.map(file => file.toLowerCase());
|
||||
|
||||
const groups = {};
|
||||
let currentExtinf = null;
|
||||
|
||||
// First line should be #EXTM3U
|
||||
const header = lines[0];
|
||||
if (!header.startsWith('#EXTM3U')) {
|
||||
throw new Error('Invalid M3U file: Missing #EXTM3U header');
|
||||
}
|
||||
|
||||
// Process each line
|
||||
lines.forEach(line => {
|
||||
line = line.trim();
|
||||
if (!line) return;
|
||||
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);
|
||||
|
||||
if (line.startsWith('#EXTINF')) {
|
||||
currentExtinf = line;
|
||||
const groupMatch = line.match(/group-title="([^"]*)"/);
|
||||
const groupTitle = groupMatch ? groupMatch[1] : 'Unknown';
|
||||
|
||||
if (!groups[groupTitle]) {
|
||||
groups[groupTitle] = ['#EXTM3U'];
|
||||
}
|
||||
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'));
|
||||
// Reset for the next entry
|
||||
isInEntry = false;
|
||||
currentEntry = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// Count entries properly by counting #EXTINF lines
|
||||
const summary = Object.entries(groups).map(([group, lines]) => {
|
||||
const channelCount = lines.filter(line => line.startsWith('#EXTINF')).length;
|
||||
return `${group}: ${channelCount} channels`;
|
||||
});
|
||||
|
||||
console.log('\nPlaylist split summary:');
|
||||
console.log(summary.join('\n'));
|
||||
}
|
||||
|
||||
const filePath = process.argv[2];
|
||||
if (!filePath) {
|
||||
console.error('Please provide the path to mystique.m3u');
|
||||
process.exit(1);
|
||||
console.error('Please provide the path to mystique.m3u');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
splitByGroup(filePath);
|
||||
splitByGroup(filePath);
|
||||
} catch (error) {
|
||||
console.error('Error splitting mystique.m3u:', error.message);
|
||||
process.exit(1);
|
||||
console.error('Error splitting mystique.m3u:', error.message);
|
||||
process.exit(1);
|
||||
}
|
|
@ -1,67 +1,81 @@
|
|||
const fs = require('fs');
|
||||
|
||||
function sortM3uByGroupTitle(filePath) {
|
||||
// Read the file
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
// Read the file
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
// First line should be #EXTM3U
|
||||
const header = lines[0];
|
||||
if (!header.startsWith('#EXTM3U')) {
|
||||
throw new Error('Invalid M3U file: Missing #EXTM3U header');
|
||||
}
|
||||
|
||||
// Group the entries
|
||||
const entries = [];
|
||||
let currentEntry = [];
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (!line) continue;
|
||||
|
||||
// First line should be #EXTM3U
|
||||
const header = lines[0];
|
||||
if (!header.startsWith('#EXTM3U')) {
|
||||
throw new Error('Invalid M3U file: Missing #EXTM3U header');
|
||||
}
|
||||
|
||||
// Group the entries
|
||||
const entries = [];
|
||||
let currentEntry = [];
|
||||
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (!line) continue;
|
||||
|
||||
if (line.startsWith('#EXTINF')) {
|
||||
if (currentEntry.length === 2) {
|
||||
entries.push(currentEntry);
|
||||
}
|
||||
currentEntry = [line];
|
||||
} else if (currentEntry.length === 1) {
|
||||
currentEntry.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last entry if exists
|
||||
if (currentEntry.length === 2) {
|
||||
if (line.startsWith('#EXTINF')) {
|
||||
// If we have a previous entry that's complete (has a URL), add it
|
||||
if (currentEntry.length > 0 && !currentEntry[currentEntry.length - 1].startsWith('#')) {
|
||||
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
|
||||
entries.sort((a, b) => {
|
||||
const groupTitleA = (a[0].match(/group-title="([^"]*)"/) || [])[1] || '';
|
||||
const groupTitleB = (b[0].match(/group-title="([^"]*)"/) || [])[1] || '';
|
||||
return groupTitleA.localeCompare(groupTitleB);
|
||||
});
|
||||
|
||||
// Rebuild the file content
|
||||
const sortedContent = [
|
||||
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');
|
||||
}
|
||||
|
||||
// Add the last entry if complete
|
||||
if (currentEntry.length > 0 && !currentEntry[currentEntry.length - 1].startsWith('#')) {
|
||||
entries.push(currentEntry);
|
||||
}
|
||||
|
||||
// Sort entries by group-title
|
||||
entries.sort((a, b) => {
|
||||
const groupTitleA = (a[0].match(/group-title="([^"]*)"/) || [])[1] || '';
|
||||
const groupTitleB = (b[0].match(/group-title="([^"]*)"/) || [])[1] || '';
|
||||
return groupTitleA.localeCompare(groupTitleB);
|
||||
});
|
||||
|
||||
// Rebuild the file content
|
||||
const sortedContent = [
|
||||
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
|
||||
const filePath = process.argv[2];
|
||||
if (!filePath) {
|
||||
console.error('Please provide a file path');
|
||||
process.exit(1);
|
||||
console.error('Please provide a file path');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
sortM3uByGroupTitle(filePath);
|
||||
sortM3uByGroupTitle(filePath);
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
console.error('Error:', error.message);
|
||||
process.exit(1);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue