mirror of
https://github.com/tomasriveral/vps-config.git
synced 2026-08-12 10:30:45 +02:00
205 lines
4.7 KiB
Python
205 lines
4.7 KiB
Python
import os
|
|
import time
|
|
import sqlite3
|
|
import requests
|
|
import subprocess
|
|
import random
|
|
|
|
from urllib.parse import urlparse
|
|
|
|
random.seed()
|
|
|
|
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()Initialize the random number generator.
|
|
|
|
If a is omitted or None, the current system time is used. If randomness sources are provided by the operating system, they are used instead of the system time (see the os.urandom() function for details on availability).
|
|
|
|
If a is an int, its absolute value is used directly.
|
|
|
|
With version 2 (the default), a str, bytes, or bytearray object gets converted to an int and all of its bits are used.
|
|
|
|
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"]
|
|
|
|
DOCKER_COMPOSE_DIR = os.environ["DOCKER_COMPOSE_DIR"]
|
|
|
|
VIDEO_HOSTS = {
|
|
"youtube.com",
|
|
"www.youtube.com",
|
|
"youtu.be",
|
|
"vimeo.com",
|
|
"www.vimeo.com",
|
|
"dailymotion.com",
|
|
"www.dailymotion.com",
|
|
}
|
|
|
|
print("=== FreshRSS archiver starting ===")
|
|
print("Working directory:", os.getcwd())
|
|
print("Python:", os.sys.executable)
|
|
print("FreshRSS DB:", FRESHRSS_DB)
|
|
print("Archive index:", ARCHIVE_INDEX)
|
|
|
|
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, e)
|
|
|
|
def send_to_archivebox(url):
|
|
try:
|
|
r = subprocess.run(
|
|
[
|
|
"docker",
|
|
"compose",
|
|
"-f",
|
|
f"{DOCKER_COMPOSE_DIR}/compose.yml",
|
|
"run",
|
|
"-T",
|
|
"archivebox",
|
|
"archivebox",
|
|
"add",
|
|
url,
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=300
|
|
)
|
|
|
|
if r.returncode != 0:
|
|
print("ArchiveBox error:")
|
|
print(r.stderr)
|
|
return (False, r.stderr)
|
|
|
|
print(r.stdout)
|
|
return True
|
|
|
|
except Exception as e:
|
|
print("ArchiveBox error:", e)
|
|
return False
|
|
|
|
def notify_failure(url, text):
|
|
try:
|
|
requests.post(
|
|
NTFY_URL + "/Alerts",
|
|
data=f"Failed to archive {url}\n{text}",
|
|
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:
|
|
wasVideo = False
|
|
url = article["link"]
|
|
|
|
if url in indexed:
|
|
continue
|
|
|
|
print("Archiving:", url)
|
|
|
|
if is_video_url(url):
|
|
success, errorMSG = send_to_metube(url)
|
|
wasVideo = true
|
|
else:
|
|
success, errorMSG = send_to_archivebox(url)
|
|
|
|
if success:
|
|
print("Success")
|
|
save_to_index(url)
|
|
if wasVideo:
|
|
time.sleep(random.randrange(1200, 3600)) # YouTube/Google is much more vigilant about scraper. Even if we use a burner google account, we wouldn't want it banned.
|
|
else:
|
|
print(f"Failed\n{errorMSG}")
|
|
notify_failure(url, errorMSG)
|
|
|
|
# avoid hammering services
|
|
time.sleep(60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
process()
|