feature: add private url scheme
* Also reformat code * Rename symbol LinkType.Song -> LinkType.Track
This commit is contained in:
parent
c00e881577
commit
3cb6653449
@ -1,4 +1,4 @@
|
|||||||
from .__version__ import __title__, __description__, __version__
|
|
||||||
from .__main__ import main
|
from .__main__ import main
|
||||||
|
from .__version__ import __description__, __title__, __version__
|
||||||
|
|
||||||
__all__ = ["__title__", "__description__", "__version__", "main"]
|
__all__ = ["__title__", "__description__", "__version__", "main"]
|
||||||
|
@ -93,7 +93,7 @@ def main(outputs: list[Path], exist: bool, overwrite: bool, quiet: bool, links:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
match parsed.type:
|
match parsed.type:
|
||||||
case LinkType.Song:
|
case LinkType.Track:
|
||||||
newTrack = api.getDetailsForTrack(parsed.id)
|
newTrack = api.getDetailsForTrack(parsed.id)
|
||||||
savePath = pickOutput(newTrack, outputs, exist)
|
savePath = pickOutput(newTrack, outputs, exist)
|
||||||
|
|
||||||
|
@ -29,6 +29,6 @@ class LrcMetaType(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class LinkType(Enum):
|
class LinkType(Enum):
|
||||||
Song = auto()
|
Track = "track"
|
||||||
Album = auto()
|
Album = "album"
|
||||||
Playlist = auto()
|
Playlist = "playlist"
|
||||||
|
@ -12,7 +12,8 @@ from .object import NCMTrack
|
|||||||
|
|
||||||
__all__ = ["Link", "parseLink", "testExistTrackSource", "pickOutput"]
|
__all__ = ["Link", "parseLink", "testExistTrackSource", "pickOutput"]
|
||||||
|
|
||||||
RE_ANDROID_ALBUM_SHARE_LINK_PATH = reCompile(r"^/album/(?P<id>\d*)/?$")
|
RE_SHARE_LINK_ID_BY_PATH = reCompile(r"^/?(?P<id>\d+)$")
|
||||||
|
RE_SHARE_LINK_ANDROID_ALBUM_PATH = reCompile(r"^/album/(?P<id>\d+)/?$")
|
||||||
RE_SAFE_FILENAME = reCompile(r"\*{2,}")
|
RE_SAFE_FILENAME = reCompile(r"\*{2,}")
|
||||||
TRANSLATER_SAFE_FILENAME = str.maketrans({i: 0x2A for i in ("<", ">", ":", '"', "/", "\\", "|", "?")})
|
TRANSLATER_SAFE_FILENAME = str.maketrans({i: 0x2A for i in ("<", ">", ":", '"', "/", "\\", "|", "?")})
|
||||||
|
|
||||||
@ -28,39 +29,72 @@ def parseLink(url: str) -> Link:
|
|||||||
contentType: LinkType | None = None
|
contentType: LinkType | None = None
|
||||||
contentId: int | None = None
|
contentId: int | None = None
|
||||||
|
|
||||||
match parsedUrl.netloc:
|
match parsedUrl.scheme:
|
||||||
case "music.163.com":
|
case "http" | "https":
|
||||||
match parsedUrl.path:
|
match parsedUrl.netloc:
|
||||||
case "/playlist" | "/#/playlist":
|
case "music.163.com":
|
||||||
contentType = LinkType.Playlist
|
match parsedUrl.path:
|
||||||
case "/album" | "/#/album":
|
case "/playlist" | "/#/playlist":
|
||||||
contentType = LinkType.Album
|
contentType = LinkType.Playlist
|
||||||
case "/song" | "/#/song":
|
case "/album" | "/#/album":
|
||||||
contentType = LinkType.Song
|
contentType = LinkType.Album
|
||||||
case _:
|
case "/song" | "/#/song":
|
||||||
# Hack for android client shared album link
|
contentType = LinkType.Track
|
||||||
matchedPath = RE_ANDROID_ALBUM_SHARE_LINK_PATH.match(parsedUrl.path)
|
case _:
|
||||||
if matchedPath is not None:
|
# Hack for android client shared album link
|
||||||
contentType = LinkType.Album
|
matchedPath = RE_SHARE_LINK_ANDROID_ALBUM_PATH.match(parsedUrl.path)
|
||||||
contentId = int(matchedPath["id"])
|
if matchedPath is not None:
|
||||||
else:
|
contentType = LinkType.Album
|
||||||
raise UnsupportedLinkError(parsedUrl)
|
contentId = int(matchedPath["id"])
|
||||||
case "y.music.163.com":
|
else:
|
||||||
match parsedUrl.path:
|
raise UnsupportedLinkError(parsedUrl)
|
||||||
case "/m/playlist":
|
case "y.music.163.com":
|
||||||
contentType = LinkType.Playlist
|
match parsedUrl.path:
|
||||||
case "/m/song":
|
case "/m/playlist":
|
||||||
contentType = LinkType.Song
|
contentType = LinkType.Playlist
|
||||||
|
case "/m/song":
|
||||||
|
contentType = LinkType.Track
|
||||||
|
case _:
|
||||||
|
raise UnsupportedLinkError(parsedUrl)
|
||||||
|
case "163cn.tv":
|
||||||
|
response = httpGet(url)
|
||||||
|
if response.status_code != 302:
|
||||||
|
raise ParseLinkError(f"未知的 Api 响应: {response.status_code}")
|
||||||
|
newUrl = response.headers.get("Location")
|
||||||
|
if newUrl is None:
|
||||||
|
raise ParseLinkError("Api 未返回重定向结果")
|
||||||
|
return parseLink(newUrl)
|
||||||
case _:
|
case _:
|
||||||
raise UnsupportedLinkError(parsedUrl)
|
raise UnsupportedLinkError(parsedUrl)
|
||||||
case "163cn.tv":
|
case "ncmlyrics": # eg: ncmlyrics://playlist/123456, ncmlyrics://album/12456, ncmlyrics://track/123456
|
||||||
response = httpGet(url)
|
try:
|
||||||
if response.status_code != 302:
|
contentType = LinkType(parsedUrl.netloc)
|
||||||
raise ParseLinkError(f"未知的 Api 响应: {response.status_code}")
|
except ValueError:
|
||||||
newUrl = response.headers.get("Location")
|
raise UnsupportedLinkError(parsedUrl)
|
||||||
if newUrl is None:
|
|
||||||
raise ParseLinkError("Api 未返回重定向结果")
|
if parsedUrl.path:
|
||||||
return parseLink(newUrl)
|
matched = RE_SHARE_LINK_ID_BY_PATH.match(parsedUrl.path)
|
||||||
|
if matched is not None:
|
||||||
|
contentId = int(matched.group("id"))
|
||||||
|
else:
|
||||||
|
raise ParseLinkError
|
||||||
|
case "playlist" | "album" | "track": # eg: playlist:123456, album:/12456, track://123456
|
||||||
|
try:
|
||||||
|
contentType = LinkType(parsedUrl.scheme)
|
||||||
|
except ValueError:
|
||||||
|
raise UnsupportedLinkError(parsedUrl)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if parsedUrl.netloc:
|
||||||
|
contentId = int(parsedUrl.netloc)
|
||||||
|
elif parsedUrl.path:
|
||||||
|
matched = RE_SHARE_LINK_ID_BY_PATH.match(parsedUrl.path)
|
||||||
|
if matched is not None:
|
||||||
|
contentId = int(matched.group("id"))
|
||||||
|
else:
|
||||||
|
raise ParseLinkError
|
||||||
|
except ValueError:
|
||||||
|
raise ParseLinkError
|
||||||
case _:
|
case _:
|
||||||
raise UnsupportedLinkError(parsedUrl)
|
raise UnsupportedLinkError(parsedUrl)
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ class TestUtils(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parseLink("https://music.163.com/song?id=2621105420"),
|
parseLink("https://music.163.com/song?id=2621105420"),
|
||||||
Link(LinkType.Song, 2621105420),
|
Link(LinkType.Track, 2621105420),
|
||||||
msg="Shared song from NCM Windows Client",
|
msg="Shared song from NCM Windows Client",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class TestUtils(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parseLink("https://music.163.com/#/song?id=2621105420"),
|
parseLink("https://music.163.com/#/song?id=2621105420"),
|
||||||
Link(LinkType.Song, 2621105420),
|
Link(LinkType.Track, 2621105420),
|
||||||
msg="Song from NCM Website",
|
msg="Song from NCM Website",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,18 +61,55 @@ class TestUtils(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parseLink("https://y.music.163.com/m/song?id=2604307454"),
|
parseLink("https://y.music.163.com/m/song?id=2604307454"),
|
||||||
Link(LinkType.Song, 2604307454),
|
Link(LinkType.Track, 2604307454),
|
||||||
msg="Shared song from NCM Android Client",
|
msg="Shared song from NCM Android Client",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_parseLink_302(self):
|
def test_parseLink_302(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parseLink("http://163cn.tv/xpaQwii"),
|
parseLink("http://163cn.tv/xpaQwii"),
|
||||||
Link(LinkType.Song, 413077069),
|
Link(LinkType.Track, 413077069),
|
||||||
msg="Shared song from NCM Android Client player",
|
msg="Shared song from NCM Android Client player",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_parseLink_UnsupportShareLinkError(self):
|
def test_parseLink_special(self):
|
||||||
|
self.assertEqual(
|
||||||
|
parseLink("ncmlyrics://playlist/123456"),
|
||||||
|
Link(LinkType.Playlist, 123456),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
parseLink("ncmlyrics://album/123456"),
|
||||||
|
Link(LinkType.Album, 123456),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
parseLink("ncmlyrics://track/123456"),
|
||||||
|
Link(LinkType.Track, 123456),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
parseLink("playlist:123456"),
|
||||||
|
Link(LinkType.Playlist, 123456),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
parseLink("album:/123456"),
|
||||||
|
Link(LinkType.Album, 123456),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
parseLink("track://123456"),
|
||||||
|
Link(LinkType.Track, 123456),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_parseLink_UnsupportedLinkError(self):
|
||||||
|
self.assertRaises(
|
||||||
|
UnsupportedLinkError,
|
||||||
|
parseLink,
|
||||||
|
"ftp://ftpserver.com/",
|
||||||
|
)
|
||||||
|
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
UnsupportedLinkError,
|
UnsupportedLinkError,
|
||||||
parseLink,
|
parseLink,
|
||||||
@ -91,13 +128,25 @@ class TestUtils(TestCase):
|
|||||||
"https://music.163.com/album/123a",
|
"https://music.163.com/album/123a",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_parseLink_ParseShareLinkError(self):
|
self.assertRaises(
|
||||||
|
UnsupportedLinkError,
|
||||||
|
parseLink,
|
||||||
|
"ncmlyrics://unsupport/123456",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_parseLink_ParseLinkError(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ParseLinkError,
|
ParseLinkError,
|
||||||
parseLink,
|
parseLink,
|
||||||
"https://music.163.com/playlist?id=123a",
|
"https://music.163.com/playlist?id=123a",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
ParseLinkError,
|
||||||
|
parseLink,
|
||||||
|
"playlist://123456a",
|
||||||
|
)
|
||||||
|
|
||||||
def test_testExistTrackSource(self):
|
def test_testExistTrackSource(self):
|
||||||
resources = Path("tests/resource/util/testExistTrackSource")
|
resources = Path("tests/resource/util/testExistTrackSource")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user