Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting 403s and 404s on tvtvus and movistarplus #2466

Open
Mauricio300808 opened this issue Nov 19, 2024 · 17 comments · May be fixed by #2468
Open

Getting 403s and 404s on tvtvus and movistarplus #2466

Mauricio300808 opened this issue Nov 19, 2024 · 17 comments · May be fixed by #2468
Labels
broken guide Error in the EPG

Comments

@Mauricio300808
Copy link

Site

tvtv.us and movistarplus.es

Description

Getting 403s and 404s on tvtvus and movistarplus

@Mauricio300808 Mauricio300808 added the broken guide Error in the EPG label Nov 19, 2024
@Guito-del
Copy link

Hi, movistarplus.es is broken. Any update is available ?? Thx

@nickleby
Copy link
Contributor

tvtv.us seems to only need headers to avoid the 403, movistarplus changed the epg schedule website and now the structure is very similar to the andorradifusio.ad site, maybe @freearhey can take a look at it and adapt the andorradifusio.ad to movistarplus.es :)

@davidclaeysquinones
Copy link
Contributor

I've been investigating the issue for movistar plus and they recently changed their api.
UP until this point I haven't found a way to fix the issue.

@davidclaeysquinones
Copy link
Contributor

look at #2440

@fraudiay79
Copy link

I'm currently working on an alternative to get the guide from orangetv.orange.es (https://orangetv.orange.es/epg), but with my limited knowledge in js scripting I have run into a stumbling block. I'm able to parse the channels correctly, but the epg content returns 0 programs. Can someone here take a look and correct it; then share with this repo? Thank you.

const dayjs = require('dayjs')

module.exports = {
  site: 'orangetv.es',
  days: 2,
  request: {
    cache: {
      ttl: 60 * 60 * 1000 // 1 hour
    }
  },
  url({ date }) {
    return `https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/${date.format('YYYYMMDD')}_8h_1.json`
  },
  parser: function ({ content }) {
    let programs = []
    const items = parseItems(content)
    items.forEach(item => {
      programs.push({
        title: item.programs.name,
        description: item.programs.description,
        season: item.programs.seriesSeason || null,
        episode: item.programs.episodeId || null,
        start: parseStart(item),
        stop: parseStop(item)
      })
    })

    return programs
  },
  async channels() {
    const axios = require('axios')
    const data = await axios
      .get(`https://pc.orangetv.orange.es/pc/api/rtv/v1/GetChannelList?bouquet_id=1&model_external_id=PC&filter_unsupported_channels=false&client=json`)
      .then(r => r.data)
      .catch(console.log)
    return data.response.map(item => {
      return {
        lang: 'es',
	name: item.name,
        site_id: item.externalChannelId
      }
    })
  }
}

function parseStart(item) {
  if (!item.programs || !item.programs.startDate) return null

  return item.startDate ? dayjs.unix(item.programs.startDate) : null
}

function parseStop(item) {
  if (!item.programs || !item.programs.endDate) return null

  return item.endDate ? dayjs.unix(item.programs.endDate) : null
}

function parseItems(content) {
  const data = JSON.parse(content)
  if (!data || !data.programs) return []

  return data.programs
}

@davidclaeysquinones
Copy link
Contributor

The PR is ready and modified, so not necessarily needed to make code for a new provider.

I'll indulge myself and point out the existence of https://github.com/davidclaeysquinones/epg-info-docker.
With the custom fixes enabled all should work again.

@fraudiay79
Copy link

Awesome. Would you have time to take a look at my file as well as a backup source?

@davidclaeysquinones
Copy link
Contributor

I'm more than happy to look at it over the weekend. Another provider certainly can't hurt.
Just in order to set my expectations, have you already tested your code ?

@Mauricio300808
Copy link
Author

Nice!! I also pushed a change to add headers to tvtv.us and seems to be working fine. Thank you guys

@fraudiay79
Copy link

Thank you. I ran separate tests for retrieving the channels list and the epg. When I tested the generating the channels it was successful. When I try to grab the epg content it retrieves 0 programs for each channel, and no error is displayed

@Mauricio300808
Copy link
Author

Mauricio300808 commented Nov 22, 2024

@davidclaeysquinones i tried the code in your PR but still getting 0 channels from movistar :/ I used this code in the config file


`const { DateTime } = require('luxon');

const API_CHANNEL_ENDPOINT = 'https://www.movistarplus.es/programacion-tv';
const API_PROGRAM_ENDPOINT = 'https://comunicacion.movistarplus.es';
const API_IMAGE_ENDPOINT = 'https://www.movistarplus.es/recorte/n/caratulaH/';

module.exports = {
  site: 'movistarplus.es',
  days: 2,
  url: function ({ date, channel }) {
    if (channel) {
      return `${API_PROGRAM_ENDPOINT}/wp-admin/admin-ajax.php`;
    }
    return `https://www.movistarplus.es/programacion-tv/${date.format('YYYY-MM-DD')}?v=json`;
  },
  request: {
    method: 'POST',
    headers: {
      Origin: API_PROGRAM_ENDPOINT,
      Referer: `${API_PROGRAM_ENDPOINT}/programacion/`,
      "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8',
    },
    data: function ({ channel, date }) {
      return {
        action: 'getProgramation',
        day: date.format('YYYY-MM-DD'),
        "channels[]": channel.site_id,
      };
    },
  },
  parser({ content, channel, date }) {
    let programs = [];
    let items = parseItems(content, channel);
    if (!items.length) return programs;

    items.forEach((item) => {
      let startTime = DateTime.fromFormat(
        `${date.format('YYYY-MM-DD')} ${item.HORA_INICIO}`,
        'yyyy-MM-dd HH:mm',
        {
          zone: 'Europe/Madrid',
        }
      ).toUTC();

      let stopTime = DateTime.fromFormat(
        `${date.format('YYYY-MM-DD')} ${item.HORA_FIN}`,
        'yyyy-MM-dd HH:mm',
        {
          zone: 'Europe/Madrid',
        }
      ).toUTC();

      if (stopTime < startTime) {
        stopTime = stopTime.plus({ days: 1 });
      }

      programs.push({
        title: item.des_evento_rejilla,
        category: item.des_genero,
        icon: parseIcon(item, channel),
        start: startTime,
        stop: stopTime,
      });
    });

    return programs;
  },
};

function parseIcon(item, channel) {
  return `${API_IMAGE_ENDPOINT}/M${channel.site_id}P${item.ELEMENTO}`;
}

function parseItems(content, channel) {
  const json = typeof content === 'string' ? JSON.parse(content) : content;
  const data = json.channelsProgram;

  if (!data || data.length !== 1) return [];
  return data[0].PROGRAMAS || [];
}
`


@davidclaeysquinones
Copy link
Contributor

I hope they haven't changed the API again
When I tested it worked.

@Mauricio300808
Copy link
Author

@davidclaeysquinones this code works for me:

const { DateTime } = require('luxon');
const axios = require('axios');

const API_PROGRAM_ENDPOINT = 'https://comunicacion.movistarplus.es';
const API_IMAGE_ENDPOINT = 'https://www.movistarplus.es/recorte/n/caratulaH/'; // Define the image endpoint

module.exports = {
  site: 'movistarplus.es',
  days: 2,
  url: function ({ channel, date }) {
    const luxonDate = DateTime.fromJSDate(new Date(date)); // Ensure `date` is converted
    return `${API_PROGRAM_ENDPOINT}/wp-admin/admin-ajax.php`;
  },
  request: {
    method: 'POST', // Try GET if POST fails
    headers: {
      Origin: API_PROGRAM_ENDPOINT,
      Referer: `${API_PROGRAM_ENDPOINT}/programacion/`,
      "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8'
    },
    data: function ({ channel, date }) {
      const luxonDate = DateTime.fromJSDate(new Date(date)); // Ensure `date` is converted
      return {
        action: 'getProgramation',
        day: luxonDate.toFormat('yyyy-MM-dd'),
        "channels[]": channel.site_id
      };
    }
  },
  async fetchData({ channel, date }) {
    const luxonDate = DateTime.fromJSDate(new Date(date)); // Ensure `date` is converted
    const payload = {
      action: 'getProgramation',
      day: luxonDate.toFormat('yyyy-MM-dd'),
      "channels[]": channel.site_id
    };

    try {
      console.log('Request Payload:', payload);

      const response = await axios({
        method: this.request.method,
        url: this.url({ channel, date }),
        headers: this.request.headers,
        data: new URLSearchParams(payload).toString() // For POST
      });

      console.log('Response Status:', response.status);
      console.log('Response Data:', response.data);

      return this.parser({ content: response.data, channel, date });
    } catch (error) {
      console.error('Error Status:', error.response?.status);
      console.error('Error Data:', error.response?.data);

      // If 405, retry with GET method
      if (error.response?.status === 405 && this.request.method === 'POST') {
        console.log('Retrying with GET...');
        return this.retryWithGet({ channel, date });
      }

      return null; // Handle the error gracefully
    }
  },
  async retryWithGet({ channel, date }) {
    const luxonDate = DateTime.fromJSDate(new Date(date));
    const query = new URLSearchParams({
      action: 'getProgramation',
      day: luxonDate.toFormat('yyyy-MM-dd'),
      "channels[]": channel.site_id
    }).toString();

    const url = `${this.url({ channel, date })}?${query}`;
    try {
      const response = await axios.get(url, { headers: this.request.headers });
      console.log('GET Response Status:', response.status);
      console.log('GET Response Data:', response.data);
      return this.parser({ content: response.data, channel, date });
    } catch (error) {
      console.error('GET Error Status:', error.response?.status);
      console.error('GET Error Data:', error.response?.data);
      return null;
    }
  },
  parser({ content, channel, date }) {
    const json = typeof content === 'string' ? JSON.parse(content) : content;

    // Ensure proper parsing of the data
    if (!json || !json.channelsProgram || json.channelsProgram.length === 0) {
      console.error('No programs found in the response.');
      return [];
    }

    const programs = json.channelsProgram[0]; // Assuming first array contains program data

    // Map over the programs and parse relevant data
    return programs.map((item) => {
      const startTime = DateTime.fromFormat(
        `${date} ${item.f_evento_rejilla}`,
        'yyyy-MM-dd HH:mm:ss',
        { zone: 'Europe/Madrid' }
      ).toUTC();

      const stopTime = DateTime.fromFormat(
        `${date} ${item.f_fin_evento_rejilla}`,
        'yyyy-MM-dd HH:mm:ss',
        { zone: 'Europe/Madrid' }
      ).toUTC();

      return {
        title: item.des_evento_rejilla,
        icon: this.parseIcon(item), // Use the `parseIcon` function
        category: item.des_genero,
        start: startTime,
        stop: stopTime
      };
    });
  },
  parseIcon(item) {
    // Construct the icon URL
    return `${API_IMAGE_ENDPOINT}${item.cod_evento_rejilla}.jpg`; // Ensure it's referencing the correct image
  }
};

@davidclaeysquinones
Copy link
Contributor

Did you get the images to work properly?

@Guito-del
Copy link

Guito-del commented Nov 22, 2024

This Code worked for me too. THX all

@Mauricio300808
Copy link
Author

here is a much cleaner code. I do not see the icons though...

const { DateTime } = require('luxon');

const API_CHANNEL_ENDPOINT = 'https://www.movistarplus.es/programacion-tv';
const API_PROGRAM_ENDPOINT = 'https://comunicacion.movistarplus.es';
const API_IMAGE_ENDPOINT = 'https://www.movistarplus.es/recorte/n/caratulaH/';

module.exports = {
  site: 'movistarplus.es',
  days: 2,
  url: function ({ channel, date }) {
    return `${API_PROGRAM_ENDPOINT}/wp-admin/admin-ajax.php`;
  },
  request: {
    method: 'POST',
    headers: {
      Origin: API_PROGRAM_ENDPOINT,
      Referer: `${API_PROGRAM_ENDPOINT}/programacion/`,
      "Content-Type" : 'application/x-www-form-urlencoded; charset=UTF-8',
    },
    data: function ({ channel, date }) {
      return {
        action: 'getProgramation',
        day: date.format('YYYY-MM-DD'),
        "channels[]": channel.site_id,
      };
    },
  },
  parser({ content, channel, date }) {
    let programs = [];
    let items = parseItems(content, channel);
    if (!items.length) return programs;

    items.forEach(item => {
      let startTime = DateTime.fromFormat(
        `${item.f_evento_rejilla}`,
        'yyyy-MM-dd HH:mm:ss',
        { zone: 'Europe/Madrid' }
      ).toUTC();

      let stopTime = DateTime.fromFormat(
        `${item.f_fin_evento_rejilla}`,
        'yyyy-MM-dd HH:mm:ss',
        { zone: 'Europe/Madrid' }
      ).toUTC();

      // Adjust stop time if it's on the next day
      if (stopTime < startTime) {
        stopTime = stopTime.plus({ days: 1 });
      }

      programs.push({
        title: item.des_evento_rejilla,
        icon: parseIcon(item, channel),
        category: item.des_genero,
        start: startTime,
        stop: stopTime,
      });
    });

    return programs;
  },
};

function parseIcon(item, channel) {
  return `${API_IMAGE_ENDPOINT}/M${channel.site_id}P${item.ELEMENTO}`;
}

function parseItems(content, channel) {
  const json = typeof content === 'string' ? JSON.parse(content) : content;
  const data = json.channelsProgram;

  if (data.length !== 1) return [];
  return data[0];
}

@davidclaeysquinones
Copy link
Contributor

Well in the first version you used item.cod_evento_rejilla and in the second item.ELEMENTO.
I was having trouble retrieving the program images with the new API version so I was wondering if you figured that out.

@freearhey freearhey linked a pull request Nov 25, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
broken guide Error in the EPG
Development

Successfully merging a pull request may close this issue.

5 participants