diff --git a/.github/workflows/update_alt_store.yml b/.github/workflows/update_alt_store.yml new file mode 100644 index 0000000..fff7390 --- /dev/null +++ b/.github/workflows/update_alt_store.yml @@ -0,0 +1,76 @@ +name: Update AltStore Source + +on: + workflow_run: + workflows: ["Build ALL"] + types: [completed] + workflow_dispatch: + +jobs: + update-source: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install requests + + - name: Record job start time + id: job_start_time + run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT + + - name: Update AltStore source + id: update_source + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + python update_alt_store.py + git config --global user.name 'GitHub Action' + git config --global user.email 'action@github.com' + git add alt_store.json + if git diff --staged --quiet; then + echo "changes=false" >> $GITHUB_OUTPUT + else + git commit -m "Updated source with latest release" + git push + echo "changes=true" >> $GITHUB_OUTPUT + fi + + - name: Calculate job duration + id: duration + if: always() + run: | + end_time=$(date +%s) + duration=$((end_time - ${{ steps.job_start_time.outputs.start_time }})) + echo "duration=$duration seconds" >> $GITHUB_OUTPUT + + - name: Create job summary + run: | + if [[ "${{ steps.update_source.outputs.changes }}" == "true" ]]; then + echo "## Update Altstore Source Summary 🚀" >> $GITHUB_STEP_SUMMARY + echo "✅ Changes Detected and Applied" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The alt_store.json file has been updated with the latest release information." >> $GITHUB_STEP_SUMMARY + else + echo "## Update Altstore Source Summary 🚀" >> $GITHUB_STEP_SUMMARY + echo "🔍 No Changes Detected" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The alt_store.json file is up to date. No changes were necessary." >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "🕐 Execution Time" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "This job took ${{ steps.duration.outputs.duration }} to complete." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "📆 Next Scheduled Run" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The next scheduled run will be tomorrow at midnight UTC." >> $GITHUB_STEP_SUMMARY diff --git a/alt_store.json b/alt_store.json new file mode 100644 index 0000000..2387e8c --- /dev/null +++ b/alt_store.json @@ -0,0 +1,63 @@ +{ + "name": "Venera", + "identifier": "com.github.wgh136.venera.source", + "website": "https://github.com/venera-app/venera", + "subtitle": "Venera official AltStore Source.", + "description": "This is the official AltStore Source for Venera.\n\n A comic reader that supports reading local and network comics", + "tintColor": "#0784FC", + "iconURL": "https://raw.githubusercontent.com/venera-app/venera/master/assets/app_icon.png", + "apps": [ + { + "beta": false, + "name": "Venera", + "bundleIdentifier": "com.github.wgh136.venera", + "developerName": "wgh136", + "subtitle": "A comic reader that supports reading local and network comics", + "version": "1.4.5", + "versionDate": "2025-06-18", + "versionDescription": "1. Fixed an abnormal single image height issue when \"imagesPerPage > 1\". 379 \r\n2. Fixed an invalid page calculation issue when \"showSingleImageOnFirstPage\" is enabled. \r\n3. Fixed an issue with incorrect reading history when displaying a single image on the first page. \r\n4. Fixed abnormal history recording when pages are not flipped. 392 \r\n5. Fixed an issue where the download task would stop after exiting the reader. 387 \r\n6. Fixed a \"RangeError\" when translating tags. 356 \r\n7. Reset the current folder to null on the favorites page if the folder is invalid. 389 \r\n8. Fixed various issues when using a custom download path on Android. 400 \r\n9. Set the initial chapter to the first downloaded chapter if no history exists when starting to read a local comic. 405 \r\n10. Removed the config file repository URL from the app.", + "downloadURL": "https://github.com/venera-app/venera/releases/download/v1.4.5/venera-ios-1.4.5%2B145.ipa", + "localizedDescription": "A comic reader that supports reading local and network comics", + "iconURL": "https://raw.githubusercontent.com/venera-app/venera/master/assets/app_icon.png", + "tintColor": "#0784FC", + "category": "utilities", + "size": 14960268, + "appPermissions": { + "entitlements": [ + "application-identifier", + "com.apple.security.application-groups", + "get-task-allow", + "keychain-access-groups", + "com.apple.developer.kernel.extended-virtual-addressing", + "com.apple.developer.kernel.increased-memory-limit", + "com.apple.developer.healthkit.background-delivery" + ], + "privacy": { + "NSFaceIDUsageDescription": "The guest app is requesting for this permission.", + "NSPhotoLibraryAddUsageDescription": "The guest app is requesting for this permission." + } + }, + "versions": [ + { + "version": "1.4.5", + "date": "2025-06-18", + "localizedDescription": "1. Fixed an abnormal single image height issue when \"imagesPerPage > 1\". 379 \r\n2. Fixed an invalid page calculation issue when \"showSingleImageOnFirstPage\" is enabled. \r\n3. Fixed an issue with incorrect reading history when displaying a single image on the first page. \r\n4. Fixed abnormal history recording when pages are not flipped. 392 \r\n5. Fixed an issue where the download task would stop after exiting the reader. 387 \r\n6. Fixed a \"RangeError\" when translating tags. 356 \r\n7. Reset the current folder to null on the favorites page if the folder is invalid. 389 \r\n8. Fixed various issues when using a custom download path on Android. 400 \r\n9. Set the initial chapter to the first downloaded chapter if no history exists when starting to read a local comic. 405 \r\n10. Removed the config file repository URL from the app.", + "downloadURL": "https://github.com/venera-app/venera/releases/download/v1.4.5/venera-ios-1.4.5%2B145.ipa", + "size": 14960268 + } + ] + } + ], + "news": [ + { + "appID": "com.github.wgh136.venera", + "caption": "Update of Venera just got released!", + "date": "2025-06-18T09:02:01Z", + "identifier": "release-v1.4.5", + "notify": true, + "tintColor": "#0784FC", + "title": "v1.4.5 - Venera 18/06/25", + "url": "https://github.com/venera-app/venera/releases/tag/v1.4.5" + } + ] +} \ No newline at end of file diff --git a/update_alt_store.py b/update_alt_store.py new file mode 100644 index 0000000..451a93a --- /dev/null +++ b/update_alt_store.py @@ -0,0 +1,150 @@ +import json +import plistlib +import re +import requests +import os +from datetime import datetime + +def prepare_description(text): + text = re.sub('<[^<]+?>', '', text) # Remove HTML tags + text = re.sub(r'#{1,6}\s?', '', text) # Remove markdown header tags + text = re.sub(r'\*{2}', '', text) # Remove all occurrences of two consecutive asterisks + text = re.sub(r'(?<=\r|\n)-', '•', text) # Only replace - with • if it is preceded by \r or \n + text = re.sub(r'`', '"', text) # Replace ` with " + text = re.sub(r'\r\n\r\n', '\r \n', text) # Replace \r\n\r\n with \r \n (avoid incorrect display of the description regarding paragraphs) + return text + +def fetch_latest_release(repo_url): + api_url = f"https://api.github.com/repos/{repo_url}/releases" + headers = { + "Accept": "application/vnd.github+json", + } + try: + response = requests.get(api_url, headers=headers) + response.raise_for_status() + release = response.json() + return release + except requests.RequestException as e: + print(f"Error fetching releases: {e}") + raise + +def get_file_size(url): + try: + response = requests.head(url) + response.raise_for_status() + return int(response.headers.get('Content-Length', 0)) + except requests.RequestException as e: + print(f"Error getting file size: {e}") + return 194586 + +def update_json_file_release(json_file, latest_release): + if isinstance(latest_release, list) and latest_release: + latest_release = latest_release[0] + else: + print("Error getting latest release") + return + + try: + with open(json_file, "r") as file: + data = json.load(file) + except json.JSONDecodeError as e: + print(f"Error reading JSON file: {e}") + data = {"apps": []} + raise + + app = data["apps"][0] + + full_version = latest_release["tag_name"] + tag = latest_release["tag_name"] + # Extract version like 1.4.5 from tag, which may be like 'v1.4.5' + version_match = re.search(r"(\d+\.\d+\.\d+)", full_version) + if version_match: + version = version_match.group(1) + else: + print("Error: Could not parse version from tag_name.") + return + version_date = latest_release["published_at"] + date_obj = datetime.strptime(version_date, "%Y-%m-%dT%H:%M:%SZ") + version_date = date_obj.strftime("%Y-%m-%d") + + description = latest_release["body"] + description = prepare_description(description) + + assets = latest_release.get("assets", []) + download_url = None + size = None + for asset in assets: + # venera-ios-1.4.5+145.ipa + if asset["name"] == f"venera-ios-{version}+{version.replace('.', '')}.ipa": + download_url = asset["browser_download_url"] + size = asset["size"] + break + + if download_url is None or size is None: + print("Error: IPA file not found in release assets.") + return + + version_entry = { + "version": version, + "date": version_date, + "localizedDescription": description, + "downloadURL": download_url, + "size": size + } + + duplicate_entries = [item for item in app["versions"] if item["version"] == version] + if duplicate_entries: + app["versions"].remove(duplicate_entries[0]) + + app["versions"].insert(0, version_entry) + + app.update({ + "version": version, + "versionDate": version_date, + "versionDescription": description, + "downloadURL": download_url, + "size": size + }) + + if "news" not in data: + data["news"] = [] + + news_identifier = f"release-{full_version}" + date_string = date_obj.strftime("%d/%m/%y") + news_entry = { + "appID": "com.github.wgh136.venera", + "caption": f"Update of Venera just got released!", + "date": latest_release["published_at"], + "identifier": news_identifier, + "notify": True, + "tintColor": "#0784FC", + "title": f"{full_version} - Venera {date_string}", + "url": f"https://github.com/venera-app/venera/releases/tag/{tag}" + } + + news_entry_exists = any(item["identifier"] == news_identifier for item in data["news"]) + if not news_entry_exists: + data["news"].append(news_entry) + + try: + with open(json_file, "w") as file: + json.dump(data, file, indent=2) + print("JSON file updated successfully.") + except IOError as e: + print(f"Error writing to JSON file: {e}") + raise + +def main(): + repo_url = "venera-app/venera" + is_nightly = "NIGHTLY_LINK" in os.environ + + try: + fetched_data_latest = fetch_latest_release(repo_url) + json_file = "alt_store.json" + update_json_file_release(json_file, fetched_data_latest) + except Exception as e: + print(f"An error occurred: {e}") + raise + +if __name__ == "__main__": + main()