Rthur2003 commited on
Commit
8de7693
·
1 Parent(s): 906c392

feat: youTube API servisi ve backend routing için video interaction ve comment management eklendi

Browse files
app/main.py CHANGED
@@ -86,10 +86,9 @@ _origins = _load_origins()
86
  app.add_middleware(
87
  CORSMiddleware,
88
  allow_origins=_origins,
89
- # credentials=True is only safe with an explicit origin list, not wildcard
90
  allow_credentials="*" not in _origins,
91
- allow_methods=["*"],
92
- allow_headers=["*"],
93
  )
94
 
95
  app.include_router(health_router)
 
86
  app.add_middleware(
87
  CORSMiddleware,
88
  allow_origins=_origins,
 
89
  allow_credentials="*" not in _origins,
90
+ allow_methods=["GET", "POST", "OPTIONS"],
91
+ allow_headers=["Content-Type", "Accept"],
92
  )
93
 
94
  app.include_router(health_router)
app/routes/analyze.py CHANGED
@@ -62,7 +62,7 @@ async def _analyze_rate_limit(request: Request) -> None:
62
  if len(_analyze_rate_store) > _ANALYZE_MAX_IPS:
63
  stale = [
64
  ip for ip, ts in _analyze_rate_store.items()
65
- if all(t <= cutoff for t in ts)
66
  ]
67
  for ip in stale:
68
  del _analyze_rate_store[ip]
 
62
  if len(_analyze_rate_store) > _ANALYZE_MAX_IPS:
63
  stale = [
64
  ip for ip, ts in _analyze_rate_store.items()
65
+ if not ts or all(t <= cutoff for t in ts)
66
  ]
67
  for ip in stale:
68
  del _analyze_rate_store[ip]
app/routes/commend/router.py CHANGED
@@ -131,7 +131,10 @@ def _evict_stale_ips() -> None:
131
  if len(_rate_limit_store) <= _MAX_TRACKED_IPS:
132
  return
133
  cutoff = time.time() - _RATE_LIMIT_WINDOW
134
- stale_keys = [ip for ip, ts in _rate_limit_store.items() if all(t <= cutoff for t in ts)]
 
 
 
135
  for key in stale_keys:
136
  del _rate_limit_store[key]
137
 
 
131
  if len(_rate_limit_store) <= _MAX_TRACKED_IPS:
132
  return
133
  cutoff = time.time() - _RATE_LIMIT_WINDOW
134
+ stale_keys = [
135
+ ip for ip, ts in _rate_limit_store.items()
136
+ if not ts or all(t <= cutoff for t in ts)
137
+ ]
138
  for key in stale_keys:
139
  del _rate_limit_store[key]
140
 
app/routes/commend/youtube_service.py CHANGED
@@ -18,6 +18,7 @@ from urllib.parse import urlparse
18
  from google.oauth2.credentials import Credentials
19
  from google.auth.transport.requests import Request
20
  from googleapiclient.discovery import build
 
21
  import isodate
22
 
23
  from ...services.logging_config import get_logger
@@ -25,11 +26,11 @@ from ...services.logging_config import get_logger
25
  logger = get_logger(__name__)
26
 
27
  # Cached service instances
28
- _api_key_service = None
29
- _oauth_service = None
30
 
31
 
32
- def _get_api_key_service():
33
  """
34
  API Key ile YouTube API servisi oluşturur.
35
  Read-only işlemler için kullanılır (video detayları, yorumları okuma).
@@ -56,16 +57,12 @@ def _get_api_key_service():
56
  raise ValueError(f"YouTube API initialization failed: {e}")
57
 
58
 
59
- def _get_oauth_service():
60
  """
61
  OAuth 2.0 ile YouTube API servisi oluşturur.
62
  Write işlemleri için kullanılır (yorum gönderme).
 
63
  """
64
- global _oauth_service
65
-
66
- if _oauth_service is not None:
67
- # Check if credentials are still valid
68
- return _oauth_service
69
 
70
  SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl']
71
 
@@ -82,9 +79,9 @@ def _get_oauth_service():
82
  logger.info("Refreshing expired OAuth token")
83
  creds.refresh(Request())
84
 
85
- _oauth_service = build('youtube', 'v3', credentials=creds)
86
  logger.info("YouTube OAuth service initialized")
87
- return _oauth_service
88
  except json.JSONDecodeError as e:
89
  logger.error(f"TOKEN_JSON is not valid JSON: {e}")
90
  raise ValueError("YouTube OAuth token is malformed")
 
18
  from google.oauth2.credentials import Credentials
19
  from google.auth.transport.requests import Request
20
  from googleapiclient.discovery import build
21
+ from googleapiclient.errors import HttpError
22
  import isodate
23
 
24
  from ...services.logging_config import get_logger
 
26
  logger = get_logger(__name__)
27
 
28
  # Cached service instances
29
+ _api_key_service: Any = None
30
+ _oauth_service: Any = None
31
 
32
 
33
+ def _get_api_key_service() -> Any:
34
  """
35
  API Key ile YouTube API servisi oluşturur.
36
  Read-only işlemler için kullanılır (video detayları, yorumları okuma).
 
57
  raise ValueError(f"YouTube API initialization failed: {e}")
58
 
59
 
60
+ def _get_oauth_service() -> Any:
61
  """
62
  OAuth 2.0 ile YouTube API servisi oluşturur.
63
  Write işlemleri için kullanılır (yorum gönderme).
64
+ Credentials are created fresh each call to avoid token reuse issues.
65
  """
 
 
 
 
 
66
 
67
  SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl']
68
 
 
79
  logger.info("Refreshing expired OAuth token")
80
  creds.refresh(Request())
81
 
82
+ service = build('youtube', 'v3', credentials=creds)
83
  logger.info("YouTube OAuth service initialized")
84
+ return service
85
  except json.JSONDecodeError as e:
86
  logger.error(f"TOKEN_JSON is not valid JSON: {e}")
87
  raise ValueError("YouTube OAuth token is malformed")
app/routes/data_processing.py CHANGED
@@ -3,6 +3,7 @@ Routes for data processing and manipulation (Audio/Image).
3
  """
4
 
5
  import time
 
6
  from collections import defaultdict
7
  from fastapi import APIRouter, File, UploadFile, Form, HTTPException, Request, Depends, status
8
  from fastapi.responses import StreamingResponse
@@ -29,7 +30,7 @@ async def _process_rate_limit(request: Request) -> None:
29
  cutoff = now - _PROCESS_RATE_WINDOW
30
 
31
  if len(_process_rate_store) > _PROCESS_MAX_IPS:
32
- stale = [ip for ip, ts in _process_rate_store.items() if all(t <= cutoff for t in ts)]
33
  for ip in stale:
34
  del _process_rate_store[ip]
35
 
@@ -72,6 +73,11 @@ async def process_audio_endpoint(
72
  raise HTTPException(status_code=400, detail={"code": "invalid_file_type", "message": "Invalid file type. Must be audio."})
73
 
74
  try:
 
 
 
 
 
75
  # Read file in chunks to enforce size limit before full allocation
76
  chunks = []
77
  total_read = 0
@@ -90,11 +96,11 @@ async def process_audio_endpoint(
90
  processed_audio = process_audio(content, parsed_options)
91
 
92
  # Return as downloadable file
93
- filename = f"processed_{file.filename}.wav"
94
  return StreamingResponse(
95
  processed_audio,
96
  media_type="audio/wav",
97
- headers={"Content-Disposition": f"attachment; filename={filename}"}
98
  )
99
 
100
  except ValueError as e:
 
3
  """
4
 
5
  import time
6
+ import re
7
  from collections import defaultdict
8
  from fastapi import APIRouter, File, UploadFile, Form, HTTPException, Request, Depends, status
9
  from fastapi.responses import StreamingResponse
 
30
  cutoff = now - _PROCESS_RATE_WINDOW
31
 
32
  if len(_process_rate_store) > _PROCESS_MAX_IPS:
33
+ stale = [ip for ip, ts in _process_rate_store.items() if not ts or all(t <= cutoff for t in ts)]
34
  for ip in stale:
35
  del _process_rate_store[ip]
36
 
 
73
  raise HTTPException(status_code=400, detail={"code": "invalid_file_type", "message": "Invalid file type. Must be audio."})
74
 
75
  try:
76
+ # Sanitize filename to prevent injection attacks
77
+ safe_filename = re.sub(r'[^a-zA-Z0-9._-]', '_', file.filename or 'audio')
78
+ if safe_filename.endswith('.wav'):
79
+ safe_filename = safe_filename[:-4]
80
+
81
  # Read file in chunks to enforce size limit before full allocation
82
  chunks = []
83
  total_read = 0
 
96
  processed_audio = process_audio(content, parsed_options)
97
 
98
  # Return as downloadable file
99
+ output_filename = f"processed_{safe_filename}.wav"
100
  return StreamingResponse(
101
  processed_audio,
102
  media_type="audio/wav",
103
+ headers={"Content-Disposition": f"attachment; filename={output_filename}"}
104
  )
105
 
106
  except ValueError as e: