iOS 26용 SwiftUI 앱(build 7)입니다. Liquid Glass 카드, 시스템 탭·도구 막대와 SF Symbols를 사용해 표준 iOS 앱 구조를 따릅니다.
- 링크 입력과 시스템 붙여넣기 버튼
- 제목, 제작자, 길이, 썸네일 확인
- 동영상 / 오디오 저장 유형 선택
- 최고 품질 / 2160p / 1440p / 1080p / 720p / 480p / 360p / 직접 입력 화질 상한
yt-dlp 기본 사용하나로 원본 포맷·기본 선택 모드를 통합- 고급 옵션의
yt-dlp 인수영역에서-t프리셋(mp4, mkv, mp3, aac, sleep) 선택 - 출력 포맷을 자동/MP4/MOV/WebM/MKV 또는 오디오 포맷으로 선택하고 직접 확장자 입력 가능
- 언어 우선순위 및 자동 생성 자막 옵션, 미디어와 자막 함께 공유
- 제한 없는 yt-dlp 명령줄 인수 입력. 인수가 있으면 앱의 다른 다운로드 옵션보다 우선
- 앱 안에서 최신 yt-dlp wheel 원클릭 설치 및 번들 버전 복구
- 진행률, 속도, 예상 시간, 취소
- 공유 시트에서 링크·형식·화질을 본 앱으로 전달해 바로 다운로드하고, 앱을 열 수 없으면 대기열에 추가
- Dynamic Island·잠금 화면 Live Activity 진행률과 최근 로그 2줄
- 현재 또는 마지막 작업의 로그 500개 저장·공유
- 다운로드 중 무음 오디오 반복 재생으로 백그라운드 실행 유지 시도, 설정에서 끄기
- 기기 내 보관함, Quick Look 미리보기, 공유 및 파일 앱 저장
- 시스템 / 라이트 / 다크 모드
- 앱 내부 CPython과 yt-dlp. 별도 서버 없음
- YouTube EJS를 Apple JavaScriptCore에서 실행하는 네이티브 어댑터
- GitHub Actions의 기기 및 시뮬레이터 빌드 설정
Design/preview.html은 초기 화면 구상을 위한 예전 정적 데모입니다. 현재 SwiftUI 화면과 차이가 있으며 실제 다운로드를 실행하지 않습니다.
Python 엔진 테스트에는 실제 yt-dlp HTTP 다운로드, 영상/오디오 두 파일 검증, Instagram 형태의 HEVC/MP4 + M4A 분리 스트림 폴백, 화질 상한·출력 포맷 선택, 자막 선택, 임의 yt-dlp 인수 파싱과 우선순위, -t 프리셋, SHA-256 검증 업데이트, 취소, JavaScriptCore 제공자 등록과 플랫폼별 확장 모듈 배치가 포함됩니다.
Xcode 컴파일, iPhone 설치, JavaScriptCore에서의 실제 YouTube 챌린지 실행, AVFoundation 병합, 공유 확장, Live Activity, 무음 오디오 백그라운드 실행은 아직 검증하지 못했습니다. 따라서 설치·다운로드 성공이 확인된 배포판이 아니라 빌드 및 기기 검증을 진행할 소스 버전입니다. 아래 Actions 설정은 포함되어 있으며 여기서 실행된 것은 아닙니다.
- 새 GitHub 저장소에 이 폴더의 내용을 업로드합니다.
App,Python,tools,.github가 저장소 루트에 있어야 합니다..github도 업로드해야 합니다. - 저장소의 Actions → iOS build → Run workflow를 실행합니다.
main으로 push해도 실행됩니다. - 빌드가 성공하면
yt-dlp-GUI-unsigned-IPA결과물을 받습니다. - 결과물은 미서명 IPA입니다. 파일을 받는 것만으로 iPhone에 설치할 수 없으며, 본인 기기용 서명과 설치 도구가 별도로 필요합니다. 서명 도구는 앱뿐 아니라 포함된 Python 확장 프레임워크도 서명해야 합니다. 공유·Live Activity 확장도 서명해야 하며 앱과 공유 확장에 동일한 App Group 권한을 포함해야 합니다.
기본 runner는 macos-15이며, 설치된 Xcode 중 26 이상을 자동으로 선택합니다. 해당 runner에 Xcode 26 이상이 없으면 명확한 오류와 함께 종료하므로, 그때 사용 가능한 더 최신 macOS runner로 바꿉니다.
이 프로젝트는 본인의 저장소에 게시하거나 Actions를 원격 실행하지 않았습니다. Apple 계정, 인증서, 프로비저닝 프로파일은 포함하지 않습니다.
Xcode 26 이상과 Python 3.12 이상이 필요합니다.
brew install xcodegen
python3 tools/bootstrap.py
open YTDLPGUI.xcodeprojXcode에서 Signing & Capabilities의 Team을 본인 계정으로 선택하고, 필요하면 Bundle Identifier를 고유한 값으로 수정한 뒤 기기를 선택해 실행합니다. 생성 전 Bundle Identifier를 바꾸려면 project.yml의 앱·두 확장 식별자를 편집합니다. APP_GROUP_IDENTIFIER를 본인 팀에서 등록한 그룹으로 변경하고, 앱과 공유 확장 모두 같은 App Group과 Team으로 서명하세요.
bootstrap은 SHA-256으로 고정한 BeeWare Python 3.13 iOS 지원 패키지와 버전을 고정한 순수 Python 패키지를 가져옵니다. 표준 라이브러리의 네이티브 확장은 Python 3.13 공식 iOS 가이드의 패키징 방식에 따라 프레임워크로 옮기며, .fwork와 .origin으로 Python 로더에 위치를 알려줍니다. 다운로드 또는 빌드 환경에 따라 최초 설치에 시간이 걸릴 수 있습니다.
- 일반 동영상 모드는 H.264 MP4를 우선하지만, 이를 필수 조건으로 두지 않습니다. HEVC/AV1 MP4나 코덱 메타데이터가 불완전한 MP4라도 iOS에서 열 수 있는 컨테이너이면 분리 M4A 오디오와 함께 내려받아 AVFoundation 병합을 시도합니다. Instagram Reels처럼 영상과 오디오가 별도 스트림으로 제공되는 경우를 이 경로에서 처리합니다.
- MP4와 MOV는 AVFoundation passthrough 병합/리먹스를 지원합니다. WebM, MKV 및 직접 입력한 다른 컨테이너는 별도 영상·오디오 병합을 하지 않으므로 해당 확장자의 완성된 영상+오디오 원본 스트림이 있어야 합니다.
- 오디오는 AAC/M4A를 우선합니다. 출력 포맷을 직접 지정한 경우 변환하지 않고 해당 확장자의 원본 오디오를 찾습니다. MP3/FLAC/WAV 변환이 필요하면 yt-dlp 프리셋 또는 직접 인수가 FFmpeg를 요구할 수 있으며 iOS에서는 실패할 수 있습니다.
yt-dlp 기본 사용은 예전의원본 포맷과yt-dlp 기본 선택을 하나로 합친 모드입니다. 앱의 화질·출력 포맷 선택을 건너뛰고 yt-dlp가 사이트의 기본 원본 선택을 직접 수행합니다. yt-dlp가 분리 스트림 병합을 위해 FFmpeg를 요구하면 실패할 수 있습니다.- 고급 옵션의
yt-dlp 인수영역에서-t프리셋을 선택할 수 있습니다. 프리셋은 yt-dlp가 직접 실행하며 다른 메인 다운로드 옵션은 사용하지 않습니다. 같은 영역에 직접 yt-dlp 인수를 입력하면 직접 입력이 최우선이며 선택한-t프리셋도 무시합니다. yt-dlp 인수칸은 별도 허용 목록 없이 yt-dlp 자체 명령줄 파서로 해석합니다. 앱은 링크 전달, 진행률/로그 연결, 취소, 기본 저장 위치와 최종 결과 파일을 보관함으로 가져오는 과정만 관리합니다. 여러 미디어 파일을 만드는 인수, 파일을 만들지 않는 인수, FFmpeg나 외부 실행 파일을 요구하는 인수는 현재 iOS 환경 또는 보관함 구조 때문에 실패할 수 있습니다.- YouTube는 yt-dlp의 추출기와 번들 EJS를 사용합니다. 제공자 등록과 EJS 버전 검증은 통과했지만 JavaScriptCore 호환성과 실제 서비스 상태에 따라 다운로드가 달라질 수 있습니다.
- 무음 오디오 설정은 기본 켜짐입니다. 다운로드 시작 시 16 kHz 모노 PCM 무음 WAV를 반복 재생하고 완료·실패·취소 시 종료합니다. 통화, 앱 강제 종료, 메모리 회수 등으로 작업이 중단될 수 있으며 백그라운드 유지 시간은 보장하지 않습니다.
- 공유 확장은 링크·저장 유형·화질·
yt-dlp 기본 사용을 본 앱으로 전달합니다. 시스템이 앱 열기를 허용하지 않으면 App Group 대기열에 저장합니다. - Dynamic Island와 잠금 화면에는 진행률과 최근 로그를 표시합니다. 로그에서 HTTP URL을 가리지만 공유 전 내용을 확인하세요.
- 설정의 업데이트 버튼은 PyPI 최신 wheel을 SHA-256으로 검증해 설치하고, 로드 실패 시 번들 버전으로 되돌립니다.
python3 -m pip install --target Python/app --no-deps -r requirements-ios.txt
PYTHONPATH=Python/app python3 -m unittest discover -s tests -vActions에는 iOS 26+ 시뮬레이터를 골라 XCTest 5개를 실행하는 단계도 포함합니다. 이 환경에서는 실행하지 않았습니다.
네이티브 브리지 _ios_bridge는 테스트에서 대체합니다. 실제 HTTP 전송은 yt-dlp가 수행하고 네트워크 콘텐츠는 로컬 생성 테스트 데이터만 사용합니다.
- 앱 실행, 화면 회전, 다크 모드, 큰 텍스트 크기
- 본인이 제작한 영상 링크의 정보 확인
- MP4 720p 이하 및 M4A 다운로드, 소리와 재생 확인
- 분리된 영상/오디오의 MP4 병합과 취소
- 파일 앱 → 나의 iPhone → yt-dlp GUI와 공유 저장
- 앱 종료 후 보관함 표시, 파일 삭제
- 네트워크 끊김, 제공되지 않는 화질, 잘못된 링크
- Safari·YouTube 공유 시트에서 앱 선택 → 대기열 추가 → 앱을 직접 열어 다운로드 확인
- Dynamic Island 축약·확장 및 잠금 화면 진행률·로그, Island 탭으로 로그 열기
- 화면 잠금 및 다른 앱 전환 후 실제 파일 증가 확인, 완료·실패·취소 시 오디오 세션 종료
- 다른 음악 앱과 동시 실행, 통화 인터럽트·재개, 설정 끄기, 앱 강제 종료 시 오래된 Live Activity 안내
- 설정에서 yt-dlp 업데이트 → 앱 완전 종료·재실행 → 현재 버전 확인 → 번들 버전 복구
- Instagram Reels, HEVC/MP4 + M4A 분리 스트림, 직접 화질, 출력 포맷 수동 입력,
yt-dlp 기본 사용, 메인-t프리셋, 직접 yt-dlp 인수 우선순위 확인
| 경로 | 역할 |
|---|---|
App/ |
SwiftUI 화면, 상태, 파일 보관, AVFoundation 병합 |
App/Native/ |
CPython C API와 JavaScriptCore 브리지 |
Python/app/downloader.py |
yt-dlp 호출과 형식 선택 |
Python/app/ios_jsc.py |
번들 EJS와 JavaScriptCore 연결 |
project.yml |
XcodeGen 설정 |
tools/bootstrap.py |
iOS Python 런타임과 의존성 설치 |
.github/workflows/ios.yml |
미서명 IPA 및 시뮬레이터 빌드 |
Design/ |
디자인 미리보기 |
tests/ |
엔진 검증 |
Shared/ |
App Group 대기열과 Live Activity 상태 |
ShareExtension/ |
공유 시트 화면 |
LiveActivityExtension/ |
Dynamic Island·잠금 화면 위젯 |
NativeTests/ |
링크·대기열·Activity 상태 XCTest 5개(미실행) |
앱 코드에는 MIT 라이선스를 적용합니다. 의존성의 라이선스는 각각 유지됩니다. yt-dlp와 EJS는 Unlicense, CPython은 PSF 라이선스, BeeWare 지원 도구는 해당 프로젝트의 라이선스, certifi의 인증서 번들은 MPL 2.0 등 자체 라이선스를 따릅니다. bootstrap이 의존성의 라이선스와 패키지 메타데이터를 보존하며, 배포 시 Python 지원 패키지의 OpenSSL 등 추가 의존성 고지도 확인해야 합니다.