const axios = require('axios') const cheerio = require('cheerio') const dayjs = require('dayjs') const utc = require('dayjs/plugin/utc') const timezone = require('dayjs/plugin/timezone') const customParseFormat = require('dayjs/plugin/customParseFormat') dayjs.extend(utc) dayjs.extend(timezone) dayjs.extend(customParseFormat) let sessionCache = null async function getSession(forceRefresh = false) { if (sessionCache && !forceRefresh) { return sessionCache } try { const initResponse = await axios.get('https://tv.dir.bg/init') if (!initResponse.data) { throw new Error('No response data from init endpoint') } // Extract cookies from response headers const setCookieHeader = initResponse.headers['set-cookie'] let xsrfToken = null let dirSessionCookie = null if (setCookieHeader) { setCookieHeader.forEach(cookie => { // Extract XSRF token from cookie const xsrfMatch = cookie.match(/XSRF-TOKEN=([^;]+)/) if (xsrfMatch) { xsrfToken = decodeURIComponent(xsrfMatch[1]) } // Extract dir_session cookie const sessionMatch = cookie.match(/dir_session=([^;]+)/) if (sessionMatch) { dirSessionCookie = sessionMatch[1] } }) } const csrfToken = initResponse.data.csrfToken if (!csrfToken) { throw new Error('No CSRF/XSRF token found in response') } // Build cookie string let cookieString = '' if (xsrfToken) { cookieString += `XSRF-TOKEN=${encodeURIComponent(xsrfToken)}` } if (dirSessionCookie) { if (cookieString) cookieString += '; ' cookieString += `dir_session=${dirSessionCookie}` } sessionCache = { csrfToken, cookieString, timestamp: Date.now() } return sessionCache } catch (error) { console.error('Error getting session:', error.message) throw error } } module.exports = { site: 'tv.dir.bg', days: 2, url: 'https://tv.dir.bg/load/programs', request: { maxContentLength: 125000000, // 10 MB method: 'POST', async headers() { try { const session = await getSession() return { 'Cookie': session.cookieString, 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest' } } catch (error) { console.error('Error getting headers:', error.message) throw error } }, async data({ channel, date }) { try { const session = await getSession() const params = new URLSearchParams() params.append('_token', session.csrfToken) params.append('channel', channel.site_id) params.append('day', date.format('YYYY-MM-DD')) return params } catch (error) { console.error('Error preparing request data:', error.message) throw error } }, }, parser({ content, date }) { const programs = [] const items = parseItems(content) items.forEach(item => { const prev = programs[programs.length - 1] const $item = cheerio.load(item) let start = parseStart($item, date) if (prev) { if (start.isBefore(prev.start)) { start = start.add(1, 'd') } prev.stop = start } const stop = start.add(30, 'm') programs.push({ title: parseTitle($item), start, stop }) }) return programs }, async channels() { try { const response = await axios.get('https://tv.dir.bg/channels') const $ = cheerio.load(response.data) const channels = [] $('.channel_cont').each((_index, element) => { const $element = $(element) const $link = $element.find('a.channel_link') const href = $link.attr('href') const $img = $element.find('img') const name = $img.attr('alt') const logo = $img.attr('src') const site_id = href ? href.match(/\/programa\/(\d+)/)?.[1] : '' if (site_id && name) { channels.push({ lang: 'bg', site_id: site_id, name: name.trim(), logo: logo ? (logo.startsWith('http') ? logo : `https://tv.dir.bg${logo}`) : null }) } }) return channels } catch (error) { console.error('Error fetching channels:', error.message) return [] } }, clearSession() { sessionCache = null } } function parseStart($item, date) { const time = $item('.broadcast-time').text().trim() const dateString = `${date.format('YYYY-MM-DD')} ${time}` return dayjs.tz(dateString, 'YYYY-MM-DD HH:mm', 'Europe/Sofia') } function parseTitle($item) { return $item('.broadcast-title').text() .replace(/\s+/g, ' ') .trim() } function parseItems(content) { try { const json = JSON.parse(content) if (!json || json.status !== true) { return [] } const $ = cheerio.load(json.html) const items = $('.broadcast-item').toArray() return items } catch (error) { console.error('❌ Error parsing items:', error.message) console.error('Error stack:', error.stack) return [] } }