stremio uses webdav now

This commit is contained in:
Pierre
2026-02-06 23:50:32 +01:00
parent 75c1b62a34
commit bbd5815f08
2 changed files with 71 additions and 85 deletions
+11
View File
@@ -14,6 +14,9 @@ from typing import List, Optional, Callable
from .provider import StorageProvider, VideoMetadata from .provider import StorageProvider, VideoMetadata
from .leader_client import LeaderClient, LeaderLockInfo from .leader_client import LeaderClient, LeaderLockInfo
# Import webdav_client to configure it after leader discovery
import webdav_client
try: try:
import redis import redis
REDIS_AVAILABLE = True REDIS_AVAILABLE = True
@@ -81,6 +84,10 @@ class LeaderStorage(StorageProvider):
info = self._leader_client.wait_for_leader(30000) info = self._leader_client.wait_for_leader(30000)
print(f"[LeaderStorage] Connecting to leader at {info.redis_url}...") print(f"[LeaderStorage] Connecting to leader at {info.redis_url}...")
self._connect_to_redis(info.redis_url) self._connect_to_redis(info.redis_url)
# Configure WebDAV client with leader's WebDAV URL
if info.webdav_url:
webdav_client.configure(info.webdav_url)
except TimeoutError as e: except TimeoutError as e:
print(f"[LeaderStorage] Failed to find leader: {e}") print(f"[LeaderStorage] Failed to find leader: {e}")
return return
@@ -122,6 +129,10 @@ class LeaderStorage(StorageProvider):
info = self._leader_client.wait_for_leader(30000) info = self._leader_client.wait_for_leader(30000)
print(f"[LeaderStorage] Reconnecting to leader at {info.redis_url}...") print(f"[LeaderStorage] Reconnecting to leader at {info.redis_url}...")
self._connect_to_redis(info.redis_url) self._connect_to_redis(info.redis_url)
# Reconfigure WebDAV client with new leader's WebDAV URL
if info.webdav_url:
webdav_client.configure(info.webdav_url)
except TimeoutError as e: except TimeoutError as e:
print(f"[LeaderStorage] Failed to reconnect: {e}") print(f"[LeaderStorage] Failed to reconnect: {e}")
+42 -67
View File
@@ -2,32 +2,44 @@
WebDAV Client for Meta-Stremio WebDAV Client for Meta-Stremio
Provides HTTP-based file access to meta-core's WebDAV server. Provides HTTP-based file access to meta-core's WebDAV server.
This allows meta-stremio to access files without direct volume mounts. Files are accessed exclusively via WebDAV - no local filesystem access.
Environment Variables: The WebDAV URL is discovered automatically from the leader via LeaderStorage.
- META_CORE_WEBDAV_URL: Base URL for WebDAV access (e.g., http://meta-core/webdav)
- FILES_PATH: Local files path prefix to strip when building WebDAV URLs (default: /files)
""" """
from __future__ import annotations from __future__ import annotations
import os import os
import re
from typing import Optional, Generator from typing import Optional, Generator
from urllib.parse import quote from urllib.parse import quote
import requests import requests
# Configuration # Configuration - set dynamically by LeaderStorage after leader discovery
WEBDAV_URL = os.environ.get('META_CORE_WEBDAV_URL', '').rstrip('/') WEBDAV_URL: str = ''
FILES_PATH = os.environ.get('FILES_PATH', '/files') FILES_PATH = os.environ.get('FILES_PATH', '/files')
# Check if WebDAV is enabled
WEBDAV_ENABLED = bool(WEBDAV_URL)
if WEBDAV_ENABLED: def configure(webdav_url: str) -> None:
print(f"[WebDAV] Using meta-core WebDAV at {WEBDAV_URL}") """
else: Configure the WebDAV client with a URL discovered from leader.
print("[WebDAV] Not configured, using direct filesystem access")
Called by LeaderStorage after connecting to the leader.
Args:
webdav_url: The WebDAV base URL (e.g., http://meta-core/webdav)
"""
global WEBDAV_URL
if not webdav_url:
return
WEBDAV_URL = webdav_url.rstrip('/')
print(f"[webdav-client] Configured: {WEBDAV_URL}")
def is_configured() -> bool:
"""Check if WebDAV client is configured."""
return bool(WEBDAV_URL)
def to_webdav_url(file_path: str) -> Optional[str]: def to_webdav_url(file_path: str) -> Optional[str]:
@@ -38,9 +50,9 @@ def to_webdav_url(file_path: str) -> Optional[str]:
file_path: Local file path (e.g., /files/watch/movie.mp4) file_path: Local file path (e.g., /files/watch/movie.mp4)
Returns: Returns:
WebDAV URL or None if WebDAV is not configured WebDAV URL or None if not configured
""" """
if not WEBDAV_ENABLED: if not WEBDAV_URL:
return None return None
# Strip the FILES_PATH prefix to get relative path # Strip the FILES_PATH prefix to get relative path
@@ -65,17 +77,13 @@ def get_file_size(file_path: str) -> Optional[int]:
Get file size via HTTP HEAD request to WebDAV. Get file size via HTTP HEAD request to WebDAV.
Args: Args:
file_path: Local file path file_path: File path
Returns: Returns:
File size in bytes, or None on error File size in bytes, or None on error
""" """
url = to_webdav_url(file_path) url = to_webdav_url(file_path)
if not url: if not url:
# Fall back to local filesystem
try:
return os.path.getsize(file_path)
except OSError:
return None return None
try: try:
@@ -84,7 +92,7 @@ def get_file_size(file_path: str) -> Optional[int]:
return int(response.headers.get('Content-Length', 0)) return int(response.headers.get('Content-Length', 0))
return None return None
except Exception as e: except Exception as e:
print(f"[WebDAV] HEAD error for {url}: {e}") print(f"[webdav-client] HEAD error for {url}: {e}")
return None return None
@@ -93,15 +101,14 @@ def file_exists(file_path: str) -> bool:
Check if file exists via HTTP HEAD request to WebDAV. Check if file exists via HTTP HEAD request to WebDAV.
Args: Args:
file_path: Local file path file_path: File path
Returns: Returns:
True if file exists True if file exists
""" """
url = to_webdav_url(file_path) url = to_webdav_url(file_path)
if not url: if not url:
# Fall back to local filesystem return False
return os.path.exists(file_path)
try: try:
response = requests.head(url, timeout=30) response = requests.head(url, timeout=30)
@@ -115,18 +122,13 @@ def read_file(file_path: str) -> Optional[bytes]:
Read entire file from WebDAV. Read entire file from WebDAV.
Args: Args:
file_path: Local file path file_path: File path
Returns: Returns:
File contents as bytes, or None on error File contents as bytes, or None on error
""" """
url = to_webdav_url(file_path) url = to_webdav_url(file_path)
if not url: if not url:
# Fall back to local filesystem
try:
with open(file_path, 'rb') as f:
return f.read()
except OSError:
return None return None
try: try:
@@ -135,7 +137,7 @@ def read_file(file_path: str) -> Optional[bytes]:
return response.content return response.content
return None return None
except Exception as e: except Exception as e:
print(f"[WebDAV] GET error for {url}: {e}") print(f"[webdav-client] GET error for {url}: {e}")
return None return None
@@ -144,7 +146,7 @@ def read_range(file_path: str, start: int, end: int) -> Optional[bytes]:
Read a byte range from a file via WebDAV. Read a byte range from a file via WebDAV.
Args: Args:
file_path: Local file path file_path: File path
start: Start byte offset (inclusive) start: Start byte offset (inclusive)
end: End byte offset (inclusive) end: End byte offset (inclusive)
@@ -153,12 +155,6 @@ def read_range(file_path: str, start: int, end: int) -> Optional[bytes]:
""" """
url = to_webdav_url(file_path) url = to_webdav_url(file_path)
if not url: if not url:
# Fall back to local filesystem
try:
with open(file_path, 'rb') as f:
f.seek(start)
return f.read(end - start + 1)
except OSError:
return None return None
try: try:
@@ -168,7 +164,7 @@ def read_range(file_path: str, start: int, end: int) -> Optional[bytes]:
return response.content return response.content
return None return None
except Exception as e: except Exception as e:
print(f"[WebDAV] Range GET error for {url}: {e}") print(f"[webdav-client] Range GET error for {url}: {e}")
return None return None
@@ -177,7 +173,7 @@ def stream_file(file_path: str, chunk_size: int = 64 * 1024) -> Generator[bytes,
Stream file content from WebDAV. Stream file content from WebDAV.
Args: Args:
file_path: Local file path file_path: File path
chunk_size: Size of chunks to yield chunk_size: Size of chunks to yield
Yields: Yields:
@@ -185,17 +181,8 @@ def stream_file(file_path: str, chunk_size: int = 64 * 1024) -> Generator[bytes,
""" """
url = to_webdav_url(file_path) url = to_webdav_url(file_path)
if not url: if not url:
# Fall back to local filesystem
try:
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
except OSError:
return return
else:
try: try:
response = requests.get(url, stream=True, timeout=300) response = requests.get(url, stream=True, timeout=300)
if response.status_code == 200: if response.status_code == 200:
@@ -203,7 +190,7 @@ def stream_file(file_path: str, chunk_size: int = 64 * 1024) -> Generator[bytes,
if chunk: if chunk:
yield chunk yield chunk
except Exception as e: except Exception as e:
print(f"[WebDAV] Stream error for {url}: {e}") print(f"[webdav-client] Stream error for {url}: {e}")
def stream_range(file_path: str, start: int, end: int, file_size: int, def stream_range(file_path: str, start: int, end: int, file_size: int,
@@ -212,10 +199,10 @@ def stream_range(file_path: str, start: int, end: int, file_size: int,
Stream a byte range from a file via WebDAV. Stream a byte range from a file via WebDAV.
Args: Args:
file_path: Local file path file_path: File path
start: Start byte offset (inclusive) start: Start byte offset (inclusive)
end: End byte offset (inclusive) end: End byte offset (inclusive)
file_size: Total file size (for local fallback) file_size: Total file size (unused, kept for API compatibility)
chunk_size: Size of chunks to yield chunk_size: Size of chunks to yield
Yields: Yields:
@@ -223,20 +210,8 @@ def stream_range(file_path: str, start: int, end: int, file_size: int,
""" """
url = to_webdav_url(file_path) url = to_webdav_url(file_path)
if not url: if not url:
# Fall back to local filesystem
try:
with open(file_path, 'rb') as f:
f.seek(start)
remaining = end - start + 1
while remaining > 0:
chunk = f.read(min(chunk_size, remaining))
if not chunk:
break
yield chunk
remaining -= len(chunk)
except OSError:
return return
else:
try: try:
headers = {'Range': f'bytes={start}-{end}'} headers = {'Range': f'bytes={start}-{end}'}
response = requests.get(url, headers=headers, stream=True, timeout=300) response = requests.get(url, headers=headers, stream=True, timeout=300)
@@ -245,4 +220,4 @@ def stream_range(file_path: str, start: int, end: int, file_size: int,
if chunk: if chunk:
yield chunk yield chunk
except Exception as e: except Exception as e:
print(f"[WebDAV] Stream range error for {url}: {e}") print(f"[webdav-client] Stream range error for {url}: {e}")