尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
Fetch by Fisola — a YouTube and TikTok downloader with a dark, hairline-drawn
UI, built so every file it saves imports straight into Premiere Pro.
Downloaders happily hand you files that play in VLC but that Premiere refuses to
import. Fetch checks every download before it calls itself done, and repairs the
three things that cause it:
Video is always stream-copied, so the picture is never re-encoded. If a file
can't be fixed losslessly — VP9/AV1 above 1080p — it says so instead of handing
you something broken.查看完整 README ↗Fetch by Fisola — a YouTube and TikTok downloader with a dark, hairline-drawn UI, built so every file it saves imports straight into Premiere Pro.
Downloaders happily hand you files that play in VLC but that Premiere refuses to import. Fetch checks every download before it calls itself done, and repairs the three things that cause it:
| Problem | What Fetch does |
|---|---|
MPEG-TS stream named .mp4 (HLS sources) | Remuxes to real MP4 |
| HE-AAC / HE-AACv2 audio | Re-encodes to AAC-LC |
moov atom after mdat | Rewrites with +faststart |
Video is always stream-copied, so the picture is never re-encoded. If a file can't be fixed losslessly — VP9/AV1 above 1080p — it says so instead of handing you something broken.
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
Fetch looks for bin/ffmpeg and bin/yt-dlp first, then your PATH. The
binaries are not in this repo. Restore them with either:
# ffmpeg — installed by requirements.txt, or system-wide:
brew install ffmpeg
# yt-dlp — current release binary:
mkdir -p bin && curl -L -o bin/yt-dlp \
https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos
chmod +x bin/yt-dlp
Without ffmpeg the app still runs, but warns loudly: no HD merging, no MP4 remux, no AAC-LC audio.
| Variable | Effect |
|---|---|
PORT | Binds 0.0.0.0 and skips opening a browser |
FETCH_DOWNLOAD_DIR | Where downloads land; falls back to ~/Downloads, then temp |
FETCH_COOKIES_FILE | Path to a cookies.txt, the only cookie source a server can use |
FETCH_COOKIES_TXT | The cookie file's contents, for hosts with a read-only disk |
FETCH_PLAYER_CLIENTS | Override the YouTube client fallback chain (; between attempts) |
FETCH_YTDLP_ARGS | Extra yt-dlp flags, e.g. pointing at a PO token provider |
python3 ui_app.py # UI at http://127.0.0.1:8765
python3 youtube_downloader.py URL # CLI
python3 youtube_downloader.py --repair # re-check files already on disk
Saves to ~/Downloads. Quality defaults to 1080p H.264, which is always
Premiere-native; Best goes higher as VP9/AV1, which Premiere cannot import.
Turn on Use browser cookies when YouTube throws bot checks or "page needs to be reloaded". Fetch greys out any browser whose cookie store it cannot read, and if a read fails anyway the download continues without cookies rather than stopping. Safari is normally unreadable: macOS protects its cookie store unless the terminal running Fetch has Full Disk Access.
Launch the app from your own terminal. Started from a sandboxed process it inherits that sandbox, cannot read any browser's cookies, and quietly falls back to lower-quality sources.
yt-dlp's TikTok extractor is currently broken upstream. Fetch falls back to reading the video data from the page directly, preferring H.264. It only runs after yt-dlp fails, so it goes dormant once upstream ships a fix.
wsgi.py exposes the app for platforms that auto-detect Flask, and the
Procfile runs it under gunicorn. PORT in the environment switches the
server to 0.0.0.0 and skips opening a browser.
The download directory is resolved at startup: FETCH_DOWNLOAD_DIR if set,
else ~/Downloads if writable, else the system temp directory. Serverless
sandboxes (AWS Lambda's /home/sbx_user*, for one) mount everything but temp
read-only, which is what the fallback is for.
Hosted, the result row offers Save instead of Show: the file is streamed
from the server to your browser via /api/file, which refuses any path outside
the download directory. Files older than an hour are swept on each new job,
since hosted disks are small and ephemeral.
A hosted instance hides what it cannot do: the playlist and browser-cookie controls disappear, because a server has no browser to read cookies from and can only hand back one file at a time. The API rejects both rather than trusting the client.
YouTube bot-checks cloud IPs, so videos that download fine locally can fail
hosted with "Sign in to confirm you're not a bot". The fix is to supply
cookies: export cookies.txt from a logged-in browser and either point
FETCH_COOKIES_FILE at it or paste the contents into FETCH_COOKIES_TXT
(the practical option on a read-only host — Fetch writes it to a 0600 temp
file at first use).
Export one with Fetch itself, from the browser profile holding that account:
python3 youtube_downloader.py --export-cookies "chrome:Profile 2"
pbcopy < cookies.txt # paste into FETCH_COOKIES_TXT on the host
Use Chrome, Brave, Firefox or Edge. Safari cannot work without Full Disk
Access — macOS protects its cookie store, and the export fails with
[Errno 1] Operation not permitted. Grant your terminal Full Disk Access in
System Settings → Privacy & Security if you must use it, or just use Chrome.
Profile names come from the browser's folder, not its display name: read
Profile Path in chrome://version and pass its last segment.
Use a throwaway YouTube account for this. Those cookies are a live session for whatever account exported them, they sit in your host's environment, and YouTube may flag an account whose session downloads from a datacenter IP.
Before reaching for cookies, note that a refusal names a client, not the
video: Fetch retries across a chain of YouTube player clients
(android_vr,… → yt-dlp defaults → tv,web_safari,…) and only reports
failure once every one is refused. FETCH_PLAYER_CLIENTS retunes that chain
without a redeploy. The robust server-side answer is a PO token provider —
run one as a sidecar and point Fetch at it with FETCH_YTDLP_ARGS.
Ephemeral disks are small: a large download can exhaust the temp volume, and Fetch reports that plainly rather than failing obscurely.
./build_macos_app.sh # produces dist/Fetch by Fisola.app
Drag it to /Applications and launch it like any app. It starts the server,
opens the UI in your browser, and exits — the server keeps running in the
background. Launching it again just reopens the window instead of starting a
second server. Stop it with Quit in the bottom-right of the UI.
The launcher deliberately does not stay running: a bundle whose executable is a long-lived script has no event loop, so macOS answers the next launch with "the application is not responding" and refuses to open it.
The bundle is a launcher, not a frozen copy: it runs this checkout, so editing the code updates the app. Rebuild if you move the project folder. First launch may ask for permission to access the folder the project lives in.
It writes ~/Library/Logs/FetchByFisola.log, which records each launch step —
read it first if the app ever fails to come up.
| File | Role |
|---|---|
core.py | Download, format selection, Premiere-readiness pass |
ui_app.py | Flask server and JSON API |
youtube_downloader.py | CLI |
ui/ | Front end (no build step, no dependencies) |
UI-DESIGN.md | Visual style reference |
build_macos_app.sh | Builds the macOS app bundle |
build/icon.html | Source art for the app icon |