@@ -0,0 +1,121 @@
|
|||||||
|
name: Conditional Sync Upstream, Build, and Push Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 * * * *' # ogni ora
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-build-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Backup files and force sync upstream
|
||||||
|
id: sync
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions"
|
||||||
|
git config user.email "[email protected]"
|
||||||
|
|
||||||
|
git remote get-url upstream || git remote add upstream https://github.com/UrloMythus/MammaMia.git
|
||||||
|
git fetch upstream
|
||||||
|
|
||||||
|
# Controlla se ci sono differenze PRIMA del reset
|
||||||
|
if git diff --quiet HEAD upstream/main; then
|
||||||
|
echo "Repository already up to date"
|
||||||
|
echo "no_change=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "Changes detected, proceeding with sync and build"
|
||||||
|
|
||||||
|
backup_dir=".git/backup_files"
|
||||||
|
mkdir -p "$backup_dir/.github/workflows"
|
||||||
|
mkdir -p "$backup_dir/Dockerfiles"
|
||||||
|
|
||||||
|
if [ -f config.json ]; then
|
||||||
|
cp config.json "$backup_dir/config.json"
|
||||||
|
else
|
||||||
|
echo "⚠️ config.json non trovato, skipping backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f update_domains.py ]; then
|
||||||
|
cp update_domains.py "$backup_dir/update_domains.py"
|
||||||
|
else
|
||||||
|
echo "⚠️ update_domains.py non trovato, skipping backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d .github/workflows ]; then
|
||||||
|
cp -r .github/workflows/* "$backup_dir/.github/workflows/" || echo "⚠️ No workflows to backup"
|
||||||
|
else
|
||||||
|
echo "⚠️ Cartella .github/workflows non trovata, skipping backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d Dockerfiles ]; then
|
||||||
|
cp -r Dockerfiles/* "$backup_dir/Dockerfiles/" || echo "⚠️ No Dockerfiles to backup"
|
||||||
|
else
|
||||||
|
echo "⚠️ Cartella Dockerfiles non trovata, skipping backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git checkout main || git checkout -b main
|
||||||
|
git reset --hard upstream/main
|
||||||
|
|
||||||
|
if [ -f "$backup_dir/config.json" ]; then
|
||||||
|
cp "$backup_dir/config.json" config.json
|
||||||
|
else
|
||||||
|
echo "⚠️ File config.json non trovato nel backup, skipping restore"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$backup_dir/update_domains.py" ]; then
|
||||||
|
cp "$backup_dir/update_domains.py" update_domains.py
|
||||||
|
else
|
||||||
|
echo "⚠️ File update_domains.py non trovato nel backup, skipping restore"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p .github/workflows
|
||||||
|
if [ -d "$backup_dir/.github/workflows" ]; then
|
||||||
|
cp -r "$backup_dir/.github/workflows/"* .github/workflows/ || echo "⚠️ No workflows to restore"
|
||||||
|
else
|
||||||
|
echo "⚠️ Backup workflows folder mancante, skipping restore"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p Dockerfiles
|
||||||
|
if [ -d "$backup_dir/Dockerfiles" ]; then
|
||||||
|
cp -r "$backup_dir/Dockerfiles/"* Dockerfiles/ || echo "⚠️ No Dockerfiles to restore"
|
||||||
|
else
|
||||||
|
echo "⚠️ Backup Dockerfiles folder mancante, skipping restore"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m "Restore files after forced sync" || echo "No changes needed"
|
||||||
|
git push origin main --force
|
||||||
|
echo "no_change=false" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
rm -rf "$backup_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check if build is needed
|
||||||
|
run: echo "Changes detected, proceed with build."
|
||||||
|
if: steps.sync.outputs.no_change == 'false'
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
if: steps.sync.outputs.no_change == 'false'
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
if: steps.sync.outputs.no_change == 'false'
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: dula117/mammamia:latest
|
||||||
|
if: steps.sync.outputs.no_change == 'false'
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
name: Update Domains
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 20 * * *" # Esegui alle 20:00 ogni giorno
|
||||||
|
workflow_dispatch: # Permette di avviare manualmente il workflow
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-domains:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip install requests
|
||||||
|
|
||||||
|
- name: Update domains in config.json
|
||||||
|
run: |
|
||||||
|
python update_domains.py
|
||||||
|
|
||||||
|
- name: Commit and push changes
|
||||||
|
run: |
|
||||||
|
git config --global user.email "[email protected]"
|
||||||
|
git config --global user.name "Your Name"
|
||||||
|
git add config.json
|
||||||
|
git commit -m "Aggiornamento domini in config.json"
|
||||||
|
git push
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -8,7 +8,7 @@ WORKDIR /app
|
|||||||
RUN apt-get update && apt-get install -y git
|
RUN apt-get update && apt-get install -y git
|
||||||
|
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
RUN git clone https://github.com/UrloMythus/MammaMia.git .
|
RUN git clone https://github.com/vitouchiha/MammaMia.git .
|
||||||
|
|
||||||
# Copy the local config.json file to the container
|
# Copy the local config.json file to the container
|
||||||
COPY config.json /app/config.json
|
COPY config.json /app/config.json
|
||||||
@@ -20,4 +20,4 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
# Run run.py when the container launches
|
# Run run.py when the container launches
|
||||||
ENTRYPOINT ["python", "run.py"]
|
ENTRYPOINT ["python", "run.py"]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ WORKDIR /app
|
|||||||
RUN apt-get update && apt-get install -y git
|
RUN apt-get update && apt-get install -y git
|
||||||
|
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
RUN git clone https://github.com/UrloMythus/MammaMia.git .
|
RUN git clone https://github.com/vitouchiha/MammaMia.git .
|
||||||
|
|
||||||
|
|
||||||
# Install any needed packages specified in requirements.txt
|
# Install any needed packages specified in requirements.txt
|
||||||
@@ -18,4 +18,4 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
# Run run.py when the container launches
|
# Run run.py when the container launches
|
||||||
ENTRYPOINT ["python", "run.py"]
|
ENTRYPOINT ["python", "run.py"]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ RUN apt-get update && \
|
|||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN git clone https://github.com/UrloMythus/MammaMia.git .
|
RUN git clone https://github.com/vitouchiha/MammaMia.git .
|
||||||
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
@@ -15,4 +15,4 @@ RUN pip install --no-cache-dir pytesseract
|
|||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
ENTRYPOINT ["python", "run.py"]
|
ENTRYPOINT ["python", "run.py"]
|
||||||
|
|||||||
+15
-15
@@ -9,51 +9,51 @@
|
|||||||
"enabled": "1"
|
"enabled": "1"
|
||||||
},
|
},
|
||||||
"StreamingWatch":{
|
"StreamingWatch":{
|
||||||
"url":"https://www.streamingwatch.org",
|
"url": "https://www.streamingwatch.org",
|
||||||
"SW_PROXY": "0",
|
"SW_PROXY": "0",
|
||||||
"SW_ForwardProxy": "0",
|
"SW_ForwardProxy": "0",
|
||||||
"enabled":"1"
|
"enabled":"1"
|
||||||
},
|
},
|
||||||
"CB01":{
|
"CB01":{
|
||||||
"url":"https://cb01.uno",
|
"url": "https://cb01net.xyz",
|
||||||
"CB_PROXY":"0",
|
"CB_PROXY":"1",
|
||||||
"CB_ForwardProxy":"0",
|
"CB_ForwardProxy":"0",
|
||||||
"MX_ForwardProxy":"0",
|
"MX_ForwardProxy":"0",
|
||||||
"MX_PROXY":"0",
|
"MX_PROXY":"0",
|
||||||
"enabled":"1"
|
"enabled":"1"
|
||||||
},
|
},
|
||||||
"Guardaserie":{
|
"Guardaserie":{
|
||||||
"url":"https://guardaserie.qpon",
|
"url": "https://guardaserietv.help",
|
||||||
"enabled":"1",
|
"enabled":"1",
|
||||||
"GS_ForwardProxy":"0",
|
"GS_ForwardProxy":"0",
|
||||||
"GS_PROXY":"0"
|
"GS_PROXY":"1"
|
||||||
},
|
},
|
||||||
"GuardaHD":{
|
"GuardaHD":{
|
||||||
"url":"https://mostraguarda.stream",
|
"url": "https://mostraguarda.stream",
|
||||||
"enabled":"1",
|
"enabled":"1",
|
||||||
"GH_ForwardProxy":"0",
|
"GH_ForwardProxy":"0",
|
||||||
"GH_PROXY":"0"
|
"GH_PROXY":"1"
|
||||||
},
|
},
|
||||||
"Eurostreaming":{
|
"Eurostreaming":{
|
||||||
"url": "https://eurostreaming.garden",
|
"url": "https://eurostreaming.dating",
|
||||||
"enabled": "1",
|
"enabled": "1",
|
||||||
"ES_ForwardProxy": "0",
|
"ES_ForwardProxy": "0",
|
||||||
"ES_PROXY": "0"
|
"ES_PROXY": "1"
|
||||||
},
|
},
|
||||||
"Guardaflix":{
|
"Guardaflix":{
|
||||||
"url": "https://guardaflix.live",
|
"url": "https://guardaflix.live",
|
||||||
"enabled": "1",
|
"enabled": "1",
|
||||||
"GF_ForwardProxy": "0",
|
"GF_ForwardProxy": "0",
|
||||||
"GF_PROXY": "0"
|
"GF_PROXY": "1"
|
||||||
},
|
},
|
||||||
"Guardoserie":{
|
"Guardoserie":{
|
||||||
"url": "https://guardoserie.live",
|
"url": "https://guardoserie.monster",
|
||||||
"enabled": "1",
|
"enabled": "1",
|
||||||
"GO_ForwardProxy": "0",
|
"GO_ForwardProxy": "0",
|
||||||
"GO_PROXY": "0"
|
"GO_PROXY": "1"
|
||||||
},
|
},
|
||||||
"AnimeWorld":{
|
"AnimeWorld":{
|
||||||
"url":"https://www.animeworld.so",
|
"url": "https://www.animeworld.ac",
|
||||||
"enabled":"1",
|
"enabled":"1",
|
||||||
"AW_ForwardProxy":"0",
|
"AW_ForwardProxy":"0",
|
||||||
"AW_PROXY":"0"
|
"AW_PROXY":"0"
|
||||||
@@ -63,8 +63,8 @@
|
|||||||
"load_env": "0",
|
"load_env": "0",
|
||||||
"HOST": "0.0.0.0",
|
"HOST": "0.0.0.0",
|
||||||
"PORT": 8080,
|
"PORT": 8080,
|
||||||
"Name": "MammaMia",
|
"Name": "Nello",
|
||||||
"Icon": "🍕️",
|
"Icon": "🐶",
|
||||||
"level": "info",
|
"level": "info",
|
||||||
"Global_Proxy": "0"
|
"Global_Proxy": "0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Merge remote URLs into local config.json, replacing ONLY the "url" lines.
|
||||||
|
- Scarica config.json remoto (raw GitHub).
|
||||||
|
- Per ogni sito in "Siti", segue i redirect e trova l'URL finale.
|
||||||
|
- Apre il config.json locale come testo, trova le righe "url" relative ai siti
|
||||||
|
e sostituisce SOLO il valore dentro le virgolette, preservando tutto il resto.
|
||||||
|
- Crea un backup del config.json locale prima di sovrascriverlo.
|
||||||
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
import shutil
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
# --- CONFIGURAZIONE ---
|
||||||
|
REMOTE_RAW_URL = "https://raw.githubusercontent.com/UrloMythus/MammaMia/main/config.json"
|
||||||
|
LOCAL_CONFIG_PATH = Path("config.json")
|
||||||
|
HEADERS = {"User-Agent": "Mozilla/5.0 (compatible; DomainMerger/1.0)"}
|
||||||
|
REQUEST_TIMEOUT = 12
|
||||||
|
# -----------------------
|
||||||
|
|
||||||
|
def backup_file(path: Path) -> Path | None:
|
||||||
|
if not path.exists():
|
||||||
|
return None
|
||||||
|
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
bak = path.with_name(f"{path.name}.bak_{ts}")
|
||||||
|
shutil.copy2(path, bak)
|
||||||
|
return bak
|
||||||
|
|
||||||
|
def normalize_url(u: str) -> str:
|
||||||
|
parsed = urlparse(u)
|
||||||
|
scheme = parsed.scheme if parsed.scheme else "https"
|
||||||
|
netloc = parsed.netloc or parsed.path
|
||||||
|
return f"{scheme}://{netloc}"
|
||||||
|
|
||||||
|
def follow_redirect(url: str) -> str | None:
|
||||||
|
if not url.startswith(("http://", "https://")):
|
||||||
|
test = "http://" + url
|
||||||
|
else:
|
||||||
|
test = url
|
||||||
|
try:
|
||||||
|
r = requests.get(test, allow_redirects=True, headers=HEADERS,
|
||||||
|
timeout=REQUEST_TIMEOUT, verify=False)
|
||||||
|
return normalize_url(r.url)
|
||||||
|
except requests.RequestException as e:
|
||||||
|
print(f"⚠️ Redirect fallito per '{url}': {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def load_remote_urls(remote_raw_url: str) -> dict:
|
||||||
|
"""Scarica il JSON remoto e ritorna mappa site_key -> url (o None se mancante)."""
|
||||||
|
try:
|
||||||
|
r = requests.get(remote_raw_url, headers=HEADERS, timeout=REQUEST_TIMEOUT)
|
||||||
|
r.raise_for_status()
|
||||||
|
remote_json = r.json()
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Impossibile scaricare/parsing remote config: {e}")
|
||||||
|
|
||||||
|
siti = remote_json.get("Siti", {})
|
||||||
|
result = {}
|
||||||
|
for site_key, site_info in siti.items():
|
||||||
|
url = site_info.get("url")
|
||||||
|
if url:
|
||||||
|
result[site_key] = url
|
||||||
|
return result
|
||||||
|
|
||||||
|
def merge_remote_into_local(remote_map: dict, local_path: Path):
|
||||||
|
if not local_path.exists():
|
||||||
|
raise FileNotFoundError(f"File locale non trovato: {local_path}")
|
||||||
|
|
||||||
|
# backup
|
||||||
|
bak = backup_file(local_path)
|
||||||
|
if bak:
|
||||||
|
print(f"📦 Backup creato: {bak.name}")
|
||||||
|
|
||||||
|
text = local_path.read_text(encoding="utf-8")
|
||||||
|
lines = text.splitlines()
|
||||||
|
|
||||||
|
# regex: match apertura blocco sito es. "CB01":{
|
||||||
|
site_open_re = re.compile(r'^\s*"(?P<site>[^"]+)"\s*:\s*\{\s*$')
|
||||||
|
|
||||||
|
# regex per trovare esattamente la riga "url": "valore" preservando indentazione e virgola finale
|
||||||
|
url_line_re = re.compile(r'^(\s*)"url"\s*:\s*"([^"]*)"( *)(,?)\s*$')
|
||||||
|
|
||||||
|
current_site = None
|
||||||
|
updated_lines = []
|
||||||
|
changes = []
|
||||||
|
|
||||||
|
for idx, line in enumerate(lines):
|
||||||
|
site_match = site_open_re.match(line)
|
||||||
|
if site_match:
|
||||||
|
current_site = site_match.group("site")
|
||||||
|
updated_lines.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
url_match = url_line_re.match(line)
|
||||||
|
if url_match:
|
||||||
|
indent = url_match.group(1)
|
||||||
|
old_value = url_match.group(2)
|
||||||
|
trailing_space = url_match.group(3) or ""
|
||||||
|
trailing_comma = url_match.group(4) or ""
|
||||||
|
site_key = current_site or "unknown"
|
||||||
|
|
||||||
|
# se il sito esiste nella mappa remota, proviamo ad aggiornare
|
||||||
|
remote_url = remote_map.get(site_key)
|
||||||
|
if remote_url:
|
||||||
|
final_url = follow_redirect(remote_url)
|
||||||
|
if final_url and final_url != normalize_url(old_value):
|
||||||
|
# sostituisco solo la parte tra virgolette
|
||||||
|
new_line = f'{indent}"url": "{final_url}"{trailing_space}{trailing_comma}'
|
||||||
|
updated_lines.append(new_line)
|
||||||
|
changes.append((site_key, old_value, final_url))
|
||||||
|
print(f"✅ {site_key}: {old_value} → {final_url}")
|
||||||
|
else:
|
||||||
|
# nessuna modifica (redirect fallito o identico)
|
||||||
|
updated_lines.append(line)
|
||||||
|
print(f"ℹ️ {site_key}: nessuna modifica per '{old_value}'")
|
||||||
|
else:
|
||||||
|
# il sito non è nella lista remota: non tocchiamo la riga
|
||||||
|
updated_lines.append(line)
|
||||||
|
print(f"ℹ️ {site_key}: nessun URL remoto per questo sito, riga lasciata invariata")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# linea normale
|
||||||
|
updated_lines.append(line)
|
||||||
|
|
||||||
|
# Scrivo solo se ci sono cambiamenti (ma anche se non ci sono, sovrascrivo per coerenza)
|
||||||
|
new_text = "\n".join(updated_lines)
|
||||||
|
# preserva newline finale se il file originale ce l'aveva
|
||||||
|
if text.endswith("\n") and not new_text.endswith("\n"):
|
||||||
|
new_text += "\n"
|
||||||
|
|
||||||
|
local_path.write_text(new_text, encoding="utf-8")
|
||||||
|
print("💾 Merge completato: solo le righe 'url' del file locale sono state eventualmente aggiornate.")
|
||||||
|
if not changes:
|
||||||
|
print("🔎 Nessun cambiamento effettuato.")
|
||||||
|
else:
|
||||||
|
print("📄 Riepilogo modifiche:")
|
||||||
|
for s, old, new in changes:
|
||||||
|
print(f" - {s}: {old} → {new}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
try:
|
||||||
|
remote_map = load_remote_urls(REMOTE_RAW_URL)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Errore: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
merge_remote_into_local(remote_map, LOCAL_CONFIG_PATH)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Errore durante il merge: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user