mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-31 23:15:51 -08:00
Compare commits
9 Commits
minor-fixe
...
v3.3.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8837c542f2 | ||
|
|
eb8c443775 | ||
|
|
b052ee8300 | ||
|
|
f684f561df | ||
|
|
7ed45ce07e | ||
|
|
10d1211388 | ||
|
|
efa6f4d142 | ||
|
|
0ca63dd765 | ||
|
|
b62d878a0e |
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
@@ -1,9 +1,9 @@
|
||||
name: Mark Stale Issues and Pull Requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Runs every day at 6:30 UTC
|
||||
- cron: "30 6 * * *"
|
||||
# schedule:
|
||||
# Runs every day at 6:30 UTC
|
||||
# - cron: "30 6 * * *"
|
||||
# Allows you to run this workflow manually from the Actions tab for testing
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ Get up and running in three simple steps:
|
||||
```bash
|
||||
viu anilist auth
|
||||
```
|
||||
This will open your browser. Authorize the app and paste the obtained token back into the terminal.
|
||||
This will open your browser. Authorize the app and paste the obtained token back into the terminal. Alternatively, you can pass the token directly as an argument, or provide a path to a text file containing the token.
|
||||
|
||||
2. **Launch the Interactive TUI:**
|
||||
```bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "viu-media"
|
||||
version = "3.3.5"
|
||||
version = "3.3.6"
|
||||
description = "A browser anime site experience from the terminal"
|
||||
license = "UNLICENSE"
|
||||
readme = "README.md"
|
||||
|
||||
284
tests/cli/commands/anilist/commands/test_auth.py
Normal file
284
tests/cli/commands/anilist/commands/test_auth.py
Normal file
@@ -0,0 +1,284 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from viu_media.cli.commands.anilist.commands.auth import auth
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def runner():
|
||||
return CliRunner()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config():
|
||||
config = MagicMock()
|
||||
config.user.interactive = True
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_auth_service():
|
||||
with patch("viu_media.cli.service.auth.AuthService") as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_feedback_service():
|
||||
with patch("viu_media.cli.service.feedback.FeedbackService") as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_selector():
|
||||
with patch("viu_media.libs.selectors.selector.create_selector") as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_api_client():
|
||||
with patch("viu_media.libs.media_api.api.create_api_client") as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_webbrowser():
|
||||
with patch("viu_media.cli.commands.anilist.commands.auth.webbrowser") as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
def test_auth_with_token_argument(
|
||||
runner,
|
||||
mock_config,
|
||||
mock_auth_service,
|
||||
mock_feedback_service,
|
||||
mock_selector,
|
||||
mock_api_client,
|
||||
):
|
||||
"""Test 'viu anilist auth <token>'."""
|
||||
api_client_instance = mock_api_client.return_value
|
||||
profile_mock = MagicMock()
|
||||
profile_mock.name = "testuser"
|
||||
api_client_instance.authenticate.return_value = profile_mock
|
||||
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_service_instance.get_auth.return_value = None
|
||||
|
||||
result = runner.invoke(auth, ["test_token"], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
mock_api_client.assert_called_with("anilist", mock_config)
|
||||
api_client_instance.authenticate.assert_called_with("test_token")
|
||||
auth_service_instance.save_user_profile.assert_called_with(
|
||||
profile_mock, "test_token"
|
||||
)
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.info.assert_called_with("Successfully logged in as testuser! ✨")
|
||||
|
||||
|
||||
def test_auth_with_token_file(
|
||||
runner,
|
||||
mock_config,
|
||||
mock_auth_service,
|
||||
mock_feedback_service,
|
||||
mock_selector,
|
||||
mock_api_client,
|
||||
tmp_path,
|
||||
):
|
||||
"""Test 'viu anilist auth <path/to/token.txt>'."""
|
||||
token_file = tmp_path / "token.txt"
|
||||
token_file.write_text("file_token")
|
||||
|
||||
api_client_instance = mock_api_client.return_value
|
||||
profile_mock = MagicMock()
|
||||
profile_mock.name = "testuser"
|
||||
api_client_instance.authenticate.return_value = profile_mock
|
||||
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_service_instance.get_auth.return_value = None
|
||||
|
||||
result = runner.invoke(auth, [str(token_file)], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
mock_api_client.assert_called_with("anilist", mock_config)
|
||||
api_client_instance.authenticate.assert_called_with("file_token")
|
||||
auth_service_instance.save_user_profile.assert_called_with(
|
||||
profile_mock, "file_token"
|
||||
)
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.info.assert_called_with("Successfully logged in as testuser! ✨")
|
||||
|
||||
|
||||
def test_auth_with_empty_token_file(
|
||||
runner,
|
||||
mock_config,
|
||||
mock_auth_service,
|
||||
mock_feedback_service,
|
||||
mock_selector,
|
||||
mock_api_client,
|
||||
tmp_path,
|
||||
):
|
||||
"""Test 'viu anilist auth' with an empty token file."""
|
||||
token_file = tmp_path / "token.txt"
|
||||
token_file.write_text("")
|
||||
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_service_instance.get_auth.return_value = None
|
||||
|
||||
result = runner.invoke(auth, [str(token_file)], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.error.assert_called_with(f"Token file is empty: {token_file}")
|
||||
|
||||
|
||||
def test_auth_interactive(
|
||||
runner,
|
||||
mock_config,
|
||||
mock_auth_service,
|
||||
mock_feedback_service,
|
||||
mock_selector,
|
||||
mock_api_client,
|
||||
mock_webbrowser,
|
||||
):
|
||||
"""Test 'viu anilist auth' interactive mode."""
|
||||
mock_webbrowser.open.return_value = True
|
||||
|
||||
selector_instance = mock_selector.return_value
|
||||
selector_instance.ask.return_value = "interactive_token"
|
||||
|
||||
api_client_instance = mock_api_client.return_value
|
||||
profile_mock = MagicMock()
|
||||
profile_mock.name = "testuser"
|
||||
api_client_instance.authenticate.return_value = profile_mock
|
||||
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_service_instance.get_auth.return_value = None
|
||||
|
||||
result = runner.invoke(auth, [], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
selector_instance.ask.assert_called_with("Enter your AniList Access Token")
|
||||
api_client_instance.authenticate.assert_called_with("interactive_token")
|
||||
auth_service_instance.save_user_profile.assert_called_with(
|
||||
profile_mock, "interactive_token"
|
||||
)
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.info.assert_called_with("Successfully logged in as testuser! ✨")
|
||||
|
||||
|
||||
def test_auth_status_logged_in(
|
||||
runner, mock_config, mock_auth_service, mock_feedback_service
|
||||
):
|
||||
"""Test 'viu anilist auth --status' when logged in."""
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
user_data_mock = MagicMock()
|
||||
user_data_mock.user_profile = "testuser"
|
||||
auth_service_instance.get_auth.return_value = user_data_mock
|
||||
|
||||
result = runner.invoke(auth, ["--status"], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.info.assert_called_with("Logged in as: testuser")
|
||||
|
||||
|
||||
def test_auth_status_logged_out(
|
||||
runner, mock_config, mock_auth_service, mock_feedback_service
|
||||
):
|
||||
"""Test 'viu anilist auth --status' when logged out."""
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_service_instance.get_auth.return_value = None
|
||||
|
||||
result = runner.invoke(auth, ["--status"], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.error.assert_called_with("Not logged in.")
|
||||
|
||||
|
||||
def test_auth_logout(
|
||||
runner, mock_config, mock_auth_service, mock_feedback_service, mock_selector
|
||||
):
|
||||
"""Test 'viu anilist auth --logout'."""
|
||||
selector_instance = mock_selector.return_value
|
||||
selector_instance.confirm.return_value = True
|
||||
|
||||
result = runner.invoke(auth, ["--logout"], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_service_instance.clear_user_profile.assert_called_once()
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.info.assert_called_with("You have been logged out.")
|
||||
|
||||
|
||||
def test_auth_logout_cancel(
|
||||
runner, mock_config, mock_auth_service, mock_feedback_service, mock_selector
|
||||
):
|
||||
"""Test 'viu anilist auth --logout' when user cancels."""
|
||||
selector_instance = mock_selector.return_value
|
||||
selector_instance.confirm.return_value = False
|
||||
|
||||
result = runner.invoke(auth, ["--logout"], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_service_instance.clear_user_profile.assert_not_called()
|
||||
|
||||
|
||||
def test_auth_already_logged_in_relogin_yes(
|
||||
runner,
|
||||
mock_config,
|
||||
mock_auth_service,
|
||||
mock_feedback_service,
|
||||
mock_selector,
|
||||
mock_api_client,
|
||||
):
|
||||
"""Test 'viu anilist auth' when already logged in and user chooses to relogin."""
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_profile_mock = MagicMock()
|
||||
auth_profile_mock.user_profile.name = "testuser"
|
||||
auth_service_instance.get_auth.return_value = auth_profile_mock
|
||||
|
||||
selector_instance = mock_selector.return_value
|
||||
selector_instance.confirm.return_value = True
|
||||
selector_instance.ask.return_value = "new_token"
|
||||
|
||||
api_client_instance = mock_api_client.return_value
|
||||
new_profile_mock = MagicMock()
|
||||
new_profile_mock.name = "newuser"
|
||||
api_client_instance.authenticate.return_value = new_profile_mock
|
||||
|
||||
result = runner.invoke(auth, [], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
selector_instance.confirm.assert_called_with(
|
||||
"You are already logged in as testuser. Would you like to relogin"
|
||||
)
|
||||
auth_service_instance.save_user_profile.assert_called_with(
|
||||
new_profile_mock, "new_token"
|
||||
)
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.info.assert_called_with("Successfully logged in as newuser! ✨")
|
||||
|
||||
|
||||
def test_auth_already_logged_in_relogin_no(
|
||||
runner, mock_config, mock_auth_service, mock_feedback_service, mock_selector
|
||||
):
|
||||
"""Test 'viu anilist auth' when already logged in and user chooses not to relogin."""
|
||||
auth_service_instance = mock_auth_service.return_value
|
||||
auth_profile_mock = MagicMock()
|
||||
auth_profile_mock.user_profile.name = "testuser"
|
||||
auth_service_instance.get_auth.return_value = auth_profile_mock
|
||||
|
||||
selector_instance = mock_selector.return_value
|
||||
selector_instance.confirm.return_value = False
|
||||
|
||||
result = runner.invoke(auth, [], obj=mock_config)
|
||||
|
||||
assert result.exit_code == 0
|
||||
auth_service_instance.save_user_profile.assert_not_called()
|
||||
feedback_instance = mock_feedback_service.return_value
|
||||
feedback_instance.info.assert_not_called()
|
||||
0
tests/libs/__init__.py
Normal file
0
tests/libs/__init__.py
Normal file
0
tests/libs/media_api/__init__.py
Normal file
0
tests/libs/media_api/__init__.py
Normal file
0
tests/libs/media_api/anilist/__init__.py
Normal file
0
tests/libs/media_api/anilist/__init__.py
Normal file
54
tests/libs/media_api/anilist/test_mapper.py
Normal file
54
tests/libs/media_api/anilist/test_mapper.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from typing import Any
|
||||
|
||||
from viu_media.libs.media_api.anilist.mapper import to_generic_user_profile
|
||||
from viu_media.libs.media_api.anilist.types import AnilistViewerData
|
||||
from viu_media.libs.media_api.types import UserProfile
|
||||
|
||||
|
||||
def test_to_generic_user_profile_success():
|
||||
data: AnilistViewerData = {
|
||||
"data": {
|
||||
"Viewer": {
|
||||
"id": 123,
|
||||
"name": "testuser",
|
||||
"avatar": {
|
||||
"large": "https://example.com/avatar.png",
|
||||
"medium": "https://example.com/avatar_medium.png",
|
||||
"extraLarge": "https://example.com/avatar_extraLarge.png",
|
||||
"small": "https://example.com/avatar_small.png",
|
||||
},
|
||||
"bannerImage": "https://example.com/banner.png",
|
||||
"token": "test_token",
|
||||
}
|
||||
}
|
||||
}
|
||||
profile = to_generic_user_profile(data)
|
||||
assert isinstance(profile, UserProfile)
|
||||
assert profile.id == 123
|
||||
assert profile.name == "testuser"
|
||||
assert profile.avatar_url == "https://example.com/avatar.png"
|
||||
assert profile.banner_url == "https://example.com/banner.png"
|
||||
|
||||
|
||||
def test_to_generic_user_profile_data_none():
|
||||
data: Any = {"data": None}
|
||||
profile = to_generic_user_profile(data)
|
||||
assert profile is None
|
||||
|
||||
|
||||
def test_to_generic_user_profile_no_data_key():
|
||||
data: Any = {"errors": [{"message": "Invalid token"}]}
|
||||
profile = to_generic_user_profile(data)
|
||||
assert profile is None
|
||||
|
||||
|
||||
def test_to_generic_user_profile_no_viewer_key():
|
||||
data: Any = {"data": {"Page": {}}}
|
||||
profile = to_generic_user_profile(data)
|
||||
assert profile is None
|
||||
|
||||
|
||||
def test_to_generic_user_profile_viewer_none():
|
||||
data: Any = {"data": {"Viewer": None}}
|
||||
profile = to_generic_user_profile(data)
|
||||
assert profile is None
|
||||
2
uv.lock
generated
2
uv.lock
generated
@@ -3743,7 +3743,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "viu-media"
|
||||
version = "3.3.5"
|
||||
version = "3.3.6"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
|
||||
@@ -189,7 +189,7 @@ You can disable this message by turning off the welcome_screen option in the con
|
||||
):
|
||||
import subprocess
|
||||
|
||||
_cli_cmd_name="viu" if not shutil.which("viu-media") else "viu-media"
|
||||
_cli_cmd_name = "viu" if not shutil.which("viu-media") else "viu-media"
|
||||
cmd = [_cli_cmd_name, "config", "--update"]
|
||||
print(f"running '{' '.join(cmd)}'...")
|
||||
subprocess.run(cmd)
|
||||
|
||||
@@ -1,25 +1,72 @@
|
||||
import click
|
||||
import webbrowser
|
||||
from pathlib import Path
|
||||
import click
|
||||
|
||||
from .....core.config.model import AppConfig
|
||||
|
||||
|
||||
def _get_token(feedback, selector, token_input: str | None) -> str | None:
|
||||
"""
|
||||
Retrieves the authentication token from a file path, a direct string, or an interactive prompt.
|
||||
"""
|
||||
if token_input:
|
||||
path = Path(token_input)
|
||||
if path.is_file():
|
||||
try:
|
||||
token = path.read_text().strip()
|
||||
if not token:
|
||||
feedback.error(f"Token file is empty: {path}")
|
||||
return None
|
||||
return token
|
||||
except Exception as e:
|
||||
feedback.error(f"Error reading token from file: {e}")
|
||||
return None
|
||||
return token_input
|
||||
|
||||
from .....core.constants import ANILIST_AUTH
|
||||
|
||||
open_success = webbrowser.open(ANILIST_AUTH, new=2)
|
||||
if open_success:
|
||||
feedback.info("Your browser has been opened to obtain an AniList token.")
|
||||
feedback.info(
|
||||
f"Or you can visit the site manually [magenta][link={ANILIST_AUTH}]here[/link][/magenta]."
|
||||
)
|
||||
else:
|
||||
feedback.warning(
|
||||
f"Failed to open the browser. Please visit the site manually [magenta][link={ANILIST_AUTH}]here[/link][/magenta]."
|
||||
)
|
||||
feedback.info(
|
||||
"After authorizing, copy the token from the address bar and paste it below."
|
||||
)
|
||||
return selector.ask("Enter your AniList Access Token")
|
||||
|
||||
|
||||
@click.command(help="Login to your AniList account to enable progress tracking.")
|
||||
@click.option("--status", "-s", is_flag=True, help="Check current login status.")
|
||||
@click.option("--logout", "-l", is_flag=True, help="Log out and erase credentials.")
|
||||
@click.argument("token_input", required=False, type=str)
|
||||
@click.pass_obj
|
||||
def auth(config: AppConfig, status: bool, logout: bool):
|
||||
"""Handles user authentication and credential management."""
|
||||
from .....core.constants import ANILIST_AUTH
|
||||
def auth(config: AppConfig, status: bool, logout: bool, token_input: str | None):
|
||||
"""
|
||||
Handles user authentication and credential management.
|
||||
|
||||
This command allows you to log in to your AniList account to enable
|
||||
progress tracking and other features.
|
||||
|
||||
You can provide your authentication token in three ways:
|
||||
1. Interactively: Run the command without arguments to open a browser
|
||||
and be prompted to paste the token.
|
||||
2. As an argument: Pass the token string directly to the command.
|
||||
$ viu anilist auth "your_token_here"
|
||||
3. As a file: Pass the path to a text file containing the token.
|
||||
$ viu anilist auth /path/to/token.txt
|
||||
"""
|
||||
from .....libs.media_api.api import create_api_client
|
||||
from .....libs.selectors.selector import create_selector
|
||||
from ....service.auth import AuthService
|
||||
from ....service.feedback import FeedbackService
|
||||
|
||||
auth_service = AuthService("anilist")
|
||||
feedback = FeedbackService(config)
|
||||
selector = create_selector(config)
|
||||
feedback.clear_console()
|
||||
|
||||
if status:
|
||||
user_data = auth_service.get_auth()
|
||||
@@ -29,6 +76,11 @@ def auth(config: AppConfig, status: bool, logout: bool):
|
||||
feedback.error("Not logged in.")
|
||||
return
|
||||
|
||||
from .....libs.selectors.selector import create_selector
|
||||
|
||||
selector = create_selector(config)
|
||||
feedback.clear_console()
|
||||
|
||||
if logout:
|
||||
if selector.confirm("Are you sure you want to log out and erase your token?"):
|
||||
auth_service.clear_user_profile()
|
||||
@@ -40,27 +92,14 @@ def auth(config: AppConfig, status: bool, logout: bool):
|
||||
f"You are already logged in as {auth_profile.user_profile.name}.Would you like to relogin"
|
||||
):
|
||||
return
|
||||
api_client = create_api_client("anilist", config)
|
||||
token = _get_token(feedback, selector, token_input)
|
||||
|
||||
open_success = webbrowser.open(ANILIST_AUTH, new=2)
|
||||
if open_success:
|
||||
feedback.info("Your browser has been opened to obtain an AniList token.")
|
||||
feedback.info(
|
||||
f"or you can visit the site manually [magenta][link={ANILIST_AUTH}]here[/link][/magenta]."
|
||||
)
|
||||
else:
|
||||
feedback.warning(
|
||||
f"Failed to open the browser. Please visit the site manually [magenta][link={ANILIST_AUTH}]here[/link][/magenta]."
|
||||
)
|
||||
feedback.info(
|
||||
"After authorizing, copy the token from the address bar and paste it below."
|
||||
)
|
||||
|
||||
token = selector.ask("Enter your AniList Access Token")
|
||||
if not token:
|
||||
feedback.error("Login cancelled.")
|
||||
if not token_input:
|
||||
feedback.error("Login cancelled.")
|
||||
return
|
||||
|
||||
api_client = create_api_client("anilist", config)
|
||||
# Use the API client to validate the token and get profile info
|
||||
profile = api_client.authenticate(token.strip())
|
||||
|
||||
|
||||
@@ -323,7 +323,14 @@ def to_generic_user_list_result(data: AnilistMediaLists) -> Optional[MediaSearch
|
||||
def to_generic_user_profile(data: AnilistViewerData) -> Optional[UserProfile]:
|
||||
"""Maps a raw AniList viewer response to a generic UserProfile."""
|
||||
|
||||
viewer_data: Optional[AnilistCurrentlyLoggedInUser] = data["data"]["Viewer"]
|
||||
data_node = data.get("data")
|
||||
if not data_node:
|
||||
return None
|
||||
|
||||
viewer_data: Optional[AnilistCurrentlyLoggedInUser] = data_node.get("Viewer")
|
||||
|
||||
if not viewer_data:
|
||||
return None
|
||||
|
||||
return UserProfile(
|
||||
id=viewer_data["id"],
|
||||
|
||||
@@ -52,7 +52,7 @@ class MpvPlayer(BasePlayer):
|
||||
if TORRENT_REGEX.match(params.url) and detect.is_running_in_termux():
|
||||
raise ViuError("Unable to play torrents on termux")
|
||||
elif params.syncplay and detect.is_running_in_termux():
|
||||
raise ViuError("Unable to play torrents on termux")
|
||||
raise ViuError("Unable to play with syncplay on termux")
|
||||
elif detect.is_running_in_termux():
|
||||
return self._play_on_mobile(params)
|
||||
else:
|
||||
|
||||
@@ -46,10 +46,11 @@ class VlcPlayer(BasePlayer):
|
||||
Returns:
|
||||
PlayerResult: Information about the playback session.
|
||||
"""
|
||||
if not self.executable:
|
||||
raise ViuError("VLC executable not found in PATH.")
|
||||
|
||||
if TORRENT_REGEX.match(params.url) and detect.is_running_in_termux():
|
||||
raise ViuError("Unable to play torrents on termux")
|
||||
elif params.syncplay and detect.is_running_in_termux():
|
||||
raise ViuError("Unable to play with syncplay on termux")
|
||||
elif detect.is_running_in_termux():
|
||||
return self._play_on_mobile(params)
|
||||
else:
|
||||
return self._play_on_desktop(params)
|
||||
@@ -116,6 +117,9 @@ class VlcPlayer(BasePlayer):
|
||||
Returns:
|
||||
PlayerResult: Information about the playback session.
|
||||
"""
|
||||
if not self.executable:
|
||||
raise ViuError("VLC executable not found in PATH.")
|
||||
|
||||
if TORRENT_REGEX.search(params.url):
|
||||
return self._stream_on_desktop_with_webtorrent_cli(params)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ SERVER_HEADERS = {
|
||||
"Accept-Encoding": "Utf-8",
|
||||
"DNT": "1",
|
||||
"Connection": "keep-alive",
|
||||
"Referer": ANIMEPAHE_BASE + '/',
|
||||
"Referer": ANIMEPAHE_BASE + "/",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"Sec-Fetch-Dest": "iframe",
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
@@ -44,7 +44,7 @@ STREAM_HEADERS = {
|
||||
"Origin": CDN_PROVIDER_BASE,
|
||||
"Sec-GPC": "1",
|
||||
"Connection": "keep-alive",
|
||||
"Referer": CDN_PROVIDER_BASE + '/',
|
||||
"Referer": CDN_PROVIDER_BASE + "/",
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "cross-site",
|
||||
|
||||
@@ -98,4 +98,6 @@ def map_to_server(
|
||||
)
|
||||
for link in stream_links
|
||||
]
|
||||
return Server(name="kwik", links=links, episode_title=episode.title, headers=headers)
|
||||
return Server(
|
||||
name="kwik", links=links, episode_title=episode.title, headers=headers
|
||||
)
|
||||
|
||||
@@ -184,9 +184,11 @@ class AnimePahe(BaseAnimeProvider):
|
||||
headers = {
|
||||
"User-Agent": self.client.headers["User-Agent"],
|
||||
"Host": stream_host or CDN_PROVIDER,
|
||||
**STREAM_HEADERS
|
||||
**STREAM_HEADERS,
|
||||
}
|
||||
yield map_to_server(episode, translation_type, stream_links, headers=headers)
|
||||
yield map_to_server(
|
||||
episode, translation_type, stream_links, headers=headers
|
||||
)
|
||||
|
||||
@lru_cache()
|
||||
def _get_episode_info(
|
||||
|
||||
Reference in New Issue
Block a user