remove url validators
This commit is contained in:
parent
87576a52c7
commit
e03b2f4754
3 changed files with 0 additions and 367 deletions
|
|
@ -1,204 +0,0 @@
|
||||||
from typing import Any
|
|
||||||
from typing import Optional
|
|
||||||
from urllib.parse import parse_qs
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
|
||||||
from ytdl_sub.validators.validators import StringValidator
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeVideoUrlValidator(StringValidator):
|
|
||||||
|
|
||||||
_expected_value_type_name = "Youtube video url"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_video_id(cls, url: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
https://youtu.be/SA2iWivDJiE
|
|
||||||
https://www.youtube.com/watch?v=_oPAwA_Udwc&feature=feedu
|
|
||||||
https://www.youtube.com/embed/SA2iWivDJiE
|
|
||||||
https://www.youtube.com/v/SA2iWivDJiE?version=3&hl=en_US
|
|
||||||
https://www.youtube.com/shorts/ucYmEqmlhFw
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
The video id if it is parsed correctly. None if parsing fails
|
|
||||||
"""
|
|
||||||
# If the url doesn't contain youtube or youtu.be, assume it is invalid
|
|
||||||
if "youtube.com" not in url and "youtu.be" not in url:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# If https:// is not present, urlparse will not work
|
|
||||||
if not url.startswith("https://"):
|
|
||||||
url = f"https://{url}"
|
|
||||||
|
|
||||||
query = urlparse(url)
|
|
||||||
if query.hostname in ("youtu.be", "www.youtu.be"):
|
|
||||||
return query.path[1:]
|
|
||||||
if query.hostname in ("youtube.com", "www.youtube.com"):
|
|
||||||
if query.path == "/watch":
|
|
||||||
parsed_q = parse_qs(query.query)
|
|
||||||
if "v" in parsed_q:
|
|
||||||
return parsed_q["v"][0]
|
|
||||||
if query.path.startswith(("/embed/", "/v/", "/shorts/")):
|
|
||||||
return query.path.split("/")[2]
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
|
||||||
super().__init__(name, value)
|
|
||||||
|
|
||||||
self._video_id = self._get_video_id(value)
|
|
||||||
if not self._video_id:
|
|
||||||
raise self._validation_exception(f"'{value}' is not a valid Youtube video url.")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def video_url(self) -> str:
|
|
||||||
"""
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Full video URL
|
|
||||||
"""
|
|
||||||
return f"https://youtube.com/watch?v={self._video_id}"
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubePlaylistUrlValidator(StringValidator):
|
|
||||||
|
|
||||||
_expected_value_type_name = "Youtube playlist url"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_playlist_id(cls, url: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
Examples:
|
|
||||||
- https://www.youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc
|
|
||||||
"""
|
|
||||||
# If the url doesn't contain youtube, assume it is invalid
|
|
||||||
if "youtube.com" not in url:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# If https:// is not present, urlparse will not work
|
|
||||||
if not url.startswith("https://"):
|
|
||||||
url = f"https://{url}"
|
|
||||||
|
|
||||||
query = urlparse(url)
|
|
||||||
if query.hostname in ("youtube.com", "www.youtube.com"):
|
|
||||||
if query.path == "/playlist":
|
|
||||||
parsed_q = parse_qs(query.query)
|
|
||||||
if "list" in parsed_q:
|
|
||||||
return parsed_q["list"][0]
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
|
||||||
super().__init__(name, value)
|
|
||||||
|
|
||||||
self._playlist_id = self._get_playlist_id(value)
|
|
||||||
if not self._playlist_id:
|
|
||||||
raise self._validation_exception(f"'{value}' is not a valid Youtube playlist url.")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def playlist_url(self) -> str:
|
|
||||||
"""
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Full playlist URL
|
|
||||||
"""
|
|
||||||
return f"https://youtube.com/playlist?list={self._playlist_id}"
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeChannelUrlValidator(StringValidator):
|
|
||||||
|
|
||||||
_expected_value_type_name = "Youtube channel url"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_channel_url(cls, url: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
Examples:
|
|
||||||
- https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw
|
|
||||||
- https://www.youtube.com/user/username
|
|
||||||
- https://www.youtube.com/c/channel_name
|
|
||||||
- https://youtube.com/channel_name
|
|
||||||
"""
|
|
||||||
# If the url doesn't contain youtube, assume it is invalid
|
|
||||||
if "youtube.com" not in url:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# If https:// is not present, urlparse will not work
|
|
||||||
if not url.startswith("https://"):
|
|
||||||
url = f"https://{url}"
|
|
||||||
|
|
||||||
query = urlparse(url)
|
|
||||||
if query.hostname in ("youtube.com", "www.youtube.com"):
|
|
||||||
query_path_split = query.path.split("/")
|
|
||||||
if any(query.path.startswith(qpath) for qpath in ("/channel/", "/user/", "/c/")):
|
|
||||||
# Ensure there is "/path/id".split('/') = ['', 'path', 'id'] at least three
|
|
||||||
if len(query_path_split) < 3 or len(query_path_split[2]) == 0:
|
|
||||||
return None
|
|
||||||
return f"https://youtube.com{query.path}"
|
|
||||||
if len(query_path_split) == 2 and len(query_path_split[1]) > 0:
|
|
||||||
# For channels that do not feature a 'c' or 'channel'
|
|
||||||
# Ensure length of channel string is at least one
|
|
||||||
return f"https://youtube.com{query.path}"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
|
||||||
super().__init__(name, value)
|
|
||||||
|
|
||||||
self._channel_url = self._get_channel_url(value)
|
|
||||||
if not self._channel_url:
|
|
||||||
raise self._validation_exception(f"'{value}' is not a valid Youtube channel url.")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def channel_url(self) -> str:
|
|
||||||
"""
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Full channel URL
|
|
||||||
"""
|
|
||||||
return self._channel_url
|
|
||||||
|
|
||||||
|
|
||||||
class SoundcloudUsernameUrlValidator(StringValidator):
|
|
||||||
|
|
||||||
_expected_value_type_name = "Soundcloud username url"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_channel_url(cls, url: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
Examples:
|
|
||||||
- https://www.soundcloud.com/artist_name
|
|
||||||
"""
|
|
||||||
# If the url doesn't contain soundcloud, assume it is invalid
|
|
||||||
if "soundcloud.com" not in url:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# If https:// is not present, urlparse will not work
|
|
||||||
if not url.startswith("https://"):
|
|
||||||
url = f"https://{url}"
|
|
||||||
|
|
||||||
query = urlparse(url)
|
|
||||||
if query.hostname in ("soundcloud.com", "www.soundcloud.com"):
|
|
||||||
if len(query.path) > 2: # /artist_name
|
|
||||||
username = query.path[1:].split("/")[0] # strip extra paths or slashes
|
|
||||||
username = username.split("?")[0] # strip extra arguments
|
|
||||||
return f"https://soundcloud.com/{username}"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __init__(self, name: str, value: Any):
|
|
||||||
super().__init__(name, value)
|
|
||||||
|
|
||||||
self._username_url = self._get_channel_url(value)
|
|
||||||
if not self._username_url:
|
|
||||||
raise self._validation_exception(f"'{value}' is not a valid Soundcloud username url.")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def username_url(self) -> str:
|
|
||||||
"""
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Full artist URL
|
|
||||||
"""
|
|
||||||
return self._username_url
|
|
||||||
|
|
@ -79,14 +79,6 @@ def channel_as_tv_show_config(working_directory) -> ConfigFile:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def soundcloud_discography_config(working_directory) -> ConfigFile:
|
|
||||||
return _load_config(
|
|
||||||
config_path="examples/soundcloud_discography_config.yaml",
|
|
||||||
working_directory=working_directory,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def music_audio_config(working_directory) -> ConfigFile:
|
def music_audio_config(working_directory) -> ConfigFile:
|
||||||
return _load_config(
|
return _load_config(
|
||||||
|
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
||||||
import re
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from ytdl_sub.utils.exceptions import ValidationException
|
|
||||||
from ytdl_sub.validators.url_validator import SoundcloudUsernameUrlValidator
|
|
||||||
from ytdl_sub.validators.url_validator import YoutubeChannelUrlValidator
|
|
||||||
from ytdl_sub.validators.url_validator import YoutubePlaylistUrlValidator
|
|
||||||
from ytdl_sub.validators.url_validator import YoutubeVideoUrlValidator
|
|
||||||
|
|
||||||
|
|
||||||
class TestYoutubeVideoUrlValidator:
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"url",
|
|
||||||
[
|
|
||||||
"youtu.be/dQw4w9WgXcQ",
|
|
||||||
"www.youtu.be/dQw4w9WgXcQ",
|
|
||||||
"https://youtu.be/dQw4w9WgXcQ",
|
|
||||||
"https://www.youtu.be/dQw4w9WgXcQ",
|
|
||||||
"https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=feedu",
|
|
||||||
"https://www.youtube.com/embed/dQw4w9WgXcQ",
|
|
||||||
"https://www.youtube.com/v/dQw4w9WgXcQ?version=3&hl=en_US",
|
|
||||||
"https://www.youtube.com/shorts/dQw4w9WgXcQ",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_youtube_video_url_validator_success(self, url):
|
|
||||||
video_url = YoutubeVideoUrlValidator(name="unit test", value=url).video_url
|
|
||||||
assert video_url == "https://youtube.com/watch?v=dQw4w9WgXcQ"
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"bad_url",
|
|
||||||
[
|
|
||||||
"utube.com/watch?v=sdfsadf",
|
|
||||||
"youtube.com/nope?v=sdfsadf",
|
|
||||||
"youtube.com",
|
|
||||||
"youtube.com/watch",
|
|
||||||
"youtube.com/watch?v=",
|
|
||||||
"youtu.be/",
|
|
||||||
"youtu.be",
|
|
||||||
"youtube.com/shorts",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_youtube_video_url_validator_fail(self, bad_url):
|
|
||||||
expected_error_msg = f"'{bad_url}' is not a valid Youtube video url."
|
|
||||||
with pytest.raises(ValidationException, match=re.escape(expected_error_msg)):
|
|
||||||
YoutubeVideoUrlValidator(name="unit test", value=bad_url)
|
|
||||||
|
|
||||||
|
|
||||||
class TestYoutubePlaylistUrlValidator:
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"url",
|
|
||||||
[
|
|
||||||
"youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc",
|
|
||||||
"www.youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc",
|
|
||||||
"https://youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc",
|
|
||||||
"https://www.youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_youtube_playlist_url_validator_success(self, url):
|
|
||||||
playlist_url = YoutubePlaylistUrlValidator(name="unit test", value=url).playlist_url
|
|
||||||
assert (
|
|
||||||
playlist_url == "https://youtube.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc"
|
|
||||||
)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"bad_url",
|
|
||||||
[
|
|
||||||
"youpoop.com/playlist?list=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc",
|
|
||||||
"youtube.com/playlistlist=PLlaN88a7y2_plecYoJxvRFTLHVbIVAOoc",
|
|
||||||
"youtube.com",
|
|
||||||
"youtube.com/playlist/asdfsdfsdf",
|
|
||||||
"youtube.com/playlist?list=",
|
|
||||||
"youtube.com/playlist",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_youtube_playlist_url_validator_fail(self, bad_url):
|
|
||||||
expected_error_msg = f"'{bad_url}' is not a valid Youtube playlist url."
|
|
||||||
with pytest.raises(ValidationException, match=re.escape(expected_error_msg)):
|
|
||||||
YoutubePlaylistUrlValidator(name="unit test", value=bad_url)
|
|
||||||
|
|
||||||
|
|
||||||
class TestYoutubeChannelUrlValidator:
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"url, expected_url",
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
"https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
"https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
"https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
"https://youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"https://www.youtube.com/c/RickastleyCoUkOfficial",
|
|
||||||
"https://youtube.com/c/RickastleyCoUkOfficial",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"https://www.youtube.com/user/videogamedunkey",
|
|
||||||
"https://youtube.com/user/videogamedunkey",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"https://youtube.com/extracredits",
|
|
||||||
"https://youtube.com/extracredits",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_youtube_channel_url_validator_success(self, url, expected_url):
|
|
||||||
channel_url = YoutubeChannelUrlValidator(name="unit test", value=url).channel_url
|
|
||||||
assert channel_url == expected_url
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"bad_url",
|
|
||||||
[
|
|
||||||
"www.youtube.com/cha/UCuAXFkgsw1L7xaCfnd5JJOw",
|
|
||||||
"www.nopetube.com/channel/asdfdsf",
|
|
||||||
"www.youtube.com/channel/",
|
|
||||||
"www.youtube.com/channell/asdfasdf",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_youtube_channel_url_validator_fail(self, bad_url):
|
|
||||||
expected_error_msg = f"'{bad_url}' is not a valid Youtube channel url."
|
|
||||||
with pytest.raises(ValidationException, match=re.escape(expected_error_msg)):
|
|
||||||
YoutubeChannelUrlValidator(name="unit test", value=bad_url)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSoundcloudUsernameUrlValidator:
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"url",
|
|
||||||
[
|
|
||||||
"soundcloud.com/poop",
|
|
||||||
"www.soundcloud.com/poop",
|
|
||||||
"https://soundcloud.com/poop",
|
|
||||||
"https://www.soundcloud.com/poop",
|
|
||||||
"https://www.soundcloud.com/poop/albums",
|
|
||||||
"https://www.soundcloud.com/poop?link=clipboard_share",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_soundcloud_artist_url_validator_success(self, url):
|
|
||||||
username_url = SoundcloudUsernameUrlValidator(name="unit test", value=url).username_url
|
|
||||||
assert username_url == "https://soundcloud.com/poop"
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("bad_url", ["soundcloud.com", "soundnope.lol", "soundcloud.comm/"])
|
|
||||||
def test_youtube_playlist_url_validator_fail(self, bad_url):
|
|
||||||
expected_error_msg = f"'{bad_url}' is not a valid Soundcloud username url."
|
|
||||||
with pytest.raises(ValidationException, match=re.escape(expected_error_msg)):
|
|
||||||
SoundcloudUsernameUrlValidator(name="unit test", value=bad_url)
|
|
||||||
Loading…
Reference in a new issue