|
"""
|
|
bilibili_api.utils.parse_link
|
|
|
|
链接资源解析。
|
|
"""
|
|
|
|
import re
|
|
import json
|
|
from enum import Enum
|
|
from typing import Tuple, Union, Literal
|
|
|
|
import httpx
|
|
from yarl import URL
|
|
|
|
from .network import Api
|
|
from ..game import Game
|
|
from ..manga import Manga
|
|
from ..topic import Topic
|
|
from ..video import Video
|
|
from ..exceptions import *
|
|
from .utils import get_api
|
|
from ..live import LiveRoom
|
|
from ..dynamic import Dynamic
|
|
from .short import get_real_url
|
|
from ..note import Note, NoteType
|
|
from ..black_room import BlackRoom
|
|
from .credential import Credential
|
|
from ..audio import Audio, AudioList
|
|
from ..bangumi import Bangumi, Episode
|
|
from ..article import Article, ArticleList
|
|
from ..cheese import CheeseList, CheeseVideo
|
|
from ..interactive_video import InteractiveVideo
|
|
from ..favorite_list import FavoriteList, FavoriteListType
|
|
from ..user import User, ChannelSeries, ChannelSeriesType, get_self_info
|
|
|
|
from .initial_state import get_initial_state
|
|
|
|
|
|
class ResourceType(Enum):
|
|
"""
|
|
链接类型类。
|
|
|
|
+ VIDEO: 视频
|
|
+ BANGUMI: 番剧
|
|
+ EPISODE: 番剧剧集
|
|
+ FAVORITE_LIST: 视频收藏夹
|
|
+ CHEESE: 课程
|
|
+ CHEESE_VIDEO: 课程视频
|
|
+ AUDIO: 音频
|
|
+ AUDIO_LIST: 歌单
|
|
+ ARTICLE: 专栏
|
|
+ USER: 用户
|
|
+ LIVE: 直播间
|
|
+ CHANNEL_SERIES: 合集与列表
|
|
+ BLACK_ROOM: 小黑屋
|
|
+ GAME: 游戏
|
|
+ TOPIC: 话题
|
|
+ MANGA: 漫画
|
|
+ NOTE: 笔记
|
|
+ FAILED: 错误
|
|
"""
|
|
|
|
VIDEO = "video"
|
|
INTERACTIVE_VIDEO = "interactive_video"
|
|
BANGUMI = "bangumi"
|
|
EPISODE = "episode"
|
|
FAVORITE_LIST = "favorite_list"
|
|
CHEESE_VIDEO = "cheese_video"
|
|
AUDIO = "audio"
|
|
AUDIO_LIST = "audio_list"
|
|
ARTICLE = "article"
|
|
USER = "user"
|
|
LIVE = "live"
|
|
CHANNEL_SERIES = "channel_series"
|
|
ARTICLE_LIST = "article_list"
|
|
DYNAMIC = "dynamic"
|
|
BLACK_ROOM = "black_room"
|
|
GAME = "game"
|
|
TOPIC = "topic"
|
|
MANGA = "manga"
|
|
NOTE = "note"
|
|
FAILED = "failed"
|
|
|
|
|
|
async def parse_link(
|
|
url: str, credential: Union[Credential, None] = None
|
|
) -> Union[
|
|
Tuple[Video, Literal[ResourceType.VIDEO]],
|
|
Tuple[InteractiveVideo, Literal[ResourceType.INTERACTIVE_VIDEO]],
|
|
Tuple[Bangumi, Literal[ResourceType.BANGUMI]],
|
|
Tuple[Episode, Literal[ResourceType.EPISODE]],
|
|
Tuple[FavoriteList, Literal[ResourceType.FAVORITE_LIST]],
|
|
Tuple[CheeseVideo, Literal[ResourceType.CHEESE_VIDEO]],
|
|
Tuple[Audio, Literal[ResourceType.AUDIO]],
|
|
Tuple[AudioList, Literal[ResourceType.AUDIO_LIST]],
|
|
Tuple[Article, Literal[ResourceType.ARTICLE]],
|
|
Tuple[User, Literal[ResourceType.USER]],
|
|
Tuple[LiveRoom, Literal[ResourceType.LIVE]],
|
|
Tuple[ChannelSeries, Literal[ResourceType.CHANNEL_SERIES]],
|
|
Tuple[ArticleList, Literal[ResourceType.ARTICLE_LIST]],
|
|
Tuple[Dynamic, Literal[ResourceType.DYNAMIC]],
|
|
Tuple[BlackRoom, Literal[ResourceType.BLACK_ROOM]],
|
|
Tuple[Game, Literal[ResourceType.GAME]],
|
|
Tuple[Topic, Literal[ResourceType.TOPIC]],
|
|
Tuple[Manga, Literal[ResourceType.MANGA]],
|
|
Tuple[Note, Literal[ResourceType.NOTE]],
|
|
Tuple[Literal[-1], Literal[ResourceType.FAILED]],
|
|
]:
|
|
"""
|
|
调用 yarl 解析 bilibili url 的函数。
|
|
|
|
Args:
|
|
url(str) : 链接
|
|
|
|
credential(Credential): 凭据类
|
|
|
|
Returns:
|
|
Tuple[obj, ResourceType]: (对象,类型) 或 -1,-1 表示出错
|
|
"""
|
|
credential = credential if credential else Credential()
|
|
url = url.replace("\\", "/")
|
|
try:
|
|
obj = None
|
|
|
|
|
|
sobj = await check_short_name(url, credential)
|
|
if sobj != -1:
|
|
sobj[0].credential = credential
|
|
return sobj
|
|
|
|
|
|
url = url.strip()
|
|
|
|
if url.lstrip("https:") == url:
|
|
url = "https:" + url
|
|
|
|
|
|
url = URL(url)
|
|
|
|
|
|
black_room = parse_black_room(url, credential)
|
|
if not black_room == -1:
|
|
obj = (black_room, ResourceType.BLACK_ROOM)
|
|
return obj
|
|
|
|
|
|
if url.host == "space.bilibili.com" and url.path == "/" or url.path == "":
|
|
try:
|
|
info = await get_self_info(credential)
|
|
except Exception as e:
|
|
return (-1, ResourceType.FAILED)
|
|
else:
|
|
return (User(info["mid"], credential=credential), ResourceType.USER)
|
|
|
|
channel = parse_season_series(
|
|
url, credential
|
|
)
|
|
if channel != -1:
|
|
return (channel, ResourceType.CHANNEL_SERIES)
|
|
|
|
url = await get_real_url(str(url))
|
|
url = URL(url)
|
|
|
|
fl_space = parse_space_favorite_list(url, credential)
|
|
if fl_space != -1:
|
|
return fl_space
|
|
game = parse_game(url, credential)
|
|
if game != -1:
|
|
game.credential = credential
|
|
return (game, ResourceType.GAME)
|
|
topic = parse_topic(url, credential)
|
|
if topic != -1:
|
|
topic.credential = credential
|
|
return (topic, ResourceType.TOPIC)
|
|
festival_video = await parse_festival(url, credential)
|
|
if festival_video != -1:
|
|
festival_video.credential = credential
|
|
return (festival_video, ResourceType.VIDEO)
|
|
note = parse_note(url, credential)
|
|
if note != -1:
|
|
return (note, ResourceType.NOTE)
|
|
|
|
obj = None
|
|
video = await parse_video(url, credential)
|
|
if not video == -1:
|
|
obj = video
|
|
bangumi = parse_bangumi(url, credential)
|
|
if not bangumi == -1:
|
|
obj = (bangumi, ResourceType.BANGUMI)
|
|
episode = await parse_episode(url, credential)
|
|
if not episode == -1:
|
|
obj = (episode, ResourceType.EPISODE)
|
|
favorite_list = parse_favorite_list(url, credential)
|
|
if not favorite_list == -1:
|
|
obj = (favorite_list, ResourceType.FAVORITE_LIST)
|
|
cheese_video = await parse_cheese_video(url, credential)
|
|
if not cheese_video == -1:
|
|
obj = (cheese_video, ResourceType.CHEESE_VIDEO)
|
|
audio = parse_audio(url, credential)
|
|
if not audio == -1:
|
|
obj = (audio, ResourceType.AUDIO)
|
|
audio_list = parse_audio_list(url, credential)
|
|
if not audio_list == -1:
|
|
obj = (audio_list, ResourceType.AUDIO_LIST)
|
|
article = parse_article(url, credential)
|
|
if not article == -1:
|
|
obj = (article, ResourceType.ARTICLE)
|
|
article_list = parse_article_list(url, credential)
|
|
if not article_list == -1:
|
|
obj = (article_list, ResourceType.ARTICLE_LIST)
|
|
user = parse_user(url, credential)
|
|
if not user == -1:
|
|
obj = (user, ResourceType.USER)
|
|
live = parse_live(url, credential)
|
|
if not live == -1:
|
|
obj = (live, ResourceType.LIVE)
|
|
dynamic = parse_dynamic(url, credential)
|
|
if not dynamic == -1:
|
|
obj = (dynamic, ResourceType.DYNAMIC)
|
|
manga = parse_manga(url, credential)
|
|
if not manga == -1:
|
|
obj = (manga, ResourceType.MANGA)
|
|
opus_dynamic = parse_opus_dynamic(url, credential)
|
|
if not opus_dynamic == -1:
|
|
obj = (opus_dynamic, ResourceType.DYNAMIC)
|
|
|
|
if obj == None or obj[0] == None:
|
|
return (-1, ResourceType.FAILED)
|
|
else:
|
|
obj[0].credential = credential
|
|
return obj
|
|
except Exception as e:
|
|
raise e
|
|
|
|
|
|
|
|
async def auto_convert_video(
|
|
video: Video, credential: Union[Credential, None] = None
|
|
) -> Tuple[Union[Video, Episode, InteractiveVideo], ResourceType]:
|
|
|
|
video_info = await video.get_info()
|
|
if video_info["rights"]["is_stein_gate"] == 1:
|
|
return (
|
|
InteractiveVideo(video.get_bvid(), credential=credential),
|
|
ResourceType.INTERACTIVE_VIDEO,
|
|
)
|
|
|
|
|
|
if "redirect_url" in video_info:
|
|
reparse_link = await parse_link(
|
|
await get_real_url(video_info["redirect_url"]), credential=credential
|
|
)
|
|
return reparse_link
|
|
|
|
|
|
return (video, ResourceType.VIDEO)
|
|
|
|
|
|
async def check_short_name(
|
|
name: str, credential: Credential
|
|
) -> Union[
|
|
Tuple[Video, Literal[ResourceType.VIDEO]],
|
|
Tuple[Episode, Literal[ResourceType.EPISODE]],
|
|
Tuple[CheeseVideo, Literal[ResourceType.CHEESE_VIDEO]],
|
|
Tuple[FavoriteList, Literal[ResourceType.FAVORITE_LIST]],
|
|
Tuple[User, Literal[ResourceType.USER]],
|
|
Tuple[Article, Literal[ResourceType.ARTICLE]],
|
|
Tuple[Audio, Literal[ResourceType.AUDIO]],
|
|
Tuple[AudioList, Literal[ResourceType.AUDIO_LIST]],
|
|
Tuple[ArticleList, Literal[ResourceType.ARTICLE_LIST]],
|
|
Literal[-1],
|
|
]:
|
|
"""
|
|
解析:
|
|
- mlxxxxxxxxxx
|
|
- uidxxxxxxxxx
|
|
- cvxxxxxxxxxx
|
|
- auxxxxxxxxxx
|
|
- amxxxxxxxxxx
|
|
- rlxxxxxxxxxx
|
|
"""
|
|
if name[:2].upper() == "AV":
|
|
v = Video(aid=int(name[2:]), credential=credential)
|
|
return await auto_convert_video(v, credential=credential)
|
|
elif name[:2].upper() == "BV":
|
|
v = Video(bvid=name, credential=credential)
|
|
return await auto_convert_video(v, credential=credential)
|
|
elif name[:2].upper() == "ML":
|
|
return (
|
|
FavoriteList(FavoriteListType.VIDEO, int(name[2:]), credential=credential),
|
|
ResourceType.FAVORITE_LIST,
|
|
)
|
|
elif name[:3].upper() == "UID":
|
|
return (User(int(name[3:]), credential=credential), ResourceType.USER)
|
|
elif name[:2].upper() == "CV":
|
|
return (Article(int(name[2:]), credential=credential), ResourceType.ARTICLE)
|
|
elif name[:2].upper() == "AU":
|
|
return (Audio(int(name[2:]), credential=credential), ResourceType.AUDIO)
|
|
elif name[:2].upper() == "AM":
|
|
return (
|
|
AudioList(int(name[2:]), credential=credential),
|
|
ResourceType.AUDIO_LIST,
|
|
)
|
|
elif name[:2].upper() == "RL":
|
|
return (
|
|
ArticleList(int(name[2:]), credential=credential),
|
|
ResourceType.ARTICLE_LIST,
|
|
)
|
|
else:
|
|
return -1
|
|
|
|
|
|
async def parse_video(
|
|
url: URL, credential: Credential
|
|
) -> Union[Tuple[Union[Video, Episode, InteractiveVideo], ResourceType], Literal[-1]]:
|
|
"""
|
|
解析视频,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and url.parts[1] == "video":
|
|
raw_video_id = url.parts[2]
|
|
if raw_video_id[:2].upper() == "AV":
|
|
aid = int(raw_video_id[2:])
|
|
v = Video(aid=aid, credential=credential)
|
|
elif raw_video_id[:2].upper() == "BV":
|
|
v = Video(bvid=raw_video_id, credential=credential)
|
|
else:
|
|
return -1
|
|
return await auto_convert_video(v, credential=credential)
|
|
else:
|
|
return -1
|
|
|
|
|
|
def parse_bangumi(url: URL, credential: Credential) -> Union[Bangumi, int]:
|
|
"""
|
|
解析番剧,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and len(url.parts) >= 4:
|
|
if url.parts[:3] == ("/", "bangumi", "media"):
|
|
media_id = int(url.parts[3][2:])
|
|
return Bangumi(media_id=media_id, credential=credential)
|
|
return -1
|
|
|
|
|
|
async def parse_episode(url: URL, credential: Credential) -> Union[Episode, int]:
|
|
"""
|
|
解析番剧剧集,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and len(url.parts) >= 3:
|
|
if url.parts[1] == "bangumi" and url.parts[2] == "play":
|
|
video_short_id = url.parts[3]
|
|
|
|
if video_short_id[:2].upper() == "EP":
|
|
epid = int(video_short_id[2:])
|
|
return Episode(epid=epid)
|
|
elif video_short_id[:2].upper() == "SS":
|
|
bangumi = Bangumi(ssid=int(video_short_id[2:]))
|
|
epid = (await bangumi.get_episodes())[0].get_epid()
|
|
return Episode(epid=epid)
|
|
return -1
|
|
|
|
|
|
def parse_favorite_list(url: URL, credential: Credential) -> Union[FavoriteList, int]:
|
|
"""
|
|
解析收藏夹,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and len(url.parts) >= 4:
|
|
if url.parts[:3] == ("/", "medialist", "detail"):
|
|
media_id = int(url.parts[3][2:])
|
|
return FavoriteList(media_id=media_id, credential=credential)
|
|
return -1
|
|
|
|
|
|
async def parse_cheese_video(
|
|
url: URL, credential: Credential
|
|
) -> Union[CheeseVideo, int]:
|
|
"""
|
|
解析课程视频,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and len(url.parts) >= 4:
|
|
if url.parts[1] == "cheese" and url.parts[2] == "play":
|
|
if url.parts[3][:2].upper() == "EP":
|
|
epid = int(url.parts[3][2:])
|
|
return CheeseVideo(epid=epid, credential=credential)
|
|
elif url.parts[3][:2].upper() == "SS":
|
|
clid = int(url.parts[3][2:])
|
|
cl = CheeseList(season_id=clid, credential=credential)
|
|
return CheeseVideo(
|
|
epid=(await cl.get_list_raw())["items"][0]["id"],
|
|
credential=credential,
|
|
)
|
|
return -1
|
|
|
|
|
|
def parse_audio(url: URL, credential: Credential) -> Union[Audio, int]:
|
|
"""
|
|
解析音频,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and url.parts[1] == "audio":
|
|
if url.parts[2][:2].upper() == "AU":
|
|
auid = int(url.parts[2][2:])
|
|
return Audio(auid=auid, credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_audio_list(url: URL, credential: Credential) -> Union[AudioList, int]:
|
|
"""
|
|
解析歌单,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and url.parts[1] == "audio":
|
|
if url.parts[2][:2].upper() == "AM":
|
|
amid = int(url.parts[2][2:])
|
|
return AudioList(amid=amid, credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_article(url: URL, credential: Credential) -> Union[Article, int]:
|
|
"""
|
|
解析专栏,如果不是返回 -1,否则返回对应类
|
|
"""
|
|
if url.host == "www.bilibili.com" and len(url.parts) >= 3:
|
|
if url.parts[1] == "read" and url.parts[2][:2].upper() == "CV":
|
|
cvid = int(url.parts[2][2:])
|
|
return Article(cvid=cvid, credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_user(url: URL, credential: Credential) -> Union[User, int]:
|
|
if url.host == "space.bilibili.com":
|
|
if len(url.parts) >= 2:
|
|
uid = url.parts[1]
|
|
return User(uid=int(uid), credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_live(url: URL, credential: Credential) -> Union[LiveRoom, int]:
|
|
if url.host == "live.bilibili.com":
|
|
if len(url.parts) >= 2:
|
|
room_display_id = int(url.parts[1])
|
|
return LiveRoom(room_display_id=room_display_id, credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_season_series(url: URL, credential: Credential) -> Union[ChannelSeries, int]:
|
|
if url.host == "space.bilibili.com":
|
|
if len(url.parts) >= 2:
|
|
try:
|
|
uid = int(url.parts[1])
|
|
except:
|
|
pass
|
|
else:
|
|
if len(url.parts) >= 4:
|
|
if url.parts[3] == "collectiondetail":
|
|
|
|
if url.query.get("sid") is not None:
|
|
sid = int(url.query["sid"])
|
|
return ChannelSeries(
|
|
uid,
|
|
ChannelSeriesType.SEASON,
|
|
id_=sid,
|
|
credential=credential,
|
|
)
|
|
elif url.parts[3] == "seriesdetail":
|
|
|
|
if url.query.get("sid") is not None:
|
|
sid = int(url.query["sid"])
|
|
return ChannelSeries(
|
|
uid,
|
|
ChannelSeriesType.SERIES,
|
|
id_=sid,
|
|
credential=credential,
|
|
)
|
|
elif url.host == "www.bilibili.com":
|
|
if url.parts[1] == "list":
|
|
|
|
if len(url.parts) >= 3:
|
|
uid = int(url.parts[2])
|
|
if "sid" in url.query:
|
|
sid = int(url.query["sid"])
|
|
return ChannelSeries(
|
|
uid, ChannelSeriesType.SERIES, id_=sid, credential=credential
|
|
)
|
|
|
|
elif url.parts[1] == "medialist" and url.parts[2] == "play":
|
|
if len(url.parts) >= 4:
|
|
uid = int(url.parts[3])
|
|
if "business_id" in url.query:
|
|
sid = int(url.query["business_id"])
|
|
return ChannelSeries(
|
|
uid, ChannelSeriesType.SERIES, id_=sid, credential=credential
|
|
)
|
|
return -1
|
|
|
|
|
|
def parse_space_favorite_list(
|
|
url: URL, credential: Credential
|
|
) -> Union[
|
|
Tuple[FavoriteList, ResourceType], Tuple[ChannelSeries, ResourceType], Literal[-1]
|
|
]:
|
|
if url.host == "space.bilibili.com":
|
|
uid = url.parts[1]
|
|
if len(url.parts) >= 3:
|
|
if url.parts[2] == "favlist":
|
|
if (
|
|
len(url.parts) == 3 and url.query.get("fid") == None
|
|
):
|
|
api = get_api("favorite-list")["info"]["list_list"]
|
|
params = {"up_mid": uid, "type": 2}
|
|
favorite_lists = (
|
|
Api(**api, credential=credential)
|
|
.update_params(**params)
|
|
.result_sync
|
|
)
|
|
|
|
if favorite_lists == None:
|
|
return -1
|
|
else:
|
|
default_favorite_id = int(favorite_lists["list"][0]["id"])
|
|
return (
|
|
FavoriteList(
|
|
media_id=default_favorite_id, credential=credential
|
|
),
|
|
ResourceType.FAVORITE_LIST,
|
|
)
|
|
|
|
elif len(url.query) != 0:
|
|
fid = url.query.get("fid")
|
|
ctype = url.query.get("ctype")
|
|
try:
|
|
fid = int(fid)
|
|
fid_is_int = True
|
|
except:
|
|
fid_is_int = False
|
|
if ctype is None and fid_is_int:
|
|
|
|
fid = int(fid)
|
|
return (FavoriteList(media_id=fid), ResourceType.FAVORITE_LIST)
|
|
elif fid_is_int:
|
|
if int(ctype) == 11:
|
|
fid = int(fid)
|
|
fid_is_int = True
|
|
return (
|
|
FavoriteList(media_id=fid, credential=credential),
|
|
ResourceType.FAVORITE_LIST,
|
|
)
|
|
elif int(ctype) == 21:
|
|
fid = int(fid)
|
|
fid_is_int = True
|
|
return (
|
|
ChannelSeries(
|
|
id_=fid,
|
|
type_=ChannelSeriesType.SEASON,
|
|
credential=credential,
|
|
),
|
|
ResourceType.CHANNEL_SERIES,
|
|
)
|
|
elif fid_is_int == False:
|
|
|
|
if fid == FavoriteListType.ARTICLE.value:
|
|
return (
|
|
FavoriteList(
|
|
FavoriteListType.ARTICLE, credential=credential
|
|
),
|
|
ResourceType.FAVORITE_LIST,
|
|
)
|
|
elif fid == FavoriteListType.CHEESE.value:
|
|
return (
|
|
FavoriteList(
|
|
FavoriteListType.CHEESE, credential=credential
|
|
),
|
|
ResourceType.FAVORITE_LIST,
|
|
)
|
|
return -1
|
|
|
|
|
|
def parse_article_list(url: URL, credential: Credential) -> Union[ArticleList, int]:
|
|
if url.host == "www.bilibili.com" and len(url.parts) >= 3:
|
|
if url.parts[:3] == ("/", "read", "readlist"):
|
|
rlid = int(url.parts[3][2:])
|
|
return ArticleList(rlid=rlid, credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_dynamic(url: URL, credential: Credential) -> Union[Dynamic, int]:
|
|
if url.host == "t.bilibili.com":
|
|
if len(url.parts) >= 2:
|
|
dynamic_id = int(url.parts[1])
|
|
return Dynamic(dynamic_id, credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_black_room(url: URL, credential: Credential) -> Union[BlackRoom, int]:
|
|
if len(url.parts) >= 3:
|
|
if url.parts[:3] == ("/", "blackroom", "ban"):
|
|
if len(url.parts) >= 4:
|
|
return BlackRoom(int(url.parts[3]), credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_game(url: URL, credential: Credential) -> Union[Game, int]:
|
|
if (
|
|
url.host == "www.biligame.com"
|
|
and url.parts[1] == "detail"
|
|
and url.query.get("id") is not None
|
|
):
|
|
return Game(int(url.query["id"]), credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_topic(url: URL, credential: Credential) -> Union[Topic, int]:
|
|
if url.host == "www.bilibili.com" and len(url.parts) >= 4:
|
|
if (
|
|
url.parts[:4] == ("/", "v", "topic", "detail")
|
|
and url.query.get("topic_id") is not None
|
|
):
|
|
return Topic(int(url.query["topic_id"]), credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_manga(url: URL, credential: Credential) -> Union[Manga, int]:
|
|
if url.host == "manga.bilibili.com" and url.parts[1] == "detail":
|
|
return Manga(int(url.parts[2][2:]), credential=credential)
|
|
return -1
|
|
|
|
|
|
async def parse_festival(url: URL, credential: Credential) -> Union[Video, int]:
|
|
bvid = url.query.get("bvid")
|
|
if bvid is not None:
|
|
return Video(bvid, credential=credential)
|
|
|
|
if (
|
|
url.host == "www.bilibili.com" and url.parts[1] == "festival"
|
|
):
|
|
content, content_type = await get_initial_state(
|
|
url=str(url), credential=credential
|
|
)
|
|
return Video(
|
|
content["videoSections"][0]["episodes"][0]["bvid"], credential=credential
|
|
)
|
|
return -1
|
|
|
|
|
|
def parse_note(url: URL, credential: Credential) -> Union[Note, int]:
|
|
|
|
if url.host == "www.bilibili.com" and url.parts[1:4] == ("h5", "note-app", "view"):
|
|
if url.query.get("cvid") == None:
|
|
return -1
|
|
return Note(cvid=int(url.query.get("cvid")), note_type=NoteType.PUBLIC, credential=credential)
|
|
return -1
|
|
|
|
|
|
def parse_nianshizhiwang(url: URL) -> None:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def parse_opus_dynamic(url: URL, credential: Credential) -> Union[Dynamic, int]:
|
|
|
|
if url.host == "www.bilibili.com" and url.parts[:2] == ("/", "opus"):
|
|
return Dynamic(dynamic_id=int(url.parts[-1]), credential=credential)
|
|
return -1
|
|
|