diff --git a/sites/tv.yandex.ru/readme.md b/sites/tv.yandex.ru/readme.md index b3ce9808..3874150c 100644 --- a/sites/tv.yandex.ru/readme.md +++ b/sites/tv.yandex.ru/readme.md @@ -4,7 +4,28 @@ https://tv.yandex.ru/ This site is protected by captcha, so if you hit by an error `Got captcha, please goto https://tv.yandex.ru and update cookies!`, update site configuration in `tv.yandex.ru.config.js` by heading to this site and use browser -Developer Tools and replace matching cookies. +Developer Tools and replace matching cookies. The Cookie will likely lasting for one year. + +To enable debugging while updating channel list or grabbing the guide, set `DEBUG` environment +to `site:tv.yandex.ru` or `site:*`. + +On Windows with Command Prompt: + +```sh +set DEBUG="site:tv.yandex.ru" +``` + +On Windows with PowerShell: + +```ps +$env:DEBUG="site:tv.yandex.ru" +``` + +On Unix-like OSes: + +```sh +export DEBUG="site:tv.yandex.ru" +``` ### Download the guide diff --git a/sites/tv.yandex.ru/tv.yandex.ru.channels.xml b/sites/tv.yandex.ru/tv.yandex.ru.channels.xml index 322c3882..f6ca8cf7 100644 --- a/sites/tv.yandex.ru/tv.yandex.ru.channels.xml +++ b/sites/tv.yandex.ru/tv.yandex.ru.channels.xml @@ -1,364 +1,100 @@ - Неизвестная Планета - Страшное HD - Museum TV - Открытый мир - SHANT Premium - 4K Fashion TV - Бьюти.TV - КБС Рус - SONGTV Georgia - Светлое ТВ - Самара 24 - Диалоги о рыбалке - Китай ТВ - Перпетуум Мобиле - КиноМеню HD - Детское кино - МультСезон - Теледом - Дом Кино Int - Удар - Дума ТВ - Глазами туриста 4К - Живи активно HD Foodman.club - DetectiveJam - FamilyJam - Окко.Спорт - KidsTV - Дорама HD - Загородная жизнь HD - Sumiko - Хабар 24 Вкусное TV Советские мультфильмы Сказки Зайки - МузСоюз Терра Инкогнита Мир вокруг Россия-Планета MTV 80s - ТНВ - Беларусь-24 - Ля-минор. Мой музыкальный - Nickelodeon HD - РТВ - Любимое кино - Настоящее Страшное Телевидение - Nick Jr - Sony ТВ HD - HD Медиа - 1 HD Music Television - 360° - 360° Новости - 365 дней ТВ + CNL-Сибирь + 4K Fashion TV + Бьюти.TV + Удар + Дума ТВ + Глазами туриста 4К + Живи активно HD + Sumiko + ROMANCE + Cartoon Classics + Suspense + VHS + Чижик + KIONХИТ + Народ Все Видит + Новый Игровой Канал + Epic + MIXM + Мы + Q Arena + Q Footboll + Q League Три Ангела - 7 TV + 360.ru A1 - A2 - Aiva Amedia Hit - Amedia Premium HD - Анекдот ТВ - Ani - Arirang - Арсенал - Авто Плюс - Baby Time - Башкортостан 24 - БелРос Большая Азия Блокбастер HD - Бобер - Бокс ТВ - Bollywood HD - Bridge TV - Bridge TV Classic - Bridge TV Deluxe Bridge TV Фрэш - Bridge TV Hits - Bridge Rock - Bridge TV Русский Хит - BRIDGE TV Шлягер Карусель INT - CBS Reality - Центральное телевидение (ЦТВ) Пятый International - 8 Канал Первый Cinema - C Music TV - Club MTV - CNBC - CuriosityStream - Da Vinci - День Победы Детский мир - Телеканал Совета Федерации "Вместе РФ" Дайвинг.TV - Доктор - DocuBox HD - Домашние животные - Драйв - DuckTV - Душевное EarthTV The World Live Эхо TV - ЕДА - ЕГЭ - English Class HD - E TV - Europa Plus TV - Еврокино - Extreme Sports - FAN - Fashion Box HD - Fashion & LifeStyle - Fashion & Style 4K - Fashion TV - Fashion TV HD - Fast & Fun Box HD - Феникс+ Кино - FilmBox HD - FilmBox Arthouse - FoodTime - Футбол - FreshTV - Fuel TV HD - Футбольный - Gagsnetwork + Мяч Глазами туриста - Gulli Girl - HDL - History - HISTORY2 - History2 HD Хит HD - Hollywood - Надежда Хузур ТВ - Иллюзион + - Индия Индийское кино - Insight Ultra HD - История - Известия Jibek Joly Joy Cook - Калейдоскоп ТВ Камеди - Капитан Фантастика - Кавказ 24 - KHL - KHL Prime - Хоккейный + Хоккей ТВ Кинеко - Кинохит - KinoJam 1 - KinoJam 2 - Кинокомедия - КИНОМАН - Киномикс - Кинопоказ HD - Кинопремьера Киносемья - Киносерия - Киносвидание - КиноТВ - КИНОУЖАС - Ключ - Комедия - Комедийное - Конный мир - Кто есть кто - Кухня - КВН ТВ - ЛДПР ТВ Лёва - Luxe HD - Luxe.TV - LUXURY - Любимое.ТВ - Мама - Матч! Арена - Матч! Боец - Матч! Футбол 1 - Матч! Футбол 1 HD Матч! Футбол 2 - Матч! Футбол 2 HD - Матч! Футбол 3 - Матч! Игра - Матч Премьер - Матур ТВ - MCM TOP - Mezzo - Mezzo Live - Hollywood HD - МИР 24 - Мир сериала - MMA-TV.com - Мосфильм. Золотая коллекция - Классика Кино - Моя Планета - Моя стихия - MTV 90s - MTV Hits International - MTV Live International HD - МУЛЬТ - Мультиландия - Мультимузыка - Music Box Gold - Мужское кино - Мужское кино HD - Мужской - Мужской МУЗ-ТВ - Музыка Первого - myZen.tv Нано - Наше HD Наше Мужское HD - Наше Новое Кино Наш Кинопоказ HD - Наука Ностальгия - Новое радио - Новый мир НТВ Хит - НТВ Право НТВ-Мир - НТВ Сериал - НТВ Стиль - О! - О2ТВ - Океан HD - Охотник и Рыболов HD - Охотник и Рыболов Int - Охота и рыбалка - О, Кино! - Оружие - Остросюжетное HD ОТВ - Первый Космический - Первый вегетарианский - Пёс и Ко - Plan B - ПОБЕДА - Поехали! - Премиальное - Приключения HD - Про Бизнес Про Любовь Просвещение - Психология 21 - Q Sport - Радость Моя - Ратник - РБК - Красная линия Ретро ТВ - Родное кино RT RT Doc - RTД - Russian Travel Guide HD - RTG International - Russian Travel Guide - RTVI - Россия 1 HD - Russian Extreme - Русский Экстрим - Russian Extreme Ultra HD - Music Box Russia - Русский бестселлер - Русский Детектив - Русский Иллюзион - Русский роман - RU TV - Рыжий - РЖД Сапфир - Сарафан - .Sci-Fi - Шансон-TB - Шокирующее Shot TV Смайлик ТВ - SongTV Armenia - SONGTV Russia - .Red - .Black Советское кино - Союз - Спас ТВ - Спортивный - Старт + ТВ Спорт START Air Старт Триумф START World - Stingray iConcerts Страна FM СТС International - СТС Kids HD - СТС Love СуперГерои - Тайна - Тайны Галактики - ТБН Театр - Телекафе - Телепутешествия TERRA - TiJi - Timeless Dizi Channel ТНТ International - ТНТ Music ТНВ-планета - Точка ТВ - Тонус - Совершенно секретно - Travel+Adventure - Travel+Adventure HD - TV BRICS - ТВ Центр ТВ Центр-International TVMChannel - ТВТУР - ТВ21 Ultra HD Cinema - Уникум - Неизвестная Россия - Усадьба Успех - В гостях у сказки MTV 00s - viju Explore - viju History - viju+ Comedy - viju+ Megahit - viju+ Planet - viju+ Premiere - viju+ Serial - viju+ Sport - viju TV1000 - viju TV1000 action - viju TV1000 русское - В мире животных Время - World Business Channel - World Fashion Channel - Загородная жизнь - Загородный Загородный Int - Зал суда - Здоровое ТВ - Жара Жар Птица Живая планета Живая природа - Живи! - Zooпарк Зоо ТВ - Звезда Плюс diff --git a/sites/tv.yandex.ru/tv.yandex.ru.config.js b/sites/tv.yandex.ru/tv.yandex.ru.config.js index bb65e296..22000db5 100644 --- a/sites/tv.yandex.ru/tv.yandex.ru.config.js +++ b/sites/tv.yandex.ru/tv.yandex.ru.config.js @@ -3,18 +3,19 @@ const debug = require('debug')('site:tv.yandex.ru') // enable to fetch guide description but its take a longer time const detailedGuide = true +const nworker = 10 // update this data by heading to https://tv.yandex.ru and change the values accordingly const cookies = { - cycada: '3w11iWu+2+o6iIIiI/S1/k9lFIb6y+G6SW6hsbLoPJg=', - i: '0nUBW1d6GpFmpLRIuHYGulEA4alIC2j4WS+WYGcusydL7lcrG9loWX8qrFEBOqg54KZxGwCVaZhZ1THYgoIo0T69iCY=', - spravka: 'dD0xNzAxMjI3MTk1O2k9MzYuODQuOTguMTcxO0Q9Njk4NDQwRkRDODk5QUEzMDJCNzI5NTJBMTM4RTY2ODNEMzQyNkM1MjI5QTkyNDI3NUJGMzMzQUJEMUZFQjMyQzczM0I2QzE0QTRDQkJFODY5Nzk0MjhGNkEzQjQ5NDJBMzcxQzIzMjE3RTRENkVDOUU1NEE1RDVFNDg0RUQ1RTI3OUNGNzlCMEYzNzUyMDcyNDhGQkVCNkIyMDg5NTMwMzc1QkZEQTlGNEU7dT0xNzAxMjI3MTk1NDg5NDIyODkzO2g9OTRmN2FiNTMxZmJjNDg5MjM4ZDk4Y2ZkN2E0ZmY0YmI=', - yandexuid: '7536067781700842414', - yashr: '7271154091700842416', - user_display: 696 + i: 'dkim62pClrWWC4CShVQYMpVw1ELNVw4XJdL/lzT4E2r05IgcST1GtCA4ho/UyGgW2AO4qftDfZzGX2OHqCzwY7GUkpM=', + spravka: 'dD0xNzMyNjgzMTEwO2k9MTgwLjI0OC41OS40MDtEPTkyOUM2MkQ0Mzc3OUNBMUFCNzg3NTIyMEQ4OEJBMEVBMzQ2RUNGNUU5Q0FEQUM5RUVDMTFCNjc1ODA2MThEQTQ3RTY3RTUyRUNBRDdBMTY2OTY1MjMzRDU1QjNGMTc1MDA0NDM3MjBGMUNGQTM5RjA3OUQwRjE2MzQxMUNFOTgxQ0E0RjNGRjRGODNCMEM1QjlGNTg5RkI4NDk0NEM2QjNDQUQ5NkJGRTBFNTVCQ0Y1OTEzMEY0O3U9MTczMjY4MzExMDY3MTA1MzIzNDtoPTA1YWJmMTY0ZmI2MGViNTBhMDUwZWUwMThmYWNiYjhm', + yandexuid: '1197179041732383499', + yashr: '4682342911732383504', + yuidss: '1197179041732383499', + user_display: 930, } const headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 OPR/104.0.0.0', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.0.0.0', } const caches = {} @@ -85,19 +86,27 @@ module.exports = { async function fetchSchedules({ date, content = null }) { const schedules = [] const queues = [] + const fetches = [] const url = getUrl(date) let mainApi // parse content as schedules and add to queue if more requests is needed - const f = data => { + const f = (data, src) => { + if (src) { + fetches.push(src) + } const [q, s] = parseContent(data, date) if (!mainApi) { mainApi = true if (caches.region) { - queues.push(`https://tv.yandex.ru/api/${caches.region}?date=${date.format('YYYY-MM-DD')}&grid=all&period=all-day`) + queues.push(getUrl(date, caches.region)) + } + } + for (const url of q) { + if (fetches.indexOf(url) < 0) { + queues.push(url) } } - queues.push(...q) schedules.push(...s) } // is main html already fetched? @@ -120,7 +129,7 @@ async function fetchPrograms({ schedules, date, channel }) { queues.push( ...schedule.events .filter(event => date.isSame(event.start, 'day')) - .map(event => `https://tv.yandex.ru/api/${caches.region}/event?eventId=${event.id}&programCoId=`) + .map(event => getUrl(null, caches.region, null, event)) ) }) await doFetch(queues, getUrl(date), content => { @@ -144,31 +153,60 @@ async function fetchPrograms({ schedules, date, channel }) { } async function doFetch(queues, referer, cb) { - const axios = require('axios') - while (true) { - if (!queues.length) { - break + if (queues.length) { + const workers = [] + let n = Math.min(nworker, queues.length) + while (workers.length < n) { + const worker = () => { + if (queues.length) { + const url = queues.shift() + debug(`Fetching ${url}`) + const data = { + 'Origin': 'https://tv.yandex.ru', + } + if (referer) { + data['Referer'] = referer + } + if (url.indexOf('api') > 0) { + data['X-Requested-With'] = 'XMLHttpRequest' + } + const headers = getHeaders(data) + doRequest(url, { headers }) + .then(res => { + cb(res, url) + worker() + }) + } else { + workers.splice(workers.indexOf(worker), 1) + } + } + workers.push(worker) + worker() } - const url = queues.shift() - debug(`Fetching ${url}`) - const data = url.indexOf('api') > 0 ? { - 'Referer': referer, - 'Origin': 'https://tv.yandex.ru', - 'X-Requested-With': 'XMLHttpRequest' - } : {} - const params = { headers: getHeaders(data) } - const content = await axios - .get(url, params) - .then(response => { - parseCookies(response.headers) - return response.data - }) - .catch(err => console.error(err.message)) - - cb(content) + await new Promise(resolve => { + const interval = setInterval(() => { + if (workers.length === 0) { + clearInterval(interval) + resolve() + } + }, 500) + }) } } +async function doRequest(url, params) { + const axios = require('axios') + const content = await axios + .get(url, params) + .then(response => { + parseCookies(response.headers) + return response.data + }) + .catch(err => console.error(err.message)) + + return content +} + function parseContent(content, date, checkOnly = false) { const queues = [] const schedules = [] @@ -186,7 +224,7 @@ function parseContent(content, date, checkOnly = false) { if (content.schedule) { // fetch next request based on schedule map if (Array.isArray(content.schedule.scheduleMap)) { - queues.push(...content.schedule.scheduleMap.map(m => `https://tv.yandex.ru/api/${caches.region}/main/chunk?page=${m.id}&date=${date.format('YYYY-MM-DD')}&period=all-day&offset=${m.offset}&limit=${m.limit}`)) + queues.push(...content.schedule.scheduleMap.map(m => getUrl(date, caches.region, m))) } // find some schedules? if (Array.isArray(content.schedule.schedules)) { @@ -244,10 +282,29 @@ function getSchedules(schedules) { function getHeaders(data = {}) { return Object.assign({}, headers, { - 'Cookie': Object.keys(cookies).map(cookie => `${cookie}=${cookies[cookie]}`).join('; ') + Cookie: Object.keys(cookies).map(cookie => `${cookie}=${cookies[cookie]}`).join('; ') }, data) } -function getUrl(date) { - return `https://tv.yandex.ru/?date=${date.format('YYYY-MM-DD')}&grid=all&period=all-day` +function getUrl(date, region = null, page = null, event = null) { + let url = 'https://tv.yandex.ru/' + if (region) { + url += `api/${region}` + } + if (page && page.id !== undefined) { + url += `${url.endsWith('/') ? '' : '/'}main/chunk?page=${page.id}` + } + if (event && event.id !== undefined) { + url += `${url.endsWith('/') ? '' : '/'}event?eventId=${event.id}&programCoId=` + } + if (date) { + url += `${url.indexOf('?') < 0 ? '?' : '&'}date=${date.format('YYYY-MM-DD')}${!page ? '&grid=all' : ''}&period=all-day` + } + if (page && page.id !== undefined && page.offset !== undefined) { + url += `${url.indexOf('?') < 0 ? '?' : '&'}offset=${page.offset}` + } + if (page && page.id !== undefined && page.limit !== undefined) { + url += `${url.indexOf('?') < 0 ? '?' : '&'}limit=${page.limit}` + } + return url } \ No newline at end of file diff --git a/sites/tv.yandex.ru/tv.yandex.ru.test.js b/sites/tv.yandex.ru/tv.yandex.ru.test.js index 5bab5f01..42f143a1 100644 --- a/sites/tv.yandex.ru/tv.yandex.ru.test.js +++ b/sites/tv.yandex.ru/tv.yandex.ru.test.js @@ -52,12 +52,12 @@ it('can generate valid url', () => { it('can generate valid request headers', () => { expect(request.headers).toMatchObject({ Cookie: - 'cycada=3w11iWu+2+o6iIIiI/S1/k9lFIb6y+G6SW6hsbLoPJg=; ' + - 'i=0nUBW1d6GpFmpLRIuHYGulEA4alIC2j4WS+WYGcusydL7lcrG9loWX8qrFEBOqg54KZxGwCVaZhZ1THYgoIo0T69iCY=; ' + - 'spravka=dD0xNzAxMjI3MTk1O2k9MzYuODQuOTguMTcxO0Q9Njk4NDQwRkRDODk5QUEzMDJCNzI5NTJBMTM4RTY2ODNEMzQyNkM1MjI5QTkyNDI3NUJGMzMzQUJEMUZFQjMyQzczM0I2QzE0QTRDQkJFODY5Nzk0MjhGNkEzQjQ5NDJBMzcxQzIzMjE3RTRENkVDOUU1NEE1RDVFNDg0RUQ1RTI3OUNGNzlCMEYzNzUyMDcyNDhGQkVCNkIyMDg5NTMwMzc1QkZEQTlGNEU7dT0xNzAxMjI3MTk1NDg5NDIyODkzO2g9OTRmN2FiNTMxZmJjNDg5MjM4ZDk4Y2ZkN2E0ZmY0YmI=; ' + - 'yandexuid=7536067781700842414; ' + - 'yashr=7271154091700842416; ' + - 'user_display=696' + 'i=dkim62pClrWWC4CShVQYMpVw1ELNVw4XJdL/lzT4E2r05IgcST1GtCA4ho/UyGgW2AO4qftDfZzGX2OHqCzwY7GUkpM=; ' + + 'spravka=dD0xNzMyNjgzMTEwO2k9MTgwLjI0OC41OS40MDtEPTkyOUM2MkQ0Mzc3OUNBMUFCNzg3NTIyMEQ4OEJBMEVBMzQ2RUNGNUU5Q0FEQUM5RUVDMTFCNjc1ODA2MThEQTQ3RTY3RTUyRUNBRDdBMTY2OTY1MjMzRDU1QjNGMTc1MDA0NDM3MjBGMUNGQTM5RjA3OUQwRjE2MzQxMUNFOTgxQ0E0RjNGRjRGODNCMEM1QjlGNTg5RkI4NDk0NEM2QjNDQUQ5NkJGRTBFNTVCQ0Y1OTEzMEY0O3U9MTczMjY4MzExMDY3MTA1MzIzNDtoPTA1YWJmMTY0ZmI2MGViNTBhMDUwZWUwMThmYWNiYjhm; ' + + 'yandexuid=1197179041732383499; ' + + 'yashr=4682342911732383504; ' + + 'yuidss=1197179041732383499; ' + + 'user_display=930' }) })