LuanRT / YouTube.js

A JavaScript client for YouTube's private API, known as InnerTube.
https://ytjs.dev
MIT License
3.53k stars 224 forks source link

Better error message when using certain APIs with unsupported ClientType #555

Open iBicha opened 10 months ago

iBicha commented 10 months ago

Steps to reproduce

import { writeFileSync } from 'fs';
import { Innertube, ClientType } from 'youtubei.js';

(async () => {
    const yt = await Innertube.create({
        client_type: ClientType.WEB
    });
    const videoInfo = await yt.getInfo('y9n6HkftavM');    

    const manifest = await videoInfo.toDash(undefined, undefined, { include_thumbnails: false });
    writeFileSync('manifest.mpd', manifest);
  })();
node dash.js

This works. Set it to ClientType.ANDROID, ClientType.IOS or ClientType.TV_EMBEDDED then it does not

Failure Logs

InnertubeError: Request to https://www.youtube.com/youtubei/v1/player?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8&prettyPrint=false&alt=json failed with status 404
    at HTTPClient.<anonymous> (file:///Users/brahim/Roku/YouTube.js/dist/src/utils/HTTPClient.js:99:19)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/brahim/Roku/YouTube.js/node_modules/tslib/tslib.js:166:62)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  info: '{\n' +
    '  "error": {\n' +
    '    "code": 404,\n' +
    '    "message": "Requested entity was not found.",\n' +
    '    "errors": [\n' +
    '      {\n' +
    '        "message": "Requested entity was not found.",\n' +
    '        "domain": "global",\n' +
    '        "reason": "notFound"\n' +
    '      }\n' +
    '    ],\n' +
    '    "status": "NOT_FOUND"\n' +
    '  }\n' +
    '}\n',
  date: 2023-12-09T15:50:17.497Z,
  version: '8.0.0'
}

Expected behavior

For different clients to be able to pull video info and generate dash manifest

Current behavior

Only WEB seems to work

Version

Edge

Anything else?

No response

Checklist

absidue commented 10 months ago

@iBicha Use getBasicInfo for those other clients, the android and iOS ones use other endpoints, the embed one understandably doesn't have the extra info, so getInfo will never work for that one.

This is basically a feature request instead of a bug report, because getInfo is currently only written to support the web client.

iBicha commented 10 months ago

@iBicha Use getBasicInfo for those other clients, the android and iOS ones use other endpoints, the embed one understandably doesn't have the extra info, so getInfo will never work for that one.

This is basically a feature request instead of a bug report, because getInfo is currently only written to support the web client.

I tried getBasicInfo instead, I'm still seeing the same error

absidue commented 10 months ago

You aren't passing the client name string to getBasicInfo, it has to match the one that you are creating the session with.

iBicha commented 10 months ago

Aha, thanks

import { writeFileSync } from 'fs';
import { Innertube, ClientType } from 'youtubei.js';

(async () => {
    const yt = await Innertube.create({
        client_type: ClientType.TV_EMBEDDED
    });
    const videoInfo = await yt.getBasicInfo('DcvvWjExea4', 'TV_EMBEDDED');    

    const manifest = await videoInfo.toDash(undefined, undefined, { include_thumbnails: false });
    writeFileSync('manifest.mpd', manifest);
  })();

I guess it got a bit confusing since I wasn't aware that getInfo only works with WEB, and that client types need to match. Additionally ClientType (enum) is passed to the session, while InnerTubeClient (string) is passed to the getBasicInfo function. Maybe it only needs a better error message

PS: I'm ok with leaving the issue open for a better error message or closing

absidue commented 10 months ago

It used to be possible to create a session with one client and then use a different client to get the info (e.g. you need WEB for most things but then want to get the video/streaming information with the android client), which is why that parameter exists and is why you need to specify the client to getBasicInfo, if you want to deviate from the default. Not sure if YouTube still allows you to do that though.

LuanRT commented 10 months ago

Just passing the client name string to the getInfo/getBasicInfo functions will do, no need to change the entire instance's client. I understand it's a tad confusing, we should probably have better documentation for this.

Duell10111 commented 10 months ago

Is it possible to fetch the HomeFeed as TV client to get a different structure to match the Youtube TV apps?

absidue commented 10 months ago

YouTube.js doesn't have support for anything TV related yet. It would also only be possible to use it from node or deno out of the box, because YouTube restricts the TV frontend to specific user-agents.

Duell10111 commented 10 months ago

YouTube.js doesn't have support for anything TV related yet. It would also only be possible to use it from node or deno out of the box, because YouTube restricts the TV frontend to specific user-agents.

Ah okay interesting, it is only a bit misleading then that the ClientType TVEmbedded exists for some reason. 😅

absidue commented 10 months ago

@Duell10111 That only works for getBasicInfo and is the only client that doesn't require you to login for age-restricted videos.

absidue commented 9 months ago

Will likely require throwing errors in most methods if a client isn't the web one. Not great, but at least people will know that they are doing unsupported stuff, instead of being confused by other errors.

github-actions[bot] commented 7 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

iBicha commented 7 months ago

Unstale, I think is still relevant

meghe2000 commented 5 months ago

I still get this error. If I use ClientType.WEB, it doesn't give error.

import { ClientType, Innertube, UniversalCache } from 'youtubei.js';

async function start() {
    const vid = 'ZiplxPdOJd4';
    const yt = await Innertube.create(
        {
            cache: new UniversalCache(true),
            generate_session_locally: true,
            client_type: ClientType.ANDROID
        }
    );

    yt.session.on('auth-pending', (data) => {
        console.log(`Go to ${data.verification_url} in your browser and enter code ${data.user_code} to authenticate.`);
    });

    await yt.session.signIn();
    await yt.session.oauth.cacheCredentials();
    console.log((await yt.getHomeFeed()).contents.contents.length);
}

start();

InnertubeError: Request to https://www.youtube.com/youtubei/v1/browse?prettyPrint=false&alt=json failed with status 404 at HTTPClient. (node_modules/youtubei.js/dist/src/utils/HTTPClient.js:101:19) at Generator.next () at fulfilled (node_modules\tslib\tslib.js:166:62) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { info: '{\n' + ' "error": {\n' + ' "code": 404,\n' + ' "message": "Requested entity was not found.",\n' + ' "status": "NOT_FOUND"\n' + ' }\n' + '}\n', date: 2024-04-23T15:19:03.219Z, version: '9.3.0' }

Node.js v18.16.0

absidue commented 5 months ago

@meghe2000 If it only works with the WEB client, then that's what you should be using. YouTube.js only supports other clients with very specific functions, specifically getBasicInfo and the ones that use the ANDROID ones internally, for everything else you should use the WEB one.

meghe2000 commented 5 months ago

In other clients, the data loading speed is much faster. I have tested this.

And in the loading speed test, I came to this conclusion:

WEB: 2.2000 ms ANDROID | EMBED: 0.6000 ms

For this reason, I prefer to use another client.

absidue commented 5 months ago

@meghe2000 Speed doesn't matter when you are trying to do stuff that YouTube.js doesn't support. Your choices are:

iBicha commented 5 months ago

The discussion on which client to use is not the point. The point is the library should throw "Client X is not supported in this function, please use Y". A clear and concise message is better trying to find an issue on Github (if any) that might or might not have a solution. If only the WEB is supported for creating sessions, then the option should be deprecated to avoid confusion

absidue commented 5 months ago

@iBicha Creating sessions works completely fine, the error is coming from the call to getHomeFeed().