diff --git a/backend/api/cookies_controller.py b/backend/api/cookies_controller.py new file mode 100644 index 0000000..3d410a5 --- /dev/null +++ b/backend/api/cookies_controller.py @@ -0,0 +1,16 @@ +from pathlib import Path + +from fastapi import APIRouter, HTTPException, Request, status + +router = APIRouter() +COOKIES_PATH = Path("/app/cookies.txt") + + +@router.post("/cookies", status_code=status.HTTP_204_NO_CONTENT) +async def uploadCookies(request: Request): + body = (await request.body()).decode("utf-8", errors="replace") + if not body.startswith("# Netscape"): + raise HTTPException(status.HTTP_400_BAD_REQUEST, "Kein Netscape-Cookie-File") + tmp = COOKIES_PATH.with_suffix(".tmp") + tmp.write_text(body, encoding="utf-8") + tmp.replace(COOKIES_PATH) diff --git a/backend/base/app.py b/backend/base/app.py index e0f3c01..2717721 100644 --- a/backend/base/app.py +++ b/backend/base/app.py @@ -1,6 +1,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware +from api.cookies_controller import router as cookiesRouter from api.profile_controller import router as profilesRouter from api.video_controller import router as videosRouter from database.database import SessionLocal, createTables @@ -18,6 +19,7 @@ app.add_middleware( app.include_router(videosRouter) app.include_router(profilesRouter) +app.include_router(cookiesRouter) registerWebsocket(app) diff --git a/browser_extension/.amo-upload-uuid b/browser_extension/.amo-upload-uuid index cad6680..29ff78d 100644 --- a/browser_extension/.amo-upload-uuid +++ b/browser_extension/.amo-upload-uuid @@ -1 +1 @@ -{"uploadUuid":"50f3b9723bb7448c905059a445f98e2e","channel":"unlisted","xpiCrcHash":"7d68b51762b603ece4d52513d186f2d3e263430054421f4d0041b58f19ecabd0"} \ No newline at end of file +{"uploadUuid":"d90f6b10216044b49f278d2ac7e340cd","channel":"unlisted","xpiCrcHash":"7b6c7cd66d1833f7ddc9b05bf378315c44aedd55645515c77d4c4b3e41d450a1"} \ No newline at end of file diff --git a/browser_extension/api/background.js b/browser_extension/api/background.js index 33ace32..51baf91 100644 --- a/browser_extension/api/background.js +++ b/browser_extension/api/background.js @@ -1,9 +1,20 @@ const SERVER_BASE = "https://youtube.marha.de"; -browser.runtime.onMessage.addListener(({ profileId, video }) => { - fetch(`${SERVER_BASE}/profiles/${profileId}/videos`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(video), - }).catch(() => {}); +browser.runtime.onMessage.addListener((msg) => { + if (msg?.type === "sync-cookies") { + return syncCookies(); + } + if (msg?.profileId && msg?.video) { + fetch(`${SERVER_BASE}/profiles/${msg.profileId}/videos`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(msg.video), + }).catch(() => {}); + } +}); + +syncCookies(); +browser.alarms.create("cookieSync", { periodInMinutes: 1440 }); +browser.alarms.onAlarm.addListener((a) => { + if (a.name === "cookieSync") syncCookies(); }); diff --git a/browser_extension/api/syncCookies.js b/browser_extension/api/syncCookies.js new file mode 100644 index 0000000..b696c40 --- /dev/null +++ b/browser_extension/api/syncCookies.js @@ -0,0 +1,37 @@ +const COOKIE_SYNC_URL = "https://youtube.marha.de/cookies"; + +function toNetscape(cookies) { + const lines = ["# Netscape HTTP Cookie File"]; + for (const c of cookies) { + const domain = c.httpOnly ? `#HttpOnly_${c.domain}` : c.domain; + const includeSubdomain = c.domain.startsWith(".") ? "TRUE" : "FALSE"; + const secure = c.secure ? "TRUE" : "FALSE"; + const expiration = Math.floor(c.expirationDate || 0); + lines.push([domain, includeSubdomain, c.path, secure, expiration, c.name, c.value].join("\t")); + } + return lines.join("\n") + "\n"; +} + +async function syncCookies() { + const when = new Date().toISOString(); + try { + const cookies = await browser.cookies.getAll({ domain: ".youtube.com" }); + if (cookies.length === 0) { + await browser.storage.local.set({ lastCookieSync: { when, ok: false, error: "keine YouTube-Cookies gefunden" } }); + return; + } + const body = toNetscape(cookies); + const res = await fetch(COOKIE_SYNC_URL, { + method: "POST", + headers: { "Content-Type": "text/plain" }, + body, + }); + if (!res.ok) { + await browser.storage.local.set({ lastCookieSync: { when, ok: false, error: `HTTP ${res.status}` } }); + return; + } + await browser.storage.local.set({ lastCookieSync: { when, ok: true, count: cookies.length } }); + } catch (e) { + await browser.storage.local.set({ lastCookieSync: { when, ok: false, error: String(e) } }); + } +} diff --git a/browser_extension/config/popup.html b/browser_extension/config/popup.html index d737e4e..9bf2855 100644 --- a/browser_extension/config/popup.html +++ b/browser_extension/config/popup.html @@ -2,15 +2,27 @@