Skip to content

Commit 883e6c1

Browse files
authored
Create ffmpeg.yml
1 parent f5acfde commit 883e6c1

File tree

1 file changed

+357
-0
lines changed

1 file changed

+357
-0
lines changed

.github/workflows/ffmpeg.yml

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
name: Weekly FFmpeg Build Update
2+
3+
on:
4+
schedule:
5+
# Run every Sunday at 2 AM UTC
6+
- cron: '0 2 * * 0'
7+
workflow_dispatch: # Allow manual trigger
8+
9+
# Add explicit permissions
10+
permissions:
11+
contents: write
12+
packages: write
13+
actions: read
14+
15+
jobs:
16+
update-ffmpeg-builds:
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
23+
- name: Install dependencies
24+
run: |
25+
sudo apt-get update
26+
sudo apt-get install -y p7zip-full curl jq
27+
28+
- name: Get latest FFmpeg builds release info
29+
id: get-builds
30+
run: |
31+
# Get the latest release info from FFmpeg-Builds
32+
LATEST_RELEASE=$(curl -s "https://api.github.com/repos/yt-dlp/FFmpeg-Builds/releases/latest")
33+
RELEASE_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
34+
35+
echo "release_tag=$RELEASE_TAG" >> $GITHUB_OUTPUT
36+
echo "Latest FFmpeg-Builds release: $RELEASE_TAG"
37+
38+
- name: Download and extract FFmpeg builds
39+
run: |
40+
# Create directory for downloads and organized files
41+
mkdir -p downloads
42+
mkdir -p organized/{linux,windows,macos}/{x64,arm64}
43+
44+
# Base URL for downloads
45+
BASE_URL="https://github.com/yt-dlp/FFmpeg-Builds/releases/download/latest"
46+
47+
# Define file mappings based on your code
48+
declare -A DOWNLOADS=(
49+
# Windows builds
50+
["windows-x64-ffmpeg"]="ffmpeg-master-latest-win64-gpl.zip"
51+
["windows-ia32-ffmpeg"]="ffmpeg-master-latest-win32-gpl.zip"
52+
["windows-arm64-ffmpeg"]="ffmpeg-master-latest-winarm64-gpl.zip"
53+
54+
# Linux builds
55+
["linux-x64-ffmpeg"]="ffmpeg-master-latest-linux64-gpl.tar.xz"
56+
["linux-arm64-ffmpeg"]="ffmpeg-master-latest-linuxarm64-gpl.tar.xz"
57+
58+
# macOS custom builds
59+
["macos-arm64-ffmpeg"]="https://www.osxexperts.net/ffmpeg711arm.zip"
60+
["macos-arm64-ffprobe"]="https://www.osxexperts.net/ffprobe711arm.zip"
61+
["macos-x64-ffmpeg"]="https://www.osxexperts.net/ffmpeg71intel.zip"
62+
["macos-x64-ffprobe"]="https://www.osxexperts.net/ffprobe71intel.zip"
63+
)
64+
65+
# Download files
66+
for key in "${!DOWNLOADS[@]}"; do
67+
file="${DOWNLOADS[$key]}"
68+
69+
if [[ $file == https://* ]]; then
70+
# Direct URL download for macOS files
71+
download_url="$file"
72+
filename=$(basename "$file")
73+
else
74+
# GitHub release download
75+
download_url="$BASE_URL/$file"
76+
filename="$file"
77+
fi
78+
79+
echo "Downloading: $filename from $download_url"
80+
curl -L -o "downloads/$filename" "$download_url"
81+
82+
if [ $? -eq 0 ]; then
83+
echo "✅ Downloaded: $filename"
84+
else
85+
echo "❌ Failed to download: $filename"
86+
fi
87+
done
88+
89+
# List downloaded files
90+
echo "Downloaded files:"
91+
ls -la downloads/
92+
93+
- name: Extract and organize files
94+
run: |
95+
cd downloads
96+
97+
# Function to extract different archive types
98+
extract_file() {
99+
local file="$1"
100+
local extract_dir="$2"
101+
102+
mkdir -p "$extract_dir"
103+
104+
case "$file" in
105+
*.zip)
106+
echo "Extracting ZIP: $file"
107+
unzip -q "$file" -d "$extract_dir"
108+
;;
109+
*.tar.xz)
110+
echo "Extracting TAR.XZ: $file"
111+
tar -xf "$file" -C "$extract_dir"
112+
;;
113+
*.7z)
114+
echo "Extracting 7Z: $file"
115+
7z x "$file" -o"$extract_dir"
116+
;;
117+
*)
118+
echo "Unknown archive type: $file"
119+
return 1
120+
;;
121+
esac
122+
}
123+
124+
# Function to find and copy executables
125+
copy_executables() {
126+
local source_dir="$1"
127+
local target_dir="$2"
128+
local platform="$3"
129+
130+
mkdir -p "$target_dir"
131+
132+
# Find ffmpeg and ffprobe executables
133+
if [ "$platform" = "windows" ]; then
134+
find "$source_dir" -name "ffmpeg.exe" -type f -exec cp {} "$target_dir/" \; 2>/dev/null || true
135+
find "$source_dir" -name "ffprobe.exe" -type f -exec cp {} "$target_dir/" \; 2>/dev/null || true
136+
else
137+
find "$source_dir" -name "ffmpeg" -type f -executable -exec cp {} "$target_dir/" \; 2>/dev/null || true
138+
find "$source_dir" -name "ffprobe" -type f -executable -exec cp {} "$target_dir/" \; 2>/dev/null || true
139+
fi
140+
}
141+
142+
# Process each downloaded file
143+
for file in *.zip *.tar.xz *.7z; do
144+
if [ -f "$file" ]; then
145+
echo "Processing: $file"
146+
147+
# Determine platform and architecture from filename
148+
platform=""
149+
arch=""
150+
151+
case "$file" in
152+
*win64*|*win32*|*winarm64*)
153+
platform="windows"
154+
if [[ "$file" == *"win64"* ]]; then
155+
arch="x64"
156+
elif [[ "$file" == *"win32"* ]]; then
157+
arch="ia32" # Keep as ia32 to match your mapping
158+
elif [[ "$file" == *"winarm64"* ]]; then
159+
arch="arm64"
160+
fi
161+
;;
162+
*linux64*|*linuxarm64*)
163+
platform="linux"
164+
if [[ "$file" == *"linux64"* ]]; then
165+
arch="x64"
166+
elif [[ "$file" == *"linuxarm64"* ]]; then
167+
arch="arm64"
168+
fi
169+
;;
170+
*ffmpeg711arm*|*ffprobe711arm*)
171+
platform="macos"
172+
arch="arm64"
173+
;;
174+
*ffmpeg71intel*|*ffprobe71intel*)
175+
platform="macos"
176+
arch="x64"
177+
;;
178+
esac
179+
180+
if [ -n "$platform" ] && [ -n "$arch" ]; then
181+
extract_dir="temp_$(basename "$file" | tr '.' '_')"
182+
extract_file "$file" "$extract_dir"
183+
184+
# For macOS files from osxexperts.net, the executable is directly in the zip
185+
if [[ "$file" == *"osxexperts"* ]]; then
186+
target_dir="../organized/$platform/$arch"
187+
mkdir -p "$target_dir"
188+
189+
# Copy the executable directly (it should be in the root of extracted folder)
190+
if [[ "$file" == *"ffmpeg"* ]]; then
191+
find "$extract_dir" -name "ffmpeg" -type f -exec cp {} "$target_dir/" \; 2>/dev/null || true
192+
elif [[ "$file" == *"ffprobe"* ]]; then
193+
find "$extract_dir" -name "ffprobe" -type f -exec cp {} "$target_dir/" \; 2>/dev/null || true
194+
fi
195+
else
196+
# For regular builds, look in bin directory
197+
copy_executables "$extract_dir" "../organized/$platform/$arch" "$platform"
198+
fi
199+
200+
# Clean up extraction directory
201+
rm -rf "$extract_dir"
202+
else
203+
echo "Could not determine platform/arch for: $file"
204+
fi
205+
fi
206+
done
207+
208+
cd ..
209+
210+
# Make all executables have proper permissions
211+
find organized -type f -exec chmod +x {} \; 2>/dev/null || true
212+
213+
# List organized files
214+
echo "Organized files:"
215+
find organized -type f | sort
216+
217+
- name: Check for existing release
218+
id: check-release
219+
run: |
220+
# Check if release already exists
221+
RELEASE_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \
222+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
223+
"https://api.github.com/repos/${{ github.repository }}/releases/tags/ffmpeg-latest")
224+
225+
echo "release_exists=$RELEASE_EXISTS" >> $GITHUB_OUTPUT
226+
echo "Release check status: $RELEASE_EXISTS"
227+
228+
- name: Delete existing release if it exists
229+
if: steps.check-release.outputs.release_exists == '200'
230+
run: |
231+
# Get release ID
232+
RELEASE_ID=$(curl -s \
233+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
234+
"https://api.github.com/repos/${{ github.repository }}/releases/tags/ffmpeg-latest" | jq -r '.id')
235+
236+
# Delete the release
237+
curl -X DELETE \
238+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
239+
"https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID"
240+
241+
echo "Deleted existing release"
242+
243+
- name: Create new release
244+
id: create-release
245+
run: |
246+
# Get current timestamp
247+
CURRENT_TIME=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
248+
249+
# Create release body
250+
RELEASE_BODY="Latest FFmpeg and FFprobe builds extracted from:
251+
- yt-dlp/FFmpeg-Builds release: ${{ steps.get-builds.outputs.release_tag }}
252+
- macOS builds from osxexperts.net
253+
254+
Updated: $CURRENT_TIME
255+
256+
These are raw executable files ready for use. Only ffmpeg and ffprobe binaries are included."
257+
258+
# Create new release using proper JSON escaping
259+
RESPONSE=$(curl -s -X POST \
260+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
261+
-H "Content-Type: application/json" \
262+
"https://api.github.com/repos/${{ github.repository }}/releases" \
263+
--data-raw "$(cat <<EOF
264+
{
265+
"tag_name": "ffmpeg-latest",
266+
"name": "Latest FFmpeg & FFprobe Builds (Raw)",
267+
"body": $(echo "$RELEASE_BODY" | jq -R -s .),
268+
"draft": false,
269+
"prerelease": false
270+
}
271+
EOF
272+
)")
273+
274+
echo "API Response: $RESPONSE"
275+
276+
# Check if response contains error
277+
if echo "$RESPONSE" | jq -e '.message' > /dev/null 2>&1; then
278+
echo "Error creating release: $(echo "$RESPONSE" | jq -r '.message')"
279+
exit 1
280+
fi
281+
282+
UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.upload_url' | sed 's/{?name,label}//')
283+
284+
if [ "$UPLOAD_URL" = "null" ] || [ -z "$UPLOAD_URL" ]; then
285+
echo "Failed to get upload URL from response"
286+
echo "Full response: $RESPONSE"
287+
exit 1
288+
fi
289+
290+
echo "upload_url=$UPLOAD_URL" >> $GITHUB_OUTPUT
291+
echo "Created release with upload URL: $UPLOAD_URL"
292+
293+
- name: Upload extracted executables
294+
run: |
295+
cd organized
296+
297+
# Upload files from each platform/arch combination
298+
find . -type f | while read file; do
299+
# Remove leading ./
300+
clean_path=${file#./}
301+
# Create descriptive filename: platform-arch-executable
302+
upload_name=$(echo "$clean_path" | tr '/' '-')
303+
304+
echo "Uploading: $file as $upload_name"
305+
306+
# Get file size for progress
307+
file_size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "unknown")
308+
echo "File size: $file_size bytes"
309+
310+
curl -X POST \
311+
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
312+
-H "Content-Type: application/octet-stream" \
313+
--data-binary "@$file" \
314+
"${{ steps.create-release.outputs.upload_url }}?name=$upload_name" \
315+
--progress-bar
316+
317+
if [ $? -eq 0 ]; then
318+
echo "✅ Uploaded: $upload_name"
319+
else
320+
echo "❌ Failed to upload: $upload_name"
321+
fi
322+
done
323+
324+
- name: Verify uploads and summary
325+
run: |
326+
echo "🎉 FFmpeg Build Update Complete!"
327+
echo ""
328+
echo "📦 Sources:"
329+
echo " - yt-dlp/FFmpeg-Builds: ${{ steps.get-builds.outputs.release_tag }}"
330+
echo " - macOS builds: osxexperts.net"
331+
echo ""
332+
echo "🎯 Target: ${{ github.repository }} ffmpeg-latest release"
333+
echo ""
334+
echo "📁 Files uploaded:"
335+
cd organized
336+
file_count=$(find . -type f | wc -l)
337+
echo " Total: $file_count executable files"
338+
echo ""
339+
echo "📋 File breakdown:"
340+
find . -type f | while read file; do
341+
clean_path=${file#./}
342+
upload_name=$(echo "$clean_path" | tr '/' '-')
343+
file_size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null || echo "0")
344+
echo " - $upload_name ($(numfmt --to=iec $file_size 2>/dev/null || echo $file_size) bytes)"
345+
done
346+
347+
# Verify we have the expected files
348+
echo ""
349+
echo "✅ Verification:"
350+
[ -f "windows/x64/ffmpeg.exe" ] && echo " ✓ Windows x64 ffmpeg" || echo " ✗ Windows x64 ffmpeg missing"
351+
[ -f "windows/x64/ffprobe.exe" ] && echo " ✓ Windows x64 ffprobe" || echo " ✗ Windows x64 ffprobe missing"
352+
[ -f "linux/x64/ffmpeg" ] && echo " ✓ Linux x64 ffmpeg" || echo " ✗ Linux x64 ffmpeg missing"
353+
[ -f "linux/x64/ffprobe" ] && echo " ✓ Linux x64 ffprobe" || echo " ✗ Linux x64 ffprobe missing"
354+
[ -f "macos/arm64/ffmpeg" ] && echo " ✓ macOS ARM64 ffmpeg" || echo " ✗ macOS ARM64 ffmpeg missing"
355+
[ -f "macos/arm64/ffprobe" ] && echo " ✓ macOS ARM64 ffprobe" || echo " ✗ macOS ARM64 ffprobe missing"
356+
[ -f "macos/x64/ffmpeg" ] && echo " ✓ macOS Intel ffmpeg" || echo " ✗ macOS Intel ffmpeg missing"
357+
[ -f "macos/x64/ffprobe" ] && echo " ✓ macOS Intel ffprobe" || echo " ✗ macOS Intel ffprobe missing"

0 commit comments

Comments
 (0)