Soumik-404 commited on
Commit
47bd5bb
·
1 Parent(s): ff6b176
app/api/server.py CHANGED
@@ -11,7 +11,6 @@ from app.config import get_settings
11
  from app.core.database import pool_manager
12
  from app.core.logger import get_logger
13
  from app.api.v1.router import api_v1_router
14
- from app.api.verify import router as verify_router
15
 
16
  _logger = get_logger(__name__)
17
  _settings = get_settings()
@@ -66,7 +65,6 @@ def create_application() -> FastAPI:
66
  )
67
 
68
  app.include_router(api_v1_router, prefix="/api/v1")
69
- app.include_router(verify_router, prefix="/verify")
70
 
71
  @app.get("/", include_in_schema=False)
72
  async def root():
 
11
  from app.core.database import pool_manager
12
  from app.core.logger import get_logger
13
  from app.api.v1.router import api_v1_router
 
14
 
15
  _logger = get_logger(__name__)
16
  _settings = get_settings()
 
65
  )
66
 
67
  app.include_router(api_v1_router, prefix="/api/v1")
 
68
 
69
  @app.get("/", include_in_schema=False)
70
  async def root():
app/api/v1/router.py CHANGED
@@ -3,9 +3,11 @@ from __future__ import annotations
3
  from fastapi import APIRouter
4
 
5
  from app.api.v1 import batch, convert, database, system
 
6
 
7
  api_v1_router = APIRouter()
8
  api_v1_router.include_router(convert.router, tags=["Convert"])
9
  api_v1_router.include_router(batch.router, tags=["Batch"])
10
  api_v1_router.include_router(system.router, tags=["System"])
11
  api_v1_router.include_router(database.router, tags=["Database"])
 
 
3
  from fastapi import APIRouter
4
 
5
  from app.api.v1 import batch, convert, database, system
6
+ from app.api.verify import router as verify_router
7
 
8
  api_v1_router = APIRouter()
9
  api_v1_router.include_router(convert.router, tags=["Convert"])
10
  api_v1_router.include_router(batch.router, tags=["Batch"])
11
  api_v1_router.include_router(system.router, tags=["System"])
12
  api_v1_router.include_router(database.router, tags=["Database"])
13
+ api_v1_router.include_router(verify_router, prefix="/verify", tags=["Verify"])
app/config.py CHANGED
@@ -1,7 +1,6 @@
1
  from __future__ import annotations
2
 
3
  from functools import lru_cache
4
- from typing import Optional
5
 
6
  from pydantic_settings import BaseSettings, SettingsConfigDict
7
 
@@ -36,7 +35,6 @@ class Settings(BaseSettings):
36
 
37
  self_ping_url: str = "https://aetherbase-llm-ready-data.hf.space/ping"
38
  spacy_model: str = "en_core_web_sm"
39
-
40
  @property
41
  def max_upload_mb(self) -> int:
42
  return self.max_upload_bytes // (1024 * 1024)
 
1
  from __future__ import annotations
2
 
3
  from functools import lru_cache
 
4
 
5
  from pydantic_settings import BaseSettings, SettingsConfigDict
6
 
 
35
 
36
  self_ping_url: str = "https://aetherbase-llm-ready-data.hf.space/ping"
37
  spacy_model: str = "en_core_web_sm"
 
38
  @property
39
  def max_upload_mb(self) -> int:
40
  return self.max_upload_bytes // (1024 * 1024)
app/services/converter_service.py CHANGED
@@ -6,44 +6,27 @@ import mimetypes
6
  import re
7
  import time
8
  from pathlib import Path
9
- from typing import Optional
10
- from urllib.parse import urlparse, parse_qs
11
 
12
  import httpx
13
  from markitdown import MarkItDown
14
 
15
- from app.config import get_settings
16
  from app.core.constants import IMAGE_EXTENSIONS, IMAGE_MIME_PREFIXES
17
  from app.core.logger import get_logger
18
  from app.models.domain import ConversionError, ConversionResult
19
  from app.services.ocr_service import ocr_image, ocr_pdf
20
 
21
  _logger = get_logger(__name__)
22
- _settings = get_settings()
23
 
24
- _YOUTUBE_URL_PATTERN = re.compile(
25
- r"(?:https?://)?"
26
- r"(?:www\.|m\.)?"
27
- r"(?:youtube\.com/(?:watch\?v=|embed/|v/|shorts/|live/)|youtu\.be/)"
28
- r"([\w-]{11})"
29
- )
30
 
31
- _YOUTUBE_DOMAINS = {"youtube.com", "www.youtube.com", "m.youtube.com", "youtu.be", "www.youtu.be"}
32
-
33
-
34
- def _is_youtube_url(url: str) -> bool:
35
- return _YOUTUBE_URL_PATTERN.match(url) is not None
36
-
37
-
38
- def _extract_youtube_video_id(url: str) -> str | None:
39
- match = _YOUTUBE_URL_PATTERN.match(url)
40
  if match:
41
  return match.group(1)
42
- parsed = urlparse(url)
43
- if parsed.hostname in _YOUTUBE_DOMAINS:
44
- qs = parse_qs(parsed.query)
45
- return qs.get("v", [None])[0]
46
- return None
47
 
48
 
49
  def _fetch_youtube_oembed(video_id: str) -> dict | None:
@@ -59,18 +42,49 @@ def _fetch_youtube_oembed(video_id: str) -> dict | None:
59
 
60
  def _fetch_youtube_transcript(video_id: str) -> str | None:
61
  try:
 
 
 
62
  from youtube_transcript_api import YouTubeTranscriptApi
63
- api = YouTubeTranscriptApi()
64
- transcript = api.fetch(video_id, languages=["en"])
65
- return " ".join(part.text for part in transcript)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  except Exception as exc:
67
  _logger.warning("YouTube transcript failed for %s: %s", video_id, exc)
68
- return None
69
 
70
 
71
  def _convert_youtube(url: str) -> ConversionResult | None:
72
- video_id = _extract_youtube_video_id(url)
73
- if not video_id:
 
74
  return None
75
 
76
  oembed = _fetch_youtube_oembed(video_id)
@@ -179,7 +193,11 @@ class ConverterService:
179
 
180
  start = time.perf_counter()
181
 
182
- if _is_youtube_url(url):
 
 
 
 
183
  result = _convert_youtube(url)
184
  if result is not None:
185
  result = ConversionResult(
 
6
  import re
7
  import time
8
  from pathlib import Path
9
+ from urllib.parse import urlparse
 
10
 
11
  import httpx
12
  from markitdown import MarkItDown
13
 
 
14
  from app.core.constants import IMAGE_EXTENSIONS, IMAGE_MIME_PREFIXES
15
  from app.core.logger import get_logger
16
  from app.models.domain import ConversionError, ConversionResult
17
  from app.services.ocr_service import ocr_image, ocr_pdf
18
 
19
  _logger = get_logger(__name__)
 
20
 
 
 
 
 
 
 
21
 
22
+ def _extract_youtube_video_id(url_or_id: str) -> str:
23
+ if len(url_or_id) == 11 and not url_or_id.startswith("http"):
24
+ return url_or_id
25
+ pattern = r"(?:v=|\/)([0-9A-Za-z_-]{11}).*"
26
+ match = re.search(pattern, url_or_id)
 
 
 
 
27
  if match:
28
  return match.group(1)
29
+ raise ValueError(f"Could not extract a valid YouTube ID from: {url_or_id}")
 
 
 
 
30
 
31
 
32
  def _fetch_youtube_oembed(video_id: str) -> dict | None:
 
42
 
43
  def _fetch_youtube_transcript(video_id: str) -> str | None:
44
  try:
45
+ import json as _json
46
+ import os as _os
47
+
48
  from youtube_transcript_api import YouTubeTranscriptApi
49
+ from youtube_transcript_api.formatters import TextFormatter
50
+ from youtube_transcript_api._errors import (
51
+ TranscriptsDisabled,
52
+ NoTranscriptFound,
53
+ VideoUnavailable,
54
+ )
55
+
56
+ kwargs: dict = {}
57
+ cookies_raw = _os.environ.get("YOUTUBE_COOKIES", "").strip()
58
+ if cookies_raw:
59
+ try:
60
+ import requests as _requests
61
+ session = _requests.Session()
62
+ session.cookies.update(_json.loads(cookies_raw))
63
+ kwargs["http_client"] = session
64
+ except Exception as exc:
65
+ _logger.warning("Failed to apply YOUTUBE_COOKIES: %s", exc)
66
+
67
+ ytt_api = YouTubeTranscriptApi(**kwargs)
68
+ transcript_data = ytt_api.fetch(video_id, languages=["en"])
69
+ formatter = TextFormatter()
70
+ return formatter.format_transcript(transcript_data)
71
+ except TranscriptsDisabled:
72
+ _logger.warning("Transcripts disabled for video %s", video_id)
73
+ except NoTranscriptFound:
74
+ _logger.warning("No transcript found for video %s in language 'en'", video_id)
75
+ except VideoUnavailable:
76
+ _logger.warning("Video %s is unavailable, deleted, or private", video_id)
77
+ except ValueError as ve:
78
+ _logger.warning("Invalid video ID %s: %s", video_id, ve)
79
  except Exception as exc:
80
  _logger.warning("YouTube transcript failed for %s: %s", video_id, exc)
81
+ return None
82
 
83
 
84
  def _convert_youtube(url: str) -> ConversionResult | None:
85
+ try:
86
+ video_id = _extract_youtube_video_id(url)
87
+ except ValueError:
88
  return None
89
 
90
  oembed = _fetch_youtube_oembed(video_id)
 
193
 
194
  start = time.perf_counter()
195
 
196
+ try:
197
+ _extract_youtube_video_id(url)
198
+ except ValueError:
199
+ pass
200
+ else:
201
  result = _convert_youtube(url)
202
  if result is not None:
203
  result = ConversionResult(