From efd88698cbedda89feabbf788cd47ff09b4c4123 Mon Sep 17 00:00:00 2001 From: Toha Date: Wed, 29 Nov 2023 12:12:54 +0700 Subject: [PATCH] Update tv.yandex.ru TV guide api. This site is captcha protected, so it will likely the cookies need to be updated regularly. Signed-off-by: Toha --- sites/tv.yandex.ru/__data__/content.html | 86 +++ sites/tv.yandex.ru/__data__/program.json | 251 +++++++ sites/tv.yandex.ru/__data__/schedule.json | 274 ++++++++ sites/tv.yandex.ru/__data__/schedule0.json | 4 + sites/tv.yandex.ru/readme.md | 25 + sites/tv.yandex.ru/tv.yandex.ru.channels.xml | 653 +++++++++++-------- sites/tv.yandex.ru/tv.yandex.ru.config.js | 263 +++++++- sites/tv.yandex.ru/tv.yandex.ru.test.js | 72 +- 8 files changed, 1292 insertions(+), 336 deletions(-) create mode 100644 sites/tv.yandex.ru/__data__/content.html create mode 100644 sites/tv.yandex.ru/__data__/program.json create mode 100644 sites/tv.yandex.ru/__data__/schedule.json create mode 100644 sites/tv.yandex.ru/__data__/schedule0.json create mode 100644 sites/tv.yandex.ru/readme.md diff --git a/sites/tv.yandex.ru/__data__/content.html b/sites/tv.yandex.ru/__data__/content.html new file mode 100644 index 00000000..ee459a32 --- /dev/null +++ b/sites/tv.yandex.ru/__data__/content.html @@ -0,0 +1,86 @@ + + + + + Программа передач ТВ — Яндекс.Телепрограмма + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +

Программа передач в Джакарте

пн, 13
вт, 14
ср, 15
чт, 16
пт, 17
сб, 18
вс, 19
пн, 20
вт, 21
ср, 22
чт, 23
Вчера
Сейчас
Сегодня
вс, 26
пн, 27
вт, 28
ср, 29
чт, 30
пт, 1
сб, 2
вс, 3
пн, 20
вт, 21
ср, 22
чт, 23
Вчера
Сейчас
Сегодня
вс, 26
Чтобы избранные каналы поднялись наверх, обновите страницуОбновить
+
+ + + + diff --git a/sites/tv.yandex.ru/__data__/program.json b/sites/tv.yandex.ru/__data__/program.json new file mode 100644 index 00000000..1253c193 --- /dev/null +++ b/sites/tv.yandex.ru/__data__/program.json @@ -0,0 +1,251 @@ +{ + "id": 217749657, + "channelId": 142, + "channelFamilyId": 16, + "live": false, + "episode": { + "id": 6773710, + "description": "Валерий Сюткин делится историями из жизни, которые повлияли на его судьбу.", + "title": "Мелодии моей жизни" + }, + "program": { + "trailers": [], + "hasTrailers": false, + "onlines": [], + "id": 6719800, + "type": { + "id": 6, + "name": "досуг", + "alias": "entertain", + "isFilm": false, + "isSerial": false, + "isForChildren": false + }, + "title": "ПОДКАСТ.ЛАБ", + "transliteratedTitle": "podkastlab-6719800", + "description": "Впереди вся ночь и есть о чем поговорить. Фильмы, музыка, любовь, звезды, еда, мода, анекдоты, спорт, деньги, настоящее, будущее - все это в творческом эксперименте.\nЛариса Гузеева читает любовные письма. Леонид Якубович рассказывает, кого не берут в пилоты. Арина Холина - какой секс способен довести до мужа или до развода. Валерий Сюткин на ходу сочиняет песню для Карины Кросс и Вали Карнавал. Дмитрий Дибров дарит новую жизнь любимой \"Антропологии\". Денис Казанский - все о футболе, хоккее и не только.\n\"ПОДКАСТЫ. ЛАБ\" - серия подкастов разной тематики, которые невозможно проспать. Интеллектуальные дискуссии после полуночи с самыми компетентными экспертами и актуальными спикерами.", + "year": 2023, + "countries": [ + "Россия" + ], + "images": [ + { + "original": { + "src": "//avatars.mds.yandex.net/get-tv-shows/55890/2a000001855e1ed32c3da7ce9494f185a8ac/orig", + "original": true + }, + "sizes": { + "29": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/40x22" + }, + "80": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/alice_80_80" + }, + "297": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/335x223" + }, + "300": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/300x225" + }, + "312": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/375x234" + }, + "426": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/alice_426_240" + }, + "447": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/502x335" + }, + "640": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/640x480" + }, + "960": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/1280x720" + }, + "1440": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/1920x1080" + } + }, + "originalSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/55890/2a000001855e1ed32c3da7ce9494f185a8ac/orig", + "original": true + }, + "maxSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75/1920x1080" + } + }, + { + "original": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e3724b48661908410321ff/orig", + "original": true + }, + "sizes": { + "29": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/40x22" + }, + "80": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/alice_80_80" + }, + "297": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/335x223" + }, + "300": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/300x225" + }, + "312": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/375x234" + }, + "426": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/alice_426_240" + }, + "447": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/502x335" + }, + "640": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/640x480" + }, + "960": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/1280x720" + }, + "1440": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/1920x1080" + } + }, + "originalSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e3724b48661908410321ff/orig", + "original": true + }, + "maxSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/50973/2a00000185c4d7e30a08109cbc342ad18fde/1920x1080" + } + }, + { + "original": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a0000018664020a81fce5ca527c59ea04ea/orig", + "original": true + }, + "sizes": { + "29": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/40x22" + }, + "80": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/alice_80_80" + }, + "297": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/335x223" + }, + "300": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/300x225" + }, + "312": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/375x234" + }, + "426": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/alice_426_240" + }, + "447": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/502x335" + }, + "640": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/640x480" + }, + "960": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/1280x720" + }, + "1440": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/1920x1080" + } + }, + "originalSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a0000018664020a81fce5ca527c59ea04ea/orig", + "original": true + }, + "maxSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/28886/2a00000186640209f0d50440ea234752e490/1920x1080" + } + }, + { + "original": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147715147a9a8cf1dcc9f926a0/orig", + "original": true + }, + "sizes": { + "29": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/40x22" + }, + "80": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/alice_80_80" + }, + "297": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/335x223" + }, + "300": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/300x225" + }, + "312": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/375x234" + }, + "426": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/alice_426_240" + }, + "447": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/502x335" + }, + "640": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/640x480" + }, + "960": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/1280x720" + }, + "1440": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/1920x1080" + } + }, + "originalSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147715147a9a8cf1dcc9f926a0/orig", + "original": true + }, + "maxSize": { + "src": "//avatars.mds.yandex.net/get-tv-shows/70674/2a00000187147714ac0572a0371774e388ef/1920x1080" + } + } + ], + "mainImageBaseUrl": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75", + "persons": [ + { + "name": "Дмитрий Бак", + "role": "presenter" + }, + { + "name": "Илья Кривицкий", + "role": "producer" + } + ], + "ageRestriction": 16, + "favourite": false, + "tags": [], + "displayIfNoEvents": false, + "duplicateIds": [ + 6719800 + ], + "url": "/program/podkastlab-6719800" + }, + "start": "2023-11-26T08:35:00+07:00", + "finish": "2023-11-26T09:10:00+07:00", + "hasSubtitles": false, + "yacFamilyId": 0, + "title": "ПОДКАСТ.ЛАБ. Мелодии моей жизни", + "programTitle": "ПОДКАСТ.ЛАБ. ", + "episodeTitle": "Мелодии моей жизни", + "seasonTitle": "", + "url": "/program/podkastlab-6719800?eventId=217749657", + "hasDescription": true, + "hasReminder": false, + "hasReminderButton": true, + "startTime": "08:35", + "hasStarted": false, + "isNow": false, + "hasFinished": false, + "progress": 0, + "humanDate": "26 ноября, воскресенье, 08:35 — 09:10" +} \ No newline at end of file diff --git a/sites/tv.yandex.ru/__data__/schedule.json b/sites/tv.yandex.ru/__data__/schedule.json new file mode 100644 index 00000000..b9e79cf1 --- /dev/null +++ b/sites/tv.yandex.ru/__data__/schedule.json @@ -0,0 +1,274 @@ +{ + "schedule": { + "isMain": false, + "isMy": false, + "isAll": true, + "selectedDate": "2023-11-26", + "selectedPeriod": "all-day", + "selectedChannelGenre": "all", + "programTypes": [ + { + "isFilm": false, + "isSerial": false, + "isForChildren": false, + "name": "Все жанры" + }, + { + "id": 5, + "name": "Фильмы", + "alias": "films", + "isFilm": true, + "isSerial": false, + "isForChildren": false + }, + { + "id": 4, + "name": "Сериалы", + "alias": "series", + "isFilm": false, + "isSerial": true, + "isForChildren": false + }, + { + "id": 7, + "name": "Спорт", + "alias": "sport", + "isFilm": false, + "isSerial": false, + "isForChildren": false + }, + { + "id": 3, + "name": "Детям", + "alias": "for-children", + "isFilm": false, + "isSerial": false, + "isForChildren": true + } + ], + "channelGenres": [ + { + "name": "Основные", + "alias": "main" + }, + { + "name": "Избранные", + "alias": "my" + }, + { + "id": 27, + "name": "Фильмы и сериалы", + "alias": "movies" + }, + { + "id": 21, + "name": "Познавательные", + "alias": "discovery" + }, + { + "id": 24, + "name": "Спорт", + "alias": "sport" + }, + { + "id": 28, + "name": "Детям", + "alias": "kids" + }, + { + "id": 22, + "name": "Информация", + "alias": "info" + }, + { + "id": 23, + "name": "Музыка", + "alias": "music" + }, + { + "id": 29, + "name": "Увлечения", + "alias": "hobbies" + }, + { + "id": 32, + "name": "Развлечения", + "alias": "entertain" + }, + { + "id": 30, + "name": "HD", + "alias": "hd" + }, + { + "id": 26, + "name": "Другое", + "alias": "other" + }, + { + "name": "Все каналы", + "alias": "all" + } + ], + "selectedProgramTypes": [], + "availableProgramTypes": [ + null, + "for-children", + "info", + null, + "series", + "sport", + "entertain", + "films", + "discover" + ], + "schedules": [ + { + "finish": "2023-12-04T08:00:00+07:00", + "events": [ + { + "id": 217749657, + "channelId": 142, + "channelFamilyId": 16, + "live": false, + "episode": { + "id": 6773710, + "title": "Мелодии моей жизни" + }, + "program": { + "trailers": [], + "onlines": [], + "id": 6719800, + "type": { + "id": 6, + "name": "досуг", + "alias": "entertain", + "isFilm": false, + "isSerial": false, + "isForChildren": false + }, + "title": "ПОДКАСТ.ЛАБ", + "transliteratedTitle": "podkastlab-6719800", + "mainImageBaseUrl": "//avatars.mds.yandex.net/get-tv-shows/33373/2a000001855e1ed2ca7953c3f757c279fc75", + "favourite": false, + "tags": [], + "displayIfNoEvents": false, + "duplicateIds": [ + 6719800 + ], + "url": "/program/podkastlab-6719800", + "images": [] + }, + "start": "2023-11-26T08:35:00+07:00", + "finish": "2023-11-26T09:10:00+07:00", + "hasSubtitles": false, + "yacFamilyId": 0, + "title": "ПОДКАСТ.ЛАБ. Мелодии моей жизни", + "programTitle": "ПОДКАСТ.ЛАБ. ", + "episodeTitle": "Мелодии моей жизни", + "seasonTitle": "", + "url": "/program/podkastlab-6719800?eventId=217749657", + "hasDescription": false, + "hasReminder": false, + "hasReminderButton": true, + "startTime": "08:35", + "hasStarted": false, + "isNow": false, + "hasFinished": false, + "progress": 0, + "humanDate": "26 ноября, воскресенье, 08:35 — 09:10" + } + ], + "channel": { + "siteUrl": "http://www.1tv.ru/", + "title": "Первый", + "familyTitle": "Первый", + "transliteratedFamilyTitle": "pervyy-16", + "logo": { + "original": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/28884/2a000001600801bd354ae2476e3c586f5d58/orig", + "original": true + }, + "sizes": { + "38": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/28884/2a000001600801bd354ae2476e3c586f5d58/small" + }, + "64": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/28884/2a000001600801bd354ae2476e3c586f5d58/alice_64_48" + }, + "80": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/55846/2a000001600801be3ac16928934aba46427e/orig" + }, + "114": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/28884/2a000001600801bd354ae2476e3c586f5d58/114x80" + }, + "160": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/69315/2a000001600801c33618e8beca2d1d6654db/orig" + } + }, + "originalSize": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/28884/2a000001600801bd354ae2476e3c586f5d58/orig", + "original": true + }, + "maxSize": { + "src": "//avatars.mds.yandex.net/get-tv-channel-logos/69315/2a000001600801c33618e8beca2d1d6654db/orig" + } + }, + "synonyms": [ + "1 tv", + "1tv", + "орт", + "первый", + "Первый канал", + "1 канал" + ], + "familyId": 16, + "id": 142, + "genres": [ + { + "id": 26, + "name": "другое", + "alias": "other" + } + ], + "type": "regional", + "url": "/channel/pervyy-16?date=2023-11-26", + "isFavorite": false, + "hasBroadcasting": false, + "broadcastingUrl": "", + "hasBroadcastingPlayer": false, + "broadcastingPlayerUrl": "" + }, + "hasFinished": false + } + ], + "scheduleMap": [ + { + "id": 0, + "limit": 11, + "offset": 0, + "channelIds": [ + 142, + 143, + 422, + 435, + 465, + 166, + 1775, + 1375, + 1046, + 161, + 761 + ] + } + ], + "currentPage": 0, + "providers": [], + "dndEnabled": false, + "totalPages": 34, + "favoriteChannelIds": [], + "hasRecommendedEditorial": false, + "ottSelectionTitle": "Смотрите Кинопоиск и любимые онлайн-шоу на любом телевизоре", + "ottSelectionSubtitle": "" + } +} \ No newline at end of file diff --git a/sites/tv.yandex.ru/__data__/schedule0.json b/sites/tv.yandex.ru/__data__/schedule0.json new file mode 100644 index 00000000..7aa26f08 --- /dev/null +++ b/sites/tv.yandex.ru/__data__/schedule0.json @@ -0,0 +1,4 @@ +{ + "currentPage": 1, + "schedules": [] +} \ No newline at end of file diff --git a/sites/tv.yandex.ru/readme.md b/sites/tv.yandex.ru/readme.md new file mode 100644 index 00000000..b3ce9808 --- /dev/null +++ b/sites/tv.yandex.ru/readme.md @@ -0,0 +1,25 @@ +# tv.yandex.ru + +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. + +### Download the guide + +```sh +npm run grab -- --site=tv.yandex.ru +``` + +### Update channel list + +```sh +npm run channels:parse -- --config=sites/tv.yandex.ru/tv.yandex.ru.config.js --output=sites/tv.yandex.ru/tv.yandex.ru.channels.xml +``` + +### Test + +```sh +npm test -- tv.yandex.ru +``` diff --git a/sites/tv.yandex.ru/tv.yandex.ru.channels.xml b/sites/tv.yandex.ru/tv.yandex.ru.channels.xml index 189ee285..cfa7cddf 100644 --- a/sites/tv.yandex.ru/tv.yandex.ru.channels.xml +++ b/sites/tv.yandex.ru/tv.yandex.ru.channels.xml @@ -1,291 +1,366 @@ - 1HD Music TV - Telekanal 2x2 - 31 Kanal - Telekanal 360° - 365 dnei TV - Tri Angela - 7 TV - 9 Volna - A1 - A2 - Ajara TV - Almaty TV - Amedia Hit - Amedia Premium - Anekdot TV - Animal Planet Rossiya - Arkhyz 24 - Atameken Business - Auto Plus - BBC Entertainment Europe - BBC News - BBC World News Europe - Belarus 24 - BelRos - Bolshaya Aziya - Bloomberg TV Europe - Bober - Boks TV - Bollywood HD - Boomerang Central & Eastern Europe - Bridge TV - Bridge TV Russkiy Hit - Cartoon Network Russia & South East Europe - CBS Reality Europe - 5 Kanal - Perviy kanal - Che! - ChePe Info - Club MTV - CNBC Europe - CNN International Europe - 24 Krim - STS - STS International - STS Love - RGVK Dagestan - Da Vinci - Discovery Channel East - Discovery Channel Rossiya - Discovery Science Rossiya - Kanal Disney - Telekanal Doktor - DocuBox HD - Domashnie Zhivotnye - Domashniy - Dom Kino - Dom Kino Premium - Draiv - DTX Rossiya - DW English - Eda - Eda Premium - English Club TV - E TV - Europa Plus TV - Eurosport 1 Rossiya - Eurosport 2 Rossiya - Evrokino - Extreme Sports Channel - FashionBox HD - Fashion One Europe - FashionTV Russia - Fast & FunBox HD - Feniks plus Kino - FilmBox Arthouse Worldwide - Food Network Russia - Telekanal Futbol - Fox Life Russia - Fox Russia - France 24 English - Pyatnitsa! - Fuel TV - HDL - History 2 Asia - History Russia - HITV - Hollywood HD - Telekanal Nadezhda - Illusion + - Indijskoe Kino - Insight UHD - Istoriya - Izvestia TV - Kaleidoskop TV - Kapitan Fantastika - Karusel - Khabar 24 - KHL - Kinohit - Kinokomedija - Kinomiks - Kinopokaz - Kinopremyera - Kinosemja - Kinoserija - Kinosvidanie - Kino TV - Komediynoe - Konniy Mir - Krik TV - Kto est kto - Kuban 24 Orbita - Kuhnya TV - Kurai TV - KVN TV - La Minor - LDPR TV - Len TV 24 - Luxe TV - Luxury - Lyubimoe - Malysh TV - Mama - Match! - Match! Arena - Match! Boets - Match! Futbol 1 - Match! Futbol 2 - Match! Futbol 3 - Match! Igra - Match! Planeta - MCM Top Russia - Mezzo - Mezzo Live HD - Mir - Mir 24 - Mir Belogorya TV - Mir Seriala - Moskva Telekanal - Moskva 24 - Moya Planeta - MTV Hits Europe - MTV Live HD - MTV Russia - Mult - Museum - Music Box Russia - Muzhskoe Kino - Muzhskoy - Muzika Pervogo - Muz Soyuz - Muz TV - MyZen TV - Nano TV - Nashe Novoe Kino - Nash Kinoroman - National Geographic Russia - National Geographic Wild Russia - Nauka - NHK World Japan - Nickelodeon CIS - Nick Jr CIS - Nika TV - Nostalgia - Noviy Mir - Novoe Radio - NST - NTV - NTV Pravo - NTV Serial - NTV Styl - Telekanal O! - O!2 - Ocean TV - Ohotnik i Rybolov - Okhota i Rybalka - Oruzhie - Ostrosyuzhetnoye - Otkritiy Mir - OTR - Paramount Channel Russia - Paramount Comedy Russia - Pobeda - Poehali! - Priklyucheniya - Prodvizhenie Moskva - Psikhologiya 21 - Radost Moya - Ratnik - RBK TV - Krasnaya Liniya - REN TV - Retro - Rodnoe Kino - RT News - RTД - RT Doc - RTG HD - RTG International - RTG TV - RTR Planeta - Rossiya 1 - Rossiya 24 - Rossiya K - Russkij Extrem - Russkiy Bestseller - Russkiy Detektiv - Russkiy Illusion - Russkiy Roman - Ru TV - Rybolov - Ryzhiy - RZD TV - Sankt Peterburg - Sarafan - Setanta Sports Ukraine - Shanson TV - Shot TV - Sony Channel Russia - Sony Sci-Fi Russia - Sony Turbo - Soyuz - Telekanal Spas - TV Start - Stingray CMusic - Stingray IConcerts - Strana FM TV - T24 - Tayny Galaktiki - TBN Rossiya - TDK - Telecafé - Telekanal Teatr - Teleputeshestviya - TLC Russia - TNT - TNT 4 - TNT International - TNT Music - TNV Planeta - TNV Tatarstan - Tochka TV - Sovershenno Sekretno TV - Travel Channel Europe - Travel + Adventure - TV 1000 Action - TV 1000 East - TV 1000 Russkoe Kino - TV 3 - TV5Monde Europe - TV Centr - TV Guberniya - TVMChannel - Dozhd - TV XXI - U - Udmurtiya - Ugra TV - Usadba - Uspeh - Vetta 24 - VH1 Europe - Viasat Explore Russia - Viasat History - Viasat Nature East - Viasat Sport East - VIP Comedy - VIP Megahit - VIP Premiere - Vmeste RF - V mire zhivotnykh - Voprosy i Otvety - Vremya - World Fashion Channel Russia - Yamal Region - Yurgan - Zagorodnaya Zhizn - Zagorodny - Zdorovoe TV - Zee TV Russia - Zhara TV - Zhar Ptitsa - Zhivaya Planeta - Zhivaya Priroda - Zhivi! - Zoo Park - Zoo TV - Telekanal Zvezda - \ No newline at end of file + + 1 HD Music Television + Три Ангела + 4K Fashion TV + 7 TV + 360° Новости + 360° + 365 дней ТВ + 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 + День Победы + DetectiveJam + Детский мир + Телеканал Совета Федерации "Вместе РФ" + Дайвинг.TV + Доктор + DocuBox HD + Домашние животные + Дом Кино Int + Драйв + DuckTV + Душевное + E TV + EarthTV The World Live + Эхо TV + ЕДА + ЕГЭ + English Class HD + Europa Plus TV + Еврокино + Extreme Sports + FamilyJam + FAN + Fashion Box HD + Fashion & LifeStyle + Fashion & Style 4K + Fashion TV + Fashion TV HD + Fast & Fun Box HD + Феникс+ Кино + FilmBox HD + FilmBox Arthouse + Foodman.club + FoodTime + Футбол + FreshTV + Fuel TV HD + Футбольный + Gagsnetwork + Глазами туриста 4К + Глазами туриста + Gulli Girl + HDL + HISTORY2 + History2 HD + History + Хит HD + Hollywood + Надежда + Хузур ТВ + Иллюзион + + Индия + Индийское кино + Insight Ultra HD + История + Известия + Jibek Joly + Joy Cook + Калейдоскоп ТВ + Камеди + Капитан Фантастика + Кавказ 24 + KHL + KHL Prime + Хоккейный + KidsTV + Кинеко + Кинохит + 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 + МУЛЬТ + Мультиландия + Мультимузыка + Museum TV + Music Box Gold + Music Box Russia + Мужское кино + Мужское кино HD + Мужской + Мужской + МУЗ-ТВ + Музыка Первого + myZen.tv + Нано + Наше HD + Наше Мужское HD + Наше Новое Кино + Наш Кинопоказ HD + Наука + Nickelodeon HD + Nick Jr + Ностальгия + Новое радио + Новый мир + НТВ Хит + НТВ Право + НТВ-Мир + НТВ Сериал + НТВ Стиль + О2ТВ + О! + Океан HD + Охотник и Рыболов HD + Охотник и Рыболов Int + Охота и рыбалка + О, Кино! + Оружие + Остросюжетное HD + Открытый мир + ОТВ + Первый Космический + Первый вегетарианский + Пёс и Ко + Plan B + ПОБЕДА + Поехали! + Премиальное + Приключения HD + Про Бизнес + Про Любовь + Просвещение + Психология 21 + Q Sport + Радость Моя + Ратник + РБК + Красная линия + Ретро ТВ + Родное кино + RT + RTД + RT Doc + RTG International + RTVI + Россия 1 HD + Russian Extreme + Русский Экстрим + Russian Extreme Ultra HD + Russian Travel Guide + Russian Travel Guide HD + Русский бестселлер + Русский Детектив + Русский Иллюзион + Русский роман + RU TV + Рыжий + РЖД + Самара 24 + Сапфир + Сарафан + Шансон-TB + SHANT Premium + Шокирующее + Shot TV + Смайлик ТВ + SongTV Armenia + SONGTV Georgia + SONGTV Russia + .Red + Sony ТВ HD + .Sci-Fi + .Black + Советское кино + Союз + Спас ТВ + Спортивный + Старт + START Air + Старт Триумф + START World + Stingray iConcerts + Страна FM + СТС International + СТС Kids HD + СТС Love + Sumiko + СуперГерои + Тайна + Тайны Галактики + ТБН + Театр + Телекафе + Телепутешествия + TERRA + TiJi + Timeless Dizi Channel + ТНТ International + ТНТ Music + ТНВ + ТНВ-планета + Точка ТВ + Тонус + Совершенно секретно + Travel+Adventure + Travel+Adventure HD + TV BRICS + ТВ Центр + ТВ Центр-International + TVMChannel + ТВТУР + ТВ21 + Ultra HD Cinema + Уникум + Неизвестная Планета + Неизвестная Россия + Усадьба + Успех + В гостях у сказки + MTV 80s + 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 + Загородная жизнь + Загородная жизнь HD + Загородный + Загородный 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 ac42f0cd..bb65e296 100644 --- a/sites/tv.yandex.ru/tv.yandex.ru.config.js +++ b/sites/tv.yandex.ru/tv.yandex.ru.config.js @@ -1,48 +1,253 @@ const dayjs = require('dayjs') +const debug = require('debug')('site:tv.yandex.ru') + +// enable to fetch guide description but its take a longer time +const detailedGuide = true + +// 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 +} +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', +} +const caches = {} module.exports = { site: 'tv.yandex.ru', days: 2, - url: function ({ date, channel }) { - const [region, id] = channel.site_id.split('#') - - return `https://tv.yandex.ru/${region}/channel/${id}?date=${date.format('YYYY-MM-DD')}` + url({ date }) { + return getUrl(date) }, request: { - headers: { - Cookie: - 'yandexuid=8747786251615498142; Expires=Tue, 11 Mar 2031 21:29:02 GMT; Domain=yandex.ru; Path=/' - } + cache: { + ttl: 3600000 // 1 hour + }, + headers: getHeaders() }, - parser: function ({ content }) { + async parser({ content, date, channel }) { const programs = [] - const items = parseItems(content) - items.forEach(item => { - programs.push({ - title: item.title, - description: item.program.description, - category: item.program.type.name, - start: dayjs(item.start), - stop: dayjs(item.finish) + const events = [] + + if (content && parseContent(content, date, true)) { + const cacheid = date.format('YYYY-MM-DD') + if (!caches[cacheid]) { + debug(`Please wait while fetching schedules for ${cacheid}`) + caches[cacheid] = await fetchSchedules({ date, content }) + } + if (detailedGuide) { + await fetchPrograms({ schedules: caches[cacheid], date, channel }) + } + caches[cacheid].forEach(schedule => { + schedule.events + .filter(event => event.channelFamilyId == channel.site_id && date.isSame(event.start, 'day')) + .forEach(event => { + if (events.indexOf(event.id) < 0) { + events.push(event.id) + programs.push({ + title: event.title, + description: event.program.description, + category: event.program.type.name, + start: dayjs(event.start), + stop: dayjs(event.finish) + }) + } + }) }) - }) + } return programs + }, + async channels() { + const channels = [] + const included = [] + const schedules = await fetchSchedules({ date: dayjs() }) + schedules.forEach(schedule => { + if (schedule.channel && included.indexOf(schedule.channel.familyId) < 0) { + included.push(schedule.channel.familyId) + channels.push({ + lang: 'ru', + site_id: schedule.channel.familyId.toString(), + name: schedule.channel.title + }) + } + }) + + return channels } } -function parseContent(content) { - const [, initialState] = content.match(/window.__INITIAL_STATE__ = (.*);/i) || [null, null] - if (!initialState) return null - const data = JSON.parse(initialState) - if (!data) return null +async function fetchSchedules({ date, content = null }) { + const schedules = [] + const queues = [] + const url = getUrl(date) - return data.channel + let mainApi + // parse content as schedules and add to queue if more requests is needed + const f = data => { + 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(...q) + schedules.push(...s) + } + // is main html already fetched? + if (content) { + f(content) + } else { + queues.push(url) + } + // fetch all queues + await doFetch(queues, url, f) + + return schedules } -function parseItems(content) { - const data = parseContent(content) - if (!data || !data.schedule || !Array.isArray(data.schedule.events)) return [] - - return data.schedule.events +async function fetchPrograms({ schedules, date, channel }) { + const queues = [] + schedules + .filter(schedule => schedule.channel.familyId == channel.site_id) + .forEach(schedule => { + 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=`) + ) + }) + await doFetch(queues, getUrl(date), content => { + // is it a program? + if (content?.program) { + let updated = false + schedules.forEach(schedule => { + schedule.events.forEach(event => { + if (event.channelFamilyId === content.channelFamilyId && event.id === content.id) { + Object.assign(event, content) + updated = true + return true + } + }) + if (updated) { + return true + } + }) + } + }) } + +async function doFetch(queues, referer, cb) { + const axios = require('axios') + while (true) { + if (!queues.length) { + break + } + 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) + } +} + +function parseContent(content, date, checkOnly = false) { + const queues = [] + const schedules = [] + let valid = false + if (content) { + if (Buffer.isBuffer(content)) { + content = content.toString() + } + // got captcha, its look like our cookies has expired + if (content?.type === 'captcha' || (typeof content === 'string' && content.match(/SmartCaptcha/))) { + throw new Error('Got captcha, please goto https://tv.yandex.ru and update cookies!') + } + if (typeof content === 'object') { + let items + 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}`)) + } + // find some schedules? + if (Array.isArray(content.schedule.schedules)) { + items = content.schedule.schedules + } + } + // find another schedules? + if (Array.isArray(content.schedules)) { + items = content.schedules + } + // add programs + if (items && items.length) { + schedules.push(...getSchedules(items)) + } + } else { + // prepare headers for next http request + const [, region] = content.match(/region: '(\d+)'/i) || [null, null] + const [, initialSk] = content.match(/window.__INITIAL_SK__ = (.*);/i) || [null, null] + const [, sessionId] = content.match(/window.__USER_SESSION_ID__ = "(.*)";/i) || [null, null] + const tvSk = initialSk ? JSON.parse(initialSk) : {} + if (region) { + caches.region = region + } + if (tvSk.key) { + headers['X-Tv-Sk'] = tvSk.key + } + if (sessionId) { + headers['X-User-Session-Id'] = sessionId + } + if (checkOnly && region && tvSk.key && sessionId) { + valid = true; + } + } + } + + return checkOnly ? valid : [queues, schedules] +} + +function parseCookies(headers) { + if (Array.isArray(headers['set-cookie'])) { + headers['set-cookie'] + .forEach(cookie => { + const [key, value] = cookie.split('; ')[0].split('=') + if (cookies[key] !== value) { + cookies[key] = value + debug(`Update cookie ${key}=${value}`) + } + }) + } +} + +function getSchedules(schedules) { + return schedules.filter(schedule => schedule.events.length); +} + +function getHeaders(data = {}) { + return Object.assign({}, headers, { + '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` +} \ 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 d1a65bc0..5bab5f01 100644 --- a/sites/tv.yandex.ru/tv.yandex.ru.test.js +++ b/sites/tv.yandex.ru/tv.yandex.ru.test.js @@ -1,35 +1,71 @@ -// npm run grab -- --site=tv.yandex.ru - const { parser, url, request } = require('./tv.yandex.ru.config.js') +const fs = require('fs') +const path = require('path') +const axios = require('axios') const dayjs = require('dayjs') const utc = require('dayjs/plugin/utc') const customParseFormat = require('dayjs/plugin/customParseFormat') + dayjs.extend(customParseFormat) dayjs.extend(utc) -const date = dayjs.utc('2021-11-25', 'YYYY-MM-DD').startOf('d') +jest.mock('axios') + +const date = dayjs.utc('2023-11-26').startOf('d') const channel = { - site_id: '162#31-kanal-429', - xmltv_id: '31Kanal.kz' + site_id: '16', + xmltv_id: 'ChannelOne.ru' } -const content = - ' ' +axios.get.mockImplementation((url, opts) => { + if (url === 'https://tv.yandex.ru/?date=2023-11-26&grid=all&period=all-day') { + return Promise.resolve({ + headers: {}, + data: fs.readFileSync(path.resolve(__dirname, '__data__/content.html')) + }) + } + if (url === 'https://tv.yandex.ru/api/120809?date=2023-11-26&grid=all&period=all-day') { + return Promise.resolve({ + headers: {}, + data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/schedule.json'))) + }) + } + if (url === 'https://tv.yandex.ru/api/120809/main/chunk?page=0&date=2023-11-26&period=all-day&offset=0&limit=11') { + return Promise.resolve({ + headers: {}, + data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/schedule0.json'))) + }) + } + if (url === 'https://tv.yandex.ru/api/120809/event?eventId=217749657&programCoId=') { + return Promise.resolve({ + headers: {}, + data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program.json'))) + }) + } +}) it('can generate valid url', () => { - expect(url({ channel, date })).toBe( - 'https://tv.yandex.ru/162/channel/31-kanal-429?date=2021-11-25' + expect(url({ date })).toBe( + 'https://tv.yandex.ru/?date=2023-11-26&grid=all&period=all-day' ) }) it('can generate valid request headers', () => { expect(request.headers).toMatchObject({ Cookie: - 'yandexuid=8747786251615498142; Expires=Tue, 11 Mar 2031 21:29:02 GMT; Domain=yandex.ru; Path=/' + 'cycada=3w11iWu+2+o6iIIiI/S1/k9lFIb6y+G6SW6hsbLoPJg=; ' + + 'i=0nUBW1d6GpFmpLRIuHYGulEA4alIC2j4WS+WYGcusydL7lcrG9loWX8qrFEBOqg54KZxGwCVaZhZ1THYgoIo0T69iCY=; ' + + 'spravka=dD0xNzAxMjI3MTk1O2k9MzYuODQuOTguMTcxO0Q9Njk4NDQwRkRDODk5QUEzMDJCNzI5NTJBMTM4RTY2ODNEMzQyNkM1MjI5QTkyNDI3NUJGMzMzQUJEMUZFQjMyQzczM0I2QzE0QTRDQkJFODY5Nzk0MjhGNkEzQjQ5NDJBMzcxQzIzMjE3RTRENkVDOUU1NEE1RDVFNDg0RUQ1RTI3OUNGNzlCMEYzNzUyMDcyNDhGQkVCNkIyMDg5NTMwMzc1QkZEQTlGNEU7dT0xNzAxMjI3MTk1NDg5NDIyODkzO2g9OTRmN2FiNTMxZmJjNDg5MjM4ZDk4Y2ZkN2E0ZmY0YmI=; ' + + 'yandexuid=7536067781700842414; ' + + 'yashr=7271154091700842416; ' + + 'user_display=696' }) }) -it('can parse response', () => { - const result = parser({ content }).map(p => { +it('can parse response', async () => { + const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html')) + const result = ( + await parser({ content, date, channel }) + ).map(p => { p.start = p.start.toJSON() p.stop = p.stop.toJSON() return p @@ -37,17 +73,17 @@ it('can parse response', () => { expect(result).toMatchObject([ { - start: '2021-11-24T23:00:00.000Z', - stop: '2021-11-24T23:58:00.000Z', - title: 'Ризамын (каз.).', + start: '2023-11-26T01:35:00.000Z', + stop: '2023-11-26T02:10:00.000Z', + title: 'ПОДКАСТ.ЛАБ. Мелодии моей жизни', category: 'досуг', - description: 'kLX6FVKAIiDCGBFE' + description: 'Впереди вся ночь и есть о чем поговорить. Фильмы, музыка, любовь, звезды, еда, мода, анекдоты, спорт, деньги, настоящее, будущее - все это в творческом эксперименте.\nЛариса Гузеева читает любовные письма. Леонид Якубович рассказывает, кого не берут в пилоты. Арина Холина - какой секс способен довести до мужа или до развода. Валерий Сюткин на ходу сочиняет песню для Карины Кросс и Вали Карнавал. Дмитрий Дибров дарит новую жизнь любимой \"Антропологии\". Денис Казанский - все о футболе, хоккее и не только.\n\"ПОДКАСТЫ. ЛАБ\" - серия подкастов разной тематики, которые невозможно проспать. Интеллектуальные дискуссии после полуночи с самыми компетентными экспертами и актуальными спикерами.' } ]) }) -it('can handle empty guide', () => { - const result = parser({ +it('can handle empty guide', async () => { + const result = await parser({ date, channel, content: ''