mirror of
https://github.com/tomasriveral/vps-config.git
synced 2026-08-11 18:18:38 +02:00
freshrss favorite archiver
This commit is contained in:
@@ -14,3 +14,15 @@ FLATNOTES_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|||||||
METUBE_PASSWORD_HASH=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
METUBE_PASSWORD_HASH=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
TRANSMUTE_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
TRANSMUTE_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
FRESHRSS_DB=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
ARCHIVE_INDEX=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
METUBE_URL=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
METUBE_USER=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
METUBE_PASS=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
NTFY_URL=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
NTFY_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
DOCKER_COMPOSE_DIR=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|||||||
+5
-1
@@ -166,13 +166,17 @@ mkdir -p \
|
|||||||
/opt/server/caddy/data \
|
/opt/server/caddy/data \
|
||||||
/opt/server/caddy/config \
|
/opt/server/caddy/config \
|
||||||
/opt/server/resspublica/feed \
|
/opt/server/resspublica/feed \
|
||||||
/opt/server/resspublica/images
|
/opt/server/resspublica/images \
|
||||||
|
/opt/server/fav-archiver
|
||||||
|
|
||||||
sudo chown -R 1000:1000 /opt/server/filebrowser # somehow filebrowser cant acces it
|
sudo chown -R 1000:1000 /opt/server/filebrowser # somehow filebrowser cant acces it
|
||||||
|
|
||||||
sudo chown -R tomasr:tomasr /opt/server/resspublica
|
sudo chown -R tomasr:tomasr /opt/server/resspublica
|
||||||
sudo chmod -R u+rwX /opt/server/resspublica
|
sudo chmod -R u+rwX /opt/server/resspublica
|
||||||
|
|
||||||
|
sudo chown -R tomasr:tomasr /opt/server/fav-archiver
|
||||||
|
sudo chmod -R u+rwX /opt/server/fav-archiver
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Configure rclone now."
|
echo "Configure rclone now."
|
||||||
rclone config
|
rclone config
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import sqlite3
|
||||||
|
import requests
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
def load_env_file(path=".env"):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(path) as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# ignore empty lines and comments
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
os.environ.setdefault(key, value)
|
||||||
|
|
||||||
|
|
||||||
|
load_env_file()
|
||||||
|
|
||||||
|
FRESHRSS_DB = os.environ["FRESHRSS_DB"]
|
||||||
|
ARCHIVE_INDEX = os.environ["ARCHIVE_INDEX"]
|
||||||
|
|
||||||
|
METUBE_URL = os.environ["METUBE_URL"]
|
||||||
|
METUBE_USER = os.environ["METUBE_USER"]
|
||||||
|
METUBE_PASS = os.environ["METUBE_PASS"]
|
||||||
|
|
||||||
|
NTFY_URL = os.environ["NTFY_URL"]
|
||||||
|
NTFY_TOKEN = os.environ["NTFY_TOKEN"]
|
||||||
|
|
||||||
|
ARCHIVEBOX_COMPOSE_DIR = os.environ["ARCHIVEBOX_COMPOSE_DIR"]
|
||||||
|
|
||||||
|
VIDEO_HOSTS = {
|
||||||
|
"youtube.com",
|
||||||
|
"www.youtube.com",
|
||||||
|
"youtu.be",
|
||||||
|
"vimeo.com",
|
||||||
|
"www.vimeo.com",
|
||||||
|
"dailymotion.com",
|
||||||
|
"www.dailymotion.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_favorites():
|
||||||
|
conn = sqlite3.connect(FRESHRSS_DB)
|
||||||
|
conn.row_factory = sqlite3.Row
|
||||||
|
|
||||||
|
cur = conn.cursor()
|
||||||
|
cur.execute("""
|
||||||
|
SELECT title, link
|
||||||
|
FROM entry
|
||||||
|
WHERE is_favorite = 1
|
||||||
|
ORDER BY date DESC
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = [
|
||||||
|
{
|
||||||
|
"title": row["title"],
|
||||||
|
"link": row["link"]
|
||||||
|
}
|
||||||
|
for row in cur.fetchall()
|
||||||
|
]
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def load_index():
|
||||||
|
if not os.path.exists(ARCHIVE_INDEX):
|
||||||
|
return set()
|
||||||
|
|
||||||
|
with open(ARCHIVE_INDEX, "r") as f:
|
||||||
|
return set(line.strip() for line in f if line.strip())
|
||||||
|
|
||||||
|
|
||||||
|
def save_to_index(url):
|
||||||
|
with open(ARCHIVE_INDEX, "a") as f:
|
||||||
|
f.write(url + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def is_video_url(url):
|
||||||
|
host = urlparse(url).netloc.lower()
|
||||||
|
return any(host.endswith(site) for site in VIDEO_HOSTS)
|
||||||
|
|
||||||
|
|
||||||
|
def send_to_metube(url):
|
||||||
|
"""
|
||||||
|
MeTube API endpoint:
|
||||||
|
POST /add
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
r = requests.post(
|
||||||
|
f"{METUBE_URL}/add",
|
||||||
|
json={
|
||||||
|
"url": url
|
||||||
|
},
|
||||||
|
auth=(METUBE_USER, METUBE_PASS),
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
return r.status_code in range(200, 300)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("MeTube error:", e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_to_archivebox(url):
|
||||||
|
try:
|
||||||
|
r = subprocess.run(
|
||||||
|
[
|
||||||
|
"docker",
|
||||||
|
"compose",
|
||||||
|
"-f",
|
||||||
|
f"{ARCHIVEBOX_COMPOSE_DIR}/docker-compose.yml",
|
||||||
|
"exec",
|
||||||
|
"-T",
|
||||||
|
"archivebox",
|
||||||
|
"archivebox",
|
||||||
|
"add",
|
||||||
|
url,
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
|
||||||
|
if r.returncode != 0:
|
||||||
|
print("ArchiveBox error:")
|
||||||
|
print(r.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(r.stdout)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("ArchiveBox error:", e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def notify_failure(url):
|
||||||
|
try:
|
||||||
|
requests.post(
|
||||||
|
NTFY_URL,
|
||||||
|
data=f"Failed to archive {url}",
|
||||||
|
auth=("", NTFY_TOKEN),
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print("ntfy error:", e)
|
||||||
|
|
||||||
|
|
||||||
|
def process():
|
||||||
|
indexed = load_index()
|
||||||
|
favorites = get_favorites()
|
||||||
|
|
||||||
|
for article in favorites:
|
||||||
|
url = article["link"]
|
||||||
|
|
||||||
|
if url in indexed:
|
||||||
|
continue
|
||||||
|
|
||||||
|
print("Archiving:", url)
|
||||||
|
|
||||||
|
if is_video_url(url):
|
||||||
|
success = send_to_metube(url)
|
||||||
|
else:
|
||||||
|
success = send_to_archivebox(url)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
print("Success")
|
||||||
|
save_to_index(url)
|
||||||
|
else:
|
||||||
|
print("Failed")
|
||||||
|
notify_failure(url)
|
||||||
|
|
||||||
|
# avoid hammering services
|
||||||
|
time.sleep(60)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
process()
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=FreshRSS favorite article archiver
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
WorkingDirectory=/home/tomasr/vps-config
|
||||||
|
ExecStart=/usr/bin/python3 /home/tomasr/vps-config/freshrss-fav-archiver.py
|
||||||
|
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
Reference in New Issue
Block a user