update
This commit is contained in:
16
backend/api/cookies_controller.py
Normal file
16
backend/api/cookies_controller.py
Normal file
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"uploadUuid":"50f3b9723bb7448c905059a445f98e2e","channel":"unlisted","xpiCrcHash":"7d68b51762b603ece4d52513d186f2d3e263430054421f4d0041b58f19ecabd0"}
|
||||
{"uploadUuid":"d90f6b10216044b49f278d2ac7e340cd","channel":"unlisted","xpiCrcHash":"7b6c7cd66d1833f7ddc9b05bf378315c44aedd55645515c77d4c4b3e41d450a1"}
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
37
browser_extension/api/syncCookies.js
Normal file
37
browser_extension/api/syncCookies.js
Normal file
@@ -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) } });
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,27 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { width: 200px; padding: 10px; font-family: sans-serif; font-size: 14px; }
|
||||
body { width: 220px; padding: 10px; font-family: sans-serif; font-size: 14px; }
|
||||
h3 { margin: 0 0 8px; }
|
||||
label { display: block; padding: 4px 0; cursor: pointer; }
|
||||
.error { color: red; font-size: 12px; }
|
||||
.section { margin-top: 12px; padding-top: 10px; border-top: 1px solid #ddd; }
|
||||
.status { font-size: 12px; color: #555; margin-bottom: 6px; }
|
||||
.status.ok { color: #2a7; }
|
||||
.status.fail { color: #c33; }
|
||||
button { width: 100%; padding: 6px; font-size: 13px; cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Profil</h3>
|
||||
<div id="profiles"></div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Cookie-Sync</h3>
|
||||
<div id="cookieStatus" class="status">noch nicht synchronisiert</div>
|
||||
<button id="syncBtn">Jetzt synchronisieren</button>
|
||||
</div>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
const SERVER_URL = "https://youtube.marha.de/profiles";
|
||||
const container = document.getElementById("profiles");
|
||||
const statusEl = document.getElementById("cookieStatus");
|
||||
const syncBtn = document.getElementById("syncBtn");
|
||||
|
||||
async function load() {
|
||||
async function loadProfiles() {
|
||||
try {
|
||||
const res = await fetch(SERVER_URL);
|
||||
const profiles = await res.json();
|
||||
@@ -27,4 +29,41 @@ async function load() {
|
||||
}
|
||||
}
|
||||
|
||||
load();
|
||||
function formatAgo(iso) {
|
||||
const diff = Date.now() - new Date(iso).getTime();
|
||||
const mins = Math.floor(diff / 60000);
|
||||
if (mins < 1) return "gerade eben";
|
||||
if (mins < 60) return `vor ${mins} min`;
|
||||
const hrs = Math.floor(mins / 60);
|
||||
if (hrs < 24) return `vor ${hrs} h`;
|
||||
const days = Math.floor(hrs / 24);
|
||||
return `vor ${days} Tagen`;
|
||||
}
|
||||
|
||||
async function refreshStatus() {
|
||||
const { lastCookieSync } = await browser.storage.local.get("lastCookieSync");
|
||||
if (!lastCookieSync) {
|
||||
statusEl.className = "status";
|
||||
statusEl.textContent = "noch nicht synchronisiert";
|
||||
return;
|
||||
}
|
||||
if (lastCookieSync.ok) {
|
||||
statusEl.className = "status ok";
|
||||
statusEl.textContent = `OK (${lastCookieSync.count} Cookies, ${formatAgo(lastCookieSync.when)})`;
|
||||
} else {
|
||||
statusEl.className = "status fail";
|
||||
statusEl.textContent = `Fehler: ${lastCookieSync.error}`;
|
||||
}
|
||||
}
|
||||
|
||||
syncBtn.addEventListener("click", async () => {
|
||||
syncBtn.disabled = true;
|
||||
syncBtn.textContent = "Syncing...";
|
||||
await browser.runtime.sendMessage({ type: "sync-cookies" });
|
||||
await refreshStatus();
|
||||
syncBtn.disabled = false;
|
||||
syncBtn.textContent = "Jetzt synchronisieren";
|
||||
});
|
||||
|
||||
loadProfiles();
|
||||
refreshStatus();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "YouTube Video Erfasser",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.1",
|
||||
"description": "Erfasst YouTube-Videos und sendet sie an den Server",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
@@ -12,9 +12,11 @@
|
||||
}
|
||||
},
|
||||
"permissions": [
|
||||
"*://www.youtube.com/*",
|
||||
"*://*.youtube.com/*",
|
||||
"https://youtube.marha.de/*",
|
||||
"storage"
|
||||
"storage",
|
||||
"cookies",
|
||||
"alarms"
|
||||
],
|
||||
"content_scripts": [
|
||||
{
|
||||
@@ -23,7 +25,7 @@
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["api/background.js"]
|
||||
"scripts": ["api/syncCookies.js", "api/background.js"]
|
||||
},
|
||||
"browser_action": {
|
||||
"default_popup": "config/popup.html",
|
||||
|
||||
Reference in New Issue
Block a user