const fs = require('fs'); const path = require('path'); // Define cultural groups by country const culturalGroups = { anglosphere: [ 'united kingdom', 'uk', 'britain', 'england', 'scotland', 'wales', 'northern ireland', 'united states', 'usa', 'america', 'canada', 'australia', 'new zealand', 'ireland' ], francophone: [ 'france', 'belgium', 'switzerland', 'quebec', 'monaco', 'luxembourg', 'haiti', 'ivory coast', 'senegal', 'cameroon' ], hispanic: [ 'spain', 'mexico', 'argentina', 'chile', 'colombia', 'peru', 'venezuela', 'ecuador', 'guatemala', 'cuba', 'dominican republic', 'honduras', 'el salvador', 'nicaragua', 'costa rica', 'panama' ], lusophone: [ 'portugal', 'brazil', 'angola', 'mozambique', 'cape verde', 'guinea-bissau', 'sao tome and principe' ], arabic: [ 'saudi arabia', 'egypt', 'uae', 'united arab emirates', 'qatar', 'kuwait', 'oman', 'bahrain', 'yemen', 'iraq', 'syria', 'jordan', 'lebanon', 'palestine', 'libya', 'tunisia', 'algeria', 'morocco', 'sudan' ], germanosphere: [ 'germany', 'austria', 'switzerland', 'luxembourg', 'liechtenstein' ], slavic: [ 'russia', 'ukraine', 'belarus', 'poland', 'czech republic', 'slovakia', 'serbia', 'croatia', 'bosnia', 'montenegro', 'slovenia', 'bulgaria', 'north macedonia' ], sinosphere: [ 'china', 'hong kong', 'taiwan', 'singapore', 'macau' ], indosphere: [ 'india', 'pakistan', 'bangladesh', 'nepal', 'sri lanka', 'bhutan', 'maldives' ], turkic: [ 'turkey', 'azerbaijan', 'uzbekistan', 'kazakhstan', 'kyrgyzstan', 'turkmenistan' ], nordic: [ 'sweden', 'norway', 'denmark', 'finland', 'iceland', 'faroe islands', 'greenland' ], baltic: [ 'estonia', 'latvia', 'lithuania' ], hellenic: [ 'greece', 'cyprus' ], benelux: [ 'netherlands', 'belgium', 'luxembourg' ], persian: [ 'iran', 'afghanistan', 'tajikistan' ], malaysphere: [ 'malaysia', 'brunei', 'indonesia' ], korean: [ 'south korea', 'korea', 'north korea' ], japanese: [ 'japan' ], vietnamese: [ 'vietnam' ], thai: [ 'thailand' ] }; function getCulturalGroup(channelInfo) { // Look specifically for group-title (country information) const groupMatch = channelInfo.match(/group-title="([^"]*)"/) || []; const groupTitle = (groupMatch[1] || '').toLowerCase(); // Check if the country belongs to any cultural group for (const [group, countries] of Object.entries(culturalGroups)) { if (countries.some(country => groupTitle.includes(country))) { return group; } } return null; // Return null instead of 'other' for non-matching channels } function splitByCulturalGroup(filePath) { const content = fs.readFileSync(filePath, 'utf8'); const lines = content.split('\n'); // Create cultural groups directory const groupsDir = path.join(path.dirname(filePath), 'cultural-groups'); if (!fs.existsSync(groupsDir)) { fs.mkdirSync(groupsDir); } // Get existing cultural group files const existingFiles = fs.readdirSync(groupsDir) .filter(file => file.endsWith('.m3u')) .map(file => file.toLowerCase()); const groups = {}; let currentExtinf = null; // Verify M3U header const header = lines[0]; if (!header.startsWith('#EXTM3U')) { throw new Error('Invalid M3U file: Missing #EXTM3U header'); } // Process lines lines.forEach(line => { line = line.trim(); if (!line) return; if (line.startsWith('#EXTINF')) { currentExtinf = line; const culturalGroup = getCulturalGroup(line); // Only add to a group if there's a match if (culturalGroup) { if (!groups[culturalGroup]) { groups[culturalGroup] = ['#EXTM3U']; } groups[culturalGroup].push(line); } } else if (currentExtinf && !line.startsWith('#')) { const culturalGroup = getCulturalGroup(currentExtinf); // Only add the URL line if the channel belonged to a group if (culturalGroup) { groups[culturalGroup].push(line); } currentExtinf = null; } }); // Get list of current cultural group files const currentGroupFiles = Object.keys(groups).map(groupTitle => `${groupTitle.toLowerCase()}.m3u` ); // Remove obsolete files existingFiles.forEach(existingFile => { if (!currentGroupFiles.includes(existingFile)) { const fileToRemove = path.join(groupsDir, existingFile); fs.unlinkSync(fileToRemove); console.log(`Removed obsolete cultural group playlist: ${existingFile}`); } }); // Write cultural group files Object.entries(groups).forEach(([groupTitle, groupLines]) => { const groupFilePath = path.join(groupsDir, `${groupTitle.toLowerCase()}.m3u`); fs.writeFileSync(groupFilePath, groupLines.join('\n') + '\n'); console.log(`Created/updated cultural group playlist: ${groupFilePath}`); }); // Generate summary 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('\nCultural group split summary:'); console.log(summary.join('\n')); } const filePath = process.argv[2]; if (!filePath) { console.error('Please provide the path to the M3U file'); process.exit(1); } try { splitByCulturalGroup(filePath); } catch (error) { console.error('Error splitting M3U file:', error.message); process.exit(1); }