iBicha / yt2alt

A cli tool to import your profile from YouTube to an alternative platform

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TypeError: Cannot read properties of undefined (reading 'text')

thinktapper opened this issue · comments

yt2alt worked successfully as expected on my first run using one of my google accounts. However, I encountered multiple errors when attempting a second time using a different google account.

I tried updating node/npm versions to no avail.

I received this output 5/6 of my troubleshooting attempts:

❯ npx yt2alt
yt2alt v0.5.0

? This tool will log into your YouTube account, read your data, and allow
you to import it to other platforms.
You will get to choose which data to import and where to export it.
Continue? yes

Go to https://www.google.com/device in your browser and enter code {{REDACTED}} to authenticate.
? Copy code to clipboard and open url in the browser now? yes

Reading library...
file:///Users/{{REDACTED}}/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:199
            title: playlist.title.text
                                  ^

TypeError: Cannot read properties of undefined (reading 'text')
    at YouTube.toPlaylist (file:///Users/{{REDACTED}}/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:199:35)
    at file:///Users/{{REDACTED}}/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:128:47
    at Proxy.map (<anonymous>)
    at YouTube.getLibraryPlaylists (file:///Users/{{REDACTED}}/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:128:26)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///Users/{{REDACTED}}/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/cli.js:25:30

Node.js v22.4.1

Interestingly, I received a different error in this output from my 3rd attempt:

❯ npx yt2alt
yt2alt v0.5.0

? This tool will log into your YouTube account, read your data, and allow
you to import it to other platforms.
You will get to choose which data to import and where to export it.
Continue? yes

Go to https://www.google.com/device in your browser and enter code [REDACTED] to authenticate.
? Copy code to clipboard and open url in the browser now? yes

Reading library...
file:///Users/tapper/.npm/_npx/00e04e11bd4e38b3/node_modules/youtubei.js/dist/src/utils/HTTPClient.js:111
            throw new InnertubeError(`Request to ${response.url} failed with status ${response.status}`, yield response.text());
                  ^

InnertubeError: Request to https://www.youtube.com/youtubei/v1/browse?prettyPrint=false&alt=json failed with status 404
    at HTTPClient.<anonymous> (file:///Users/tapper/.npm/_npx/00e04e11bd4e38b3/node_modules/youtubei.js/dist/src/utils/HTTPClient.js:111:19)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/tapper/.npm/_npx/00e04e11bd4e38b3/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: 2024-07-10T17:28:03.995Z,
  version: '10.1.0'
}

Node.js v20.9.0

Please let me know if any other info could be useful.

Edit by @iBicha : removed pairing code in second log

Thanks for reporting this
Starting with the first error

file:///Users/{{REDACTED}}/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:199
            title: playlist.title.text
                                  ^

TypeError: Cannot read properties of undefined (reading 'text')

When you open https://www.youtube.com/feed/playlists, do you see a playlist that is not normal / does not have a title? (perhaps a special type of playlist or something)? I assumed all playlists would have a title

I see two playlists with emojis in the titles - which could be considered abnormal/non-text..

image image

Emojis seem to be captured fine
Screen Shot 2024-07-10 at 5 33 44 PM
I might need to add some sort of flag to dump the request/response payload and get a more clear picture

You can try the new version with npx yt2alt -- --debug, it wouldn't fix it, but maybe there are some logs that could indicate something it going wrong. It's not a full dump of the response, so it might or might not help

Here's most of the output of running npx yt2alt --debug:

Output
? Copy code to clipboard and open url in the browser now? yes
[YOUTUBEJS][OAuth2]: Polling for access token...
[YOUTUBEJS][OAuth2]: Polling for access token...

Reading library...
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Alo Yoga',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 8,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'Alo Yoga',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 16, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Almost Friday TV',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 16,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'Almost Friday TV',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 11, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'CHUG ViSiON',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 17, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Yoga With Adriene',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 17,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'Yoga With Adriene',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 11, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'YMH Studios',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 11,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'YMH Studios',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 7, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Podcast',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 10, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'ClangersTV',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 13, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Splooge Ville',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 15, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Conscious Chefs',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 17, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Yoga With Adriene',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 17,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'Yoga With Adriene',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 4, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'saul',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 17, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Yoga With Adriene',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 17,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'Yoga With Adriene',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 9, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Leon Noel',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 21, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Case Closed TV Warren',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 9, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'BroadCity',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 7, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'VICE TV',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 7,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'VICE TV',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 15, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Leland Chandler',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 11, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Vanity Fair',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: {
    startIndex: 11,
    styleRunExtensions: { styleRunColorMapExtension: [Object] }
  },
  input_data: {
    content: 'Vanity Fair',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object], [Object] ],
    attachmentRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 11, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Medium Zach',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8 },
  input_data: { content: 'Play all', styleRuns: [ [Object] ] }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 6, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Zeke B',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
[YOUTUBEJS][Text]: Skipping style run as it is doesn't have any information that we parse. {
  style_run: { startIndex: 0, length: 8, weightLabel: 'FONT_WEIGHT_NORMAL' },
  input_data: {
    content: 'Playlist',
    commandRuns: [ [Object] ],
    styleRuns: [ [Object] ]
  }
}
file:///Users/tapper/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:207
            title: playlist.title.text
                                  ^

TypeError: Cannot read properties of undefined (reading 'text')
    at YouTube.toPlaylist (file:///Users/tapper/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:207:35)
    at file:///Users/tapper/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:136:47
    at Proxy.map (<anonymous>)
    at YouTube.getLibraryPlaylists (file:///Users/tapper/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/youtube.js:136:26)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///Users/tapper/.npm/_npx/00e04e11bd4e38b3/node_modules/yt2alt/src/cli.js:38:30

Node.js v22.4.1

There was session data printed out before this step that I didn't include due to the potential sensitivity of its content, but let me know if session data/client identity stuff could be useful

@iBicha should I elevate this to youtube.js?
Actually, I went ahead and did some debugging on my own and tracked down the issue 🏋️‍♂️

I have a fix for it too, but wondering if I should submit the fix here or upstream to youtube.js itself?
Edit: nvm the fix is for yt2alt's src/youtube.js not the dependency

This is the playlist that caused the title: playlist.title.text undefined error:

JSON
{
  "type": "LockupView",
  "content_image": {
    "type": "CollectionThumbnailView",
    "primary_thumbnail": {
      "type": "ThumbnailView",
      "image": [
        {
          "url": "https://i.ytimg.com/pl_c/PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD/studio_square_thumbnail.jpg?sqp=CMTh_rQG-oaymwEICNAFENAFSFqi85f_AwYIs7nhoQY=&rs=AOn4CLDgn9wlNpqAc4PhdjDZJpiIRYPvJg",
          "width": 720,
          "height": 720
        },
        {
          "url": "https://i.ytimg.com/pl_c/PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD/studio_square_thumbnail.jpg?sqp=CMrA_rQG-oaymwEICOADEOADSFqi85f_AwYIs7nhoQY=&rs=AOn4CLAglUyrwFfEyRt7YFKbxtBayVqE1A",
          "width": 480,
          "height": 480
        },
        {
          "url": "https://i.ytimg.com/pl_c/PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD/studio_square_thumbnail.jpg?sqp=CJDK_rQG-oaymwEICPABEPABSFqi85f_AwYIs7nhoQY=&rs=AOn4CLAH7ROQFRBlbk95fLvuYw5_7xirXg",
          "width": 240,
          "height": 240
        }
      ],
      "overlays": [
        {
          "type": "ThumbnailOverlayBadgeView",
          "badges": [
            {
              "type": "ThumbnailBadgeView",
              "icon_name": "BROADCAST",
              "text": "503 episodes",
              "badge_style": "THUMBNAIL_OVERLAY_BADGE_STYLE_DEFAULT",
              "background_color": {
                "light_theme": 2631201,
                "dark_theme": 2631201
              }
            }
          ],
          "position": "THUMBNAIL_OVERLAY_BADGE_POSITION_BOTTOM_END"
        },
        {
          "type": "ThumbnailHoverOverlayView",
          "icon_name": "PLAY_ALL",
          "text": {
            "runs": [
              {
                "text": "Play all",
                "bold": false,
                "italics": false,
                "strikethrough": false
              }
            ],
            "text": "Play all"
          },
          "style": "THUMBNAIL_HOVER_OVERLAY_STYLE_COVER"
        }
      ],
      "background_color": {
        "light_theme": 4144180,
        "dark_theme": 4144180
      }
    },
    "stack_color": {
      "light_theme": 12563613,
      "dark_theme": 9208947
    }
  },
  "metadata": {
    "type": "LockupMetadataView",
    "title": {
      "runs": [
        {
          "text": "YMH Podcast | Full Episodes",
          "bold": false,
          "italics": false,
          "strikethrough": false
        }
      ],
      "text": "YMH Podcast | Full Episodes"
    },
    "metadata": {
      "type": "ContentMetadataView",
      "metadata_rows": [
        {
          "metadata_parts": [
            {
              "text": {
                "runs": [
                  {
                    "text": "YMH Studios",
                    "bold": false,
                    "italics": false,
                    "strikethrough": false,
                    "endpoint": {
                      "type": "NavigationEndpoint",
                      "payload": {
                        "browseId": "UCYIgiXwJck_Pb5Nj-wIrsqg",
                        "canonicalBaseUrl": "/@YMHStudios"
                      },
                      "metadata": {
                        "url": "/@YMHStudios",
                        "page_type": "WEB_PAGE_TYPE_CHANNEL",
                        "api_url": "browse"
                      }
                    },
                    "attachment": {
                      "startIndex": 11,
                      "length": 0,
                      "element": {
                        "type": {
                          "imageType": {
                            "image": {
                              "sources": [
                                {
                                  "clientResource": {
                                    "imageName": "CHECK_CIRCLE_FILLED"
                                  },
                                  "width": 14,
                                  "height": 14
                                }
                              ]
                            }
                          }
                        },
                        "properties": {
                          "layoutProperties": {
                            "height": {
                              "value": 14,
                              "unit": "DIMENSION_UNIT_POINT"
                            },
                            "width": {
                              "value": 14,
                              "unit": "DIMENSION_UNIT_POINT"
                            },
                            "margin": {
                              "left": {
                                "value": 4,
                                "unit": "DIMENSION_UNIT_POINT"
                              }
                            }
                          }
                        }
                      },
                      "alignment": "ALIGNMENT_VERTICAL_CENTER"
                    }
                  }
                ],
                "text": "YMH Studios",
                "endpoint": {
                  "type": "NavigationEndpoint",
                  "payload": {
                    "browseId": "UCYIgiXwJck_Pb5Nj-wIrsqg",
                    "canonicalBaseUrl": "/@YMHStudios"
                  },
                  "metadata": {
                    "url": "/@YMHStudios",
                    "page_type": "WEB_PAGE_TYPE_CHANNEL",
                    "api_url": "browse"
                  }
                }
              }
            },
            {
              "text": {
                "runs": [
                  {
                    "text": "Podcast",
                    "bold": false,
                    "italics": false,
                    "strikethrough": false,
                    "endpoint": {
                      "type": "NavigationEndpoint",
                      "payload": {
                        "browseId": "UCYIgiXwJck_Pb5Nj-wIrsqg",
                        "canonicalBaseUrl": "/@YMHStudios"
                      },
                      "metadata": {
                        "url": "/@YMHStudios",
                        "page_type": "WEB_PAGE_TYPE_CHANNEL",
                        "api_url": "browse"
                      }
                    }
                  }
                ],
                "text": "Podcast",
                "endpoint": {
                  "type": "NavigationEndpoint",
                  "payload": {
                    "browseId": "UCYIgiXwJck_Pb5Nj-wIrsqg",
                    "canonicalBaseUrl": "/@YMHStudios"
                  },
                  "metadata": {
                    "url": "/@YMHStudios",
                    "page_type": "WEB_PAGE_TYPE_CHANNEL",
                    "api_url": "browse"
                  }
                }
              }
            }
          ]
        },
        {
          "metadata_parts": [
            {
              "text": {
                "runs": [
                  {
                    "text": "Updated 4 days ago",
                    "bold": false,
                    "italics": false,
                    "strikethrough": false
                  }
                ],
                "text": "Updated 4 days ago"
              }
            }
          ]
        },
        {
          "metadata_parts": [
            {
              "text": {
                "runs": [
                  {
                    "text": "View full podcast",
                    "bold": true,
                    "italics": false,
                    "strikethrough": false,
                    "endpoint": {
                      "type": "NavigationEndpoint",
                      "payload": {
                        "browseId": "VLPL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD"
                      },
                      "metadata": {
                        "url": "/playlist?list=PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD",
                        "page_type": "WEB_PAGE_TYPE_PLAYLIST",
                        "api_url": "browse"
                      }
                    }
                  }
                ],
                "text": "View full podcast",
                "endpoint": {
                  "type": "NavigationEndpoint",
                  "payload": {
                    "browseId": "VLPL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD"
                  },
                  "metadata": {
                    "url": "/playlist?list=PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD",
                    "page_type": "WEB_PAGE_TYPE_PLAYLIST",
                    "api_url": "browse"
                  }
                }
              }
            }
          ]
        }
      ],
      "delimiter": " • "
    }
  },
  "content_id": "PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD",
  "content_type": "PODCAST",
  "on_tap_endpoint": {
    "type": "NavigationEndpoint",
    "payload": {
      "videoId": "x1H2S_7UmWM",
      "playlistId": "PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD",
      "params": "OAI%3D",
      "playerParams": "iAQB",
      "loggingContext": {
        "vssLoggingContext": {
          "serializedContextData": "GiJQTC1pM0VWMXY1aExkOUgxcDJ3VDVaRDhhbEVZMEVteFlE"
        }
      }
    },
    "metadata": {
      "url": "/watch?v=x1H2S_7UmWM&list=PL-i3EV1v5hLd9H1p2wT5ZD8alEY0EmxYD&pp=iAQB",
      "page_type": "WEB_PAGE_TYPE_WATCH",
      "api_url": "/player"
    }
  }
}

There's a check in the toPlaylist() method that checks

if (playlist.type === 'LockupView' && playlist.content_type === 'PLAYLIST')

...but not

playlist.content_type === 'PODCAST'

which this single playlist on my account has.

Submitting a pr now :)

@thinktapper Thanks for keeping the issue updated with your debugging, it helped me to use the tool without having to wait for the PR to be merged (which helped my anxiety haha)