implement po_token extractor args

This commit is contained in:
Simon 2025-01-11 16:39:50 +07:00
parent 2474a6a96b
commit 457f6a0b87
9 changed files with 168 additions and 7 deletions

View File

@ -40,6 +40,7 @@ class DownloadsConfigType(TypedDict):
comment_max: str | bool
comment_sort: Literal["top", "new"]
cookie_import: bool
potoken: bool
throttledratelimit: int
extractor_lang: str | bool
integrate_ryd: bool
@ -85,6 +86,7 @@ class AppConfig:
"comment_max": False,
"comment_sort": "top",
"cookie_import": False,
"potoken": False,
"throttledratelimit": False,
"extractor_lang": False,
"integrate_ryd": False,

View File

@ -34,6 +34,11 @@ urlpatterns = [
views.CookieView.as_view(),
name="api-cookie",
),
path(
"potoken/",
views.POTokenView.as_view(),
name="api-potoken",
),
path(
"token/",
views.TokenView.as_view(),

View File

@ -5,7 +5,7 @@ from appsettings.src.config import AppConfig
from appsettings.src.snapshot import ElasticSnapshot
from common.src.ta_redis import RedisArchivist
from common.views_base import AdminOnly, ApiBaseView
from download.src.yt_dlp_base import CookieHandler
from download.src.yt_dlp_base import CookieHandler, POTokenHandler
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from task.src.task_manager import TaskCommand
@ -252,6 +252,36 @@ class CookieView(ApiBaseView):
return validation
class POTokenView(ApiBaseView):
"""handle PO token"""
permission_classes = [AdminOnly]
def get(self, request):
"""get token"""
config = AppConfig().config
potoken = POTokenHandler(config).get()
return Response({"potoken": potoken})
def post(self, request):
"""post token"""
config = AppConfig().config
new_token = request.data.get("potoken")
if not new_token:
message = "missing potoken key in request data"
print(message)
return Response({"message": message}, status=400)
POTokenHandler(config).set_token(new_token)
return Response({"potoken": new_token})
def delete(self, request):
"""delete token"""
config = AppConfig().config
POTokenHandler(config).revoke_token()
return Response({"potoken": None})
class TokenView(ApiBaseView):
"""resolves to /api/appsettings/token/
DELETE: revoke the token

View File

@ -251,6 +251,7 @@ class PendingList(PendingIndex):
self._notify_add(idx, total)
video_details = self.get_youtube_details(youtube_id, vid_type)
if not video_details:
rand_sleep(self.config)
continue
video_details.update(

View File

@ -11,6 +11,7 @@ from io import StringIO
import yt_dlp
from appsettings.src.config import AppConfig
from common.src.ta_redis import RedisArchivist
from django.conf import settings
class YtWrap:
@ -36,6 +37,10 @@ class YtWrap:
self.obs.update(self.obs_request)
if self.config:
self.add_cookie()
self.add_potoken()
if getattr(settings, "DEBUG", False):
print(self.obs)
def add_cookie(self):
"""add cookie if enabled"""
@ -43,6 +48,21 @@ class YtWrap:
cookie_io = CookieHandler(self.config).get()
self.obs["cookiefile"] = cookie_io
def add_potoken(self):
"""add potoken if enabled"""
if self.config["downloads"].get("potoken"):
potoken = POTokenHandler(self.config).get()
self.obs.update(
{
"extractor_args": {
"youtube": {
"po_token": [potoken],
"player-client": ["web", "default"],
},
}
}
)
def download(self, url):
"""make download request"""
with yt_dlp.YoutubeDL(self.obs) as ydl:
@ -148,3 +168,27 @@ class CookieHandler:
"validated_str": now.strftime("%Y-%m-%d %H:%M"),
}
RedisArchivist().set_message("cookie:valid", message)
class POTokenHandler:
"""handle po token"""
REDIS_KEY = "potoken"
def __init__(self, config):
self.config = config
def get(self) -> str | None:
"""get PO token"""
potoken = RedisArchivist().get_message_str(self.REDIS_KEY)
return potoken
def set_token(self, new_token: str) -> None:
"""set new PO token"""
RedisArchivist().set_message(self.REDIS_KEY, new_token)
AppConfig().update_config({"downloads.potoken": True})
def revoke_token(self) -> None:
"""revoke token"""
RedisArchivist().del_message(self.REDIS_KEY)
AppConfig().update_config({"downloads.potoken": False})

View File

@ -0,0 +1,9 @@
import APIClient from '../../functions/APIClient';
const deletePoToken = async () => {
return APIClient('/api/appsettings/potoken/', {
method: 'DELETE',
});
};
export default deletePoToken;

View File

@ -0,0 +1,10 @@
import APIClient from '../../functions/APIClient';
const updatePoToken = async (potoken: string) => {
return APIClient('/api/appsettings/potoken/', {
method: 'POST',
body: { potoken },
});
};
export default updatePoToken;

View File

@ -21,6 +21,7 @@ export type AppSettingsConfigType = {
comment_max: string | null;
comment_sort: string;
cookie_import: boolean;
potoken: boolean;
throttledratelimit: number | null;
extractor_lang: string | null;
integrate_ryd: boolean;

View File

@ -17,6 +17,8 @@ import updateCookie from '../api/actions/updateCookie';
import loadCookie, { CookieStateType } from '../api/loader/loadCookie';
import deleteCookie from '../api/actions/deleteCookie';
import validateCookie from '../api/actions/validateCookie';
import deletePoToken from '../api/actions/deletePoToken';
import updatePoToken from '../api/actions/updatePoToken';
type SnapshotType = {
id: string;
@ -81,9 +83,8 @@ const SettingsApplication = () => {
// Cookie
const [cookieFormData, setCookieFormData] = useState<string>('');
const [showCookieForm, setShowCookieForm] = useState<boolean>(false);
// const [cookieImport, setCookieImport] = useState(false);
// const [validatingCookie, setValidatingCookie] = useState(false);
// const [cookieResponse, setCookieResponse] = useState<ValidatedCookieType>();
const [poTokenFormData, setPoTokenFormData] = useState<string>('web+');
const [showPoTokenForm, setShowPoTokenForm] = useState<boolean>(false);
// Integrations
const [showApiToken, setShowApiToken] = useState(false);
@ -129,9 +130,6 @@ const SettingsApplication = () => {
setCommentsMax(appSettingsConfig.downloads.comment_max);
setCommentsSort(appSettingsConfig.downloads.comment_sort);
// Cookie
// setCookieImport(appSettingsConfig?.downloads.cookie_import);
// Integrations
setDownloadDislikes(appSettingsConfig.downloads.integrate_ryd);
setEnableSponsorBlock(appSettingsConfig.downloads.integrate_sponsorblock);
@ -172,6 +170,18 @@ const SettingsApplication = () => {
setRefresh(true);
};
const handlePoTokenRevoke = async () => {
await deletePoToken();
setRefresh(true);
};
const handlePoTokenUpdate = async () => {
await updatePoToken(poTokenFormData);
setPoTokenFormData('web+');
setShowPoTokenForm(false);
setRefresh(true);
};
useEffect(() => {
fetchData();
}, []);
@ -509,6 +519,55 @@ const SettingsApplication = () => {
)}
</div>
</div>
<div className="settings-box-wrapper">
<div>
<p>Add PO Token</p>
</div>
<div>
{response?.appSettingsConfig?.downloads.potoken ? (
<>
<p>PO Token enabled.</p>
<button onClick={handlePoTokenRevoke} className="danger-button">
Revoke
</button>
</>
) : (
<p>PO Token disabled</p>
)}
{showPoTokenForm ? (
<div>
<input
type="text"
value={poTokenFormData}
onChange={async e => {
setPoTokenFormData(e.target.value);
}}
/>
{poTokenFormData !== 'web+' && (
<button onClick={handlePoTokenUpdate}>Update</button>
)}
<button
onClick={() => {
setShowPoTokenForm(false);
setPoTokenFormData('web+');
}}
>
Cancel
</button>
</div>
) : (
<div>
<button
onClick={async () => {
setShowPoTokenForm(true);
}}
>
Update PO Token
</button>
</div>
)}
</div>
</div>
</div>
<div className="info-box-item">
<h2 id="sntegrations">Integrations</h2>