Skip to content

Add Bilibili, Youku and NetEase Music adapters - #588

Merged
Vitorinox merged 3 commits into
masterfrom
feat/chinese-providers
Oct 31, 2026
Merged

Vitorinox merged 3 commits into
masterfrom
feat/chinese-providers

Conversation

@Vitorinox

@Vitorinox Vitorinox commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #243

Adds adapters for three Chinese providers. None of them offers oEmbed, so the embed code is built from the URL alone, without extra requests. That way the player works even when the page itself cannot be read.

Adapters

Bilibili (bilibili.com, and b23.tv short links through their redirect)

  • /video/BV... and /video/av... get an iframe to https://player.bilibili.com/player.html?bvid=<BV> (or aid=<n>), 640x360.
  • When Bilibili serves one of its error pages (titles 出错啦! or 视频去哪了呢?_哔哩哔哩_bilibili), title, description and image return null instead of the error page values. A description that is an unfilled template (with {$0} or undefined placeholders) is discarded too.

Youku (youku.com)

  • v.youku.com/v_show/id_<ID>.html gets an iframe to https://player.youku.com/embed/<ID>, 640x360.

NetEase Music (music.163.com)

  • Songs, albums and playlists get an iframe to https://music.163.com/outchain/player?type=<2|1|0>&id=<id>&auto=0&height=66, 330x86.
  • URLs like music.163.com/#/song?id=1 keep the route after #, which is never sent to the server, so the homepage came back. They are now requested as music.163.com/song?id=1.
  • authorName is read from the description (由 X 演唱) when there is no other author.
  • MV pages (/mv?id= and /#/mv?id=) are normalized the same way, but NetEase has no official embeddable MV player, so they get metadata only (no code).

To support the NetEase case there is a new static Extractor::normalizeUri() hook. ExtractorFactory::normalizeUri() calls it for the matching adapter, and Embed::get() / getMulti() use it before sending the request. The default implementation returns the URL unchanged.

Testing

  • tests/ChineseProvidersTest.php: offline unit tests for the player URLs (bvid, aid, Youku id, NetEase song/album/playlist with and without #/), both Bilibili error pages and the unfilled description template, the requested NetEase URL (songs and MVs), MV without iframe and the author parsing.
  • tests/PagesTest.php: new snapshots for one Bilibili video, one Youku video, one NetEase song and one NetEase album (#/ URL).
  • The Youku and NetEase snapshots were captured from a residential connection in Venezuela: Youku returns the real title and description (its page leaves og:image empty for this video). The NetEase album ID (18905) comes from the album link of the real song page (186016).
  • Bilibili still serves its error page from the residential connection too (and from a US data center IP), so its snapshot only contains the error page. This is stated in the fixture and tests.
  • Full suite passes on PHP 7.4, 8.1, 8.2, 8.3, 8.4 and 8.5 (Homebrew), and on PHP 8.5 on a Raspberry Pi behind the residential connection. PHPStan reports no errors.

Help wanted

Youku and NetEase are verified from a residential connection in Venezuela, but Bilibili only returns its error page from every network we tried, so we could not check its real metadata. If you are in a region where Bilibili works normally (or outside restricted networks in general), we would be very grateful if you could try a few real video URLs with this branch and tell us what you get for title, image and code. Something like this is enough:

$info = (new Embed\Embed())->get('https://www.bilibili.com/video/BV1GJ411x7h7');
var_dump($info->title, (string) $info->image, $info->code->html ?? null);

Thank you!

@Vitorinox
Vitorinox merged commit bda2c8b into master Oct 31, 2026
10 checks passed
@Vitorinox
Vitorinox deleted the feat/chinese-providers branch October 31, 2026 02:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant