feat: 50 channels, daily 3AM cron sync, countdown timer, remove manual button
This commit is contained in:
+1
-1
@@ -51,7 +51,7 @@ async def fetch_subscriptions():
|
|||||||
# Step 2: For each channel, get latest videos
|
# Step 2: For each channel, get latest videos
|
||||||
new_count = 0
|
new_count = 0
|
||||||
async with aiosqlite.connect(DB_PATH) as db:
|
async with aiosqlite.connect(DB_PATH) as db:
|
||||||
for cid in channel_ids[:20]:
|
for cid in channel_ids[:50]:
|
||||||
try:
|
try:
|
||||||
req = youtube.search().list(
|
req = youtube.search().list(
|
||||||
part="snippet",
|
part="snippet",
|
||||||
|
|||||||
+6
-7
@@ -7,14 +7,13 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: t-youtube
|
container_name: t-youtube
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: host
|
ports:
|
||||||
|
- "8001:8000"
|
||||||
environment:
|
environment:
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-sk-Kq5JbgmnEEkdzTqNAQ6bV6nztQ0ngeHTHF9Vg8nUglQvrOlx}
|
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
|
||||||
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
|
- DEEPSEEK_BASE_URL=https://api.deepseek.com
|
||||||
- ANTHROPIC_BASE_URL=https://www.bytecatcode.org
|
- DEEPSEEK_MODEL=deepseek-chat
|
||||||
- HTTP_PROXY=http://100.70.115.1:8118
|
- DB_DIR=/app/data
|
||||||
- HTTPS_PROXY=http://100.70.115.1:8118
|
|
||||||
- NO_PROXY=localhost,127.0.0.1,100.0.0.0/8,192.168.0.0/16
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
let isSyncing = $state(false);
|
let isSyncing = $state(false);
|
||||||
let syncMessage = $state('');
|
let syncMessage = $state('');
|
||||||
let pendingCount = $state(0);
|
let pendingCount = $state(0);
|
||||||
|
let nextSync = $state(localStorage.getItem('t-yt-next-sync') || '');
|
||||||
let theme = $state(localStorage.getItem('theme') || 'dark');
|
let theme = $state(localStorage.getItem('theme') || 'dark');
|
||||||
let summaryPanel = $state(null);
|
let summaryPanel = $state(null);
|
||||||
let notes = $state(JSON.parse(localStorage.getItem('t-yt-notes') || '{}'));
|
let notes = $state(JSON.parse(localStorage.getItem('t-yt-notes') || '{}'));
|
||||||
@@ -92,10 +93,25 @@
|
|||||||
document.documentElement.className = theme;
|
document.documentElement.className = theme;
|
||||||
loadVideos();
|
loadVideos();
|
||||||
loadPendingCount();
|
loadPendingCount();
|
||||||
|
updateSyncTimer();
|
||||||
window.addEventListener('keydown', handleKeydown);
|
window.addEventListener('keydown', handleKeydown);
|
||||||
return () => window.removeEventListener('keydown', handleKeydown);
|
return () => window.removeEventListener('keydown', handleKeydown);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateSyncTimer() {
|
||||||
|
const el = document.getElementById('sync-timer');
|
||||||
|
if (!el) return;
|
||||||
|
const now = new Date();
|
||||||
|
const next = new Date(now);
|
||||||
|
next.setHours(3, 0, 0, 0); // 3 AM daily
|
||||||
|
if (now > next) next.setDate(next.getDate() + 1);
|
||||||
|
const diff = next - now;
|
||||||
|
const h = Math.floor(diff / 3600000);
|
||||||
|
const m = Math.floor((diff % 3600000) / 60000);
|
||||||
|
el.textContent = `Next sync in ${h}h ${m}m`;
|
||||||
|
setTimeout(updateSyncTimer, 60000);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadPendingCount() {
|
async function loadPendingCount() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/videos?status=pending&limit=1');
|
const res = await fetch('/api/videos?status=pending&limit=1');
|
||||||
@@ -191,16 +207,12 @@
|
|||||||
<span class="text-[10px] px-2 py-0.5 rounded font-semibold uppercase tracking-widest" style="background:var(--bg-tertiary);color:var(--text-muted)">text feed</span>
|
<span class="text-[10px] px-2 py-0.5 rounded font-semibold uppercase tracking-widest" style="background:var(--bg-tertiary);color:var(--text-muted)">text feed</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-3">
|
<div class="flex items-center space-x-3">
|
||||||
<button onclick={syncSubscriptions} disabled={isSyncing}
|
|
||||||
class="px-3 py-1.5 text-xs font-bold rounded-lg transition"
|
|
||||||
style="background:var(--accent);color:white">
|
|
||||||
{isSyncing ? 'Syncing...' : 'Sync'}
|
|
||||||
</button>
|
|
||||||
<button onclick={toggleTheme}
|
<button onclick={toggleTheme}
|
||||||
class="text-sm px-2 py-1 rounded transition" style="color:var(--text-muted)"
|
class="text-sm px-2 py-1 rounded transition" style="color:var(--text-muted)"
|
||||||
title="Toggle theme">
|
title="Toggle theme">
|
||||||
{theme === 'dark' ? '☀' : '☾'}
|
{theme === 'dark' ? '☀' : '☾'}
|
||||||
</button>
|
</button>
|
||||||
|
<span class="text-[10px]" style="color:var(--text-muted)" id="sync-timer"></span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user