kewlbear / YoutubeDL-iOS

Swift package of youtube_dl python module for iOS
MIT License
112 stars 35 forks source link

Calling YoutubeDL.extractInfo(url:) results in EXC_BAD_ACCESS #9

Open levitatingpineapple opened 1 year ago

levitatingpineapple commented 1 year ago

Hello 👋

Thanks for maintaining these repositories!

I'm experiencing a crash, when trying to extract info from a video. The following test is also failing with 🛑 EXC_BAD_ACCESS The crash appears to be originating from FFmpeg-iOS-Support dependency.

Test

The following test has been crashing as well: https://github.com/kewlbear/YoutubeDL-iOS/blob/6621fd127d373b42fff86e2ba2d472f7a7e48a20/Tests/YoutubeDL_iOSTests/YoutubeDLTests.swift#L35-L41

Workaround

Although I was not able to determine the cause of the crash, I currently have a workaround for extracting info by making a direct call:

// ✅ Passing
func testDirectExtractInfo() async throws {
    let youtubeDL: PythonObject = try await YtDlp().yt_dlp.YoutubeDL(["nocheckcertificate": true])
    let info = try PythonDecoder()
        .decode(
            Info.self,
            from: youtubeDL
                .extract_info
                .throwing
                .dynamicallyCall(withKeywordArguments: ["": "WdFj7fUnmC0", "download": false])
        )
    XCTAssertEqual(info.title, "YoutubeDL iOS app demo")
}
leonx98 commented 1 year ago

+1

f0enix commented 1 year ago

@levitatingpineapple i am having same issue. i do not want to download the video but only extract the info. Using the provided work-around does not work reliably with all videos. For example if you apply it to this url: https://video.kenh14.vn/si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053.chn it will crash because there is no formats key from the response which makes the decode to Info fail.

{
    "id":"si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-1685943258732976645813",
    "title":"Sĩ tử Hà Nội nô nức đến Văn Miếu cầu may trước kỳ thi vào lớp 10",
    "timestamp":"None",
    "description":"VOV.VN - Kỳ thi tuyển sinh lớp 10 THPT công lập năm nay sẽ được tổ chức trong thời gian 2 ngày từ 10-11/6 gồm 3 môn Toán, Ngữ văn, Ngoại ngữ. Những ngày này, rất đông sĩ tử và phụ huynh tới Văn Miếu - Quốc Tử Giám thắp hương, cầu may mắn.",
    "thumbnail":"https://kenh14cdn.com/203336854389633024/2023/6/5/.si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-1685943258732976645813.mp4.jpg",
    "age_limit":0,
    "uploader":"video.kenh14.vn",
    "http_headers":{
       "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.115 Safari/537.36",
       "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
       "Accept-Language":"en-us,en;q=0.5",
       "Sec-Fetch-Mode":"navigate",
       "Referer":"https://video.kenh14.vn/si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053.chn"
    },
    "url":"https://kenh14cdn.com/203336854389633024/2023/6/5/si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-1685943258732976645813.mp4",
    "webpage_url":"https://video.kenh14.vn/si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053.chn",
    "original_url":"https://video.kenh14.vn/si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053.chn",
    "webpage_url_basename":"si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053.chn",
    "webpage_url_domain":"video.kenh14.vn",
    "extractor":"generic",
    "extractor_key":"Generic",
    "playlist":"None",
    "playlist_index":"None",
    "thumbnails":[
       {
          "url":"https://kenh14cdn.com/203336854389633024/2023/6/5/.si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-1685943258732976645813.mp4.jpg",
          "id":"0"
       }
    ],
    "display_id":"si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-1685943258732976645813",
    "fulltitle":"Sĩ tử Hà Nội nô nức đến Văn Miếu cầu may trước kỳ thi vào lớp 10",
    "requested_subtitles":"None",
    "_has_drm":"None",
    "ext":"mp4",
    "protocol":"https",
    "resolution":"None",
    "dynamic_range":"SDR",
    "aspect_ratio":"None",
    "video_ext":"mp4",
    "audio_ext":"none",
    "format_id":"0",
    "format":"0 - unknown"
 }

Screenshot 2023-06-05 at 12 57 48

Here is the ios log:

[generic] Extracting URL: https://video.kenh14.vn/si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053.chn
[generic] si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] si-tu-ha-noi-no-nuc-den-van-mieu-cau-may-truoc-ky-thi-vao-lop-10-326053: Extracting information
YoutubeDL/PythonDecoder.swift:112: Fatal error: Unexpectedly found nil while unwrapping an Optional value
2023-06-05 12:57:37.342825+0400 Y[2360:275756] YoutubeDL/PythonDecoder.swift:112: Fatal error: Unexpectedly found nil while unwrapping an Optional value

If i use the yt-dlp directly from command line on my mac, it does not crash and download the file successfully.

scriptprojectsdev commented 8 months ago

I'm having this same problem, but the work around doesn't work at all

scriptprojectsdev commented 8 months ago

I found a workaround to the issue until it's fixed permanently. It seems that the last commit broke things. You can bypass it by following these steps:

  1. Click on your project name in the file explorer (Project navigator)
  2. Select your project name (above targets)
  3. Click on package dependencies
  4. Find YoutubeDL-iOS and change the dependency rule to commit
  5. Enter this commit 7eaa8312ffc33fe55912b5b6f5a2acdda283086e in the box to the right and press enter
  6. Things should work
dolphinysaru commented 6 months ago

@scriptprojectsdev @kewlbear Even with that commit (0.0.8) we still have a pretty high percentage of crashes. Does your app run stably? Doesn't it crash? 스크린샷 2024-04-18 오후 2 03 38

HamstyDeveloper commented 6 months ago

are the code is working fine run debug mood. but when I create a release it is crashing on.

try await yt_dlp(argv: argv) { dict in info = dict["info_dict"]

        let status = String(dict["status"]!)

        self.progress.localizedDescription = nil

        switch status {
            case "downloading":
                self.progress.kind = .file
                self.progress.fileOperationKind = .downloading
                if #available(iOS 16.0, *) {
                    self.progress.fileURL = URL(filePath: String(dict["tmpfilename"]!)!)
                } else {
                    // Fallback on earlier versions
                }
                self.progress.completedUnitCount = Int64(dict["downloaded_bytes"]!) ?? -1
                self.progress.totalUnitCount = Int64(Double(dict["total_bytes"] ?? dict["total_bytes_estimate"] ?? Python.None) ?? -1)
                self.progress.throughput = Int(dict["speed"]!)
                self.progress.estimatedTimeRemaining = TimeInterval(dict["eta"]!)
            case "finished":
                print(#function, dict["filename"] ?? "no filename")
                files.append(String(dict["filename"]!)!)
                formats.append(info!)

                // Reset progress indicators or perform any necessary actions when downloading is complete
                self.progress.kind = nil
                self.progress.completedUnitCount = 0
                self.progress.totalUnitCount = 0
                self.progress.throughput = 0
                self.progress.estimatedTimeRemaining = 0
            default:
                print(#function, dict)
        }
    } log: { level, message in
        print(#function, level, message)

        if level == "error" || message.hasSuffix("has already been downloaded") {
            error = message
        }
    } makeTranscodeProgressBlock: {
            self.progress.localizedDescription = NSLocalizedString("Transcoding...", comment: "Progress description")
            self.progress.completedUnitCount = 0
            self.progress.totalUnitCount = 100

            let t0 = ProcessInfo.processInfo.systemUptime

            return { (progress: Double) in
                print(#function, "transcode:", progress)
                let elapsed = ProcessInfo.processInfo.systemUptime - t0
                let speed = progress / elapsed
                let ETA = (1 - progress) / speed

                guard ETA.isFinite else { return }

                self.progress.completedUnitCount = Int64(progress * 100)
                self.progress.estimatedTimeRemaining = ETA
       }
    }
    if let error {
        throw NSError(domain: "App", code: 1, userInfo: [NSLocalizedDescriptionKey: error])
    }
    return (info, files, formats)
}
HamstyDeveloper commented 6 months ago

issue is. here in this file func loadPythonModule(downloadPythonModule: Bool = true) async throws -> PythonObject { if Py_IsInitialized() == 0 { PythonSupport.initialize() }

    if !FileManager.default.fileExists(atPath: Self.pythonModuleURL.path) {
        guard downloadPythonModule else {
            throw YoutubeDLError.noPythonModule
        }
        try await Self.downloadPythonModule()
    }

    let sys = try Python.attemptImport("sys")
    if !(Array(sys.path) ?? []).contains(Self.pythonModuleURL.path) {
        injectFakePopen(handler: popenHandler)

        sys.path.insert(1, Self.pythonModuleURL.path)
    }

    let pythonModule = try Python.attemptImport("yt_dlp")
    version = String(pythonModule.version.__version__)
    return pythonModule
}
kewlbear commented 6 months ago

are the code is working fine run debug mood. but when I create a release it is crashing on.

try await yt_dlp(argv: argv) { dict in info = dict["info_dict"]

        let status = String(dict["status"]!)

        self.progress.localizedDescription = nil

        switch status {
            case "downloading":
                self.progress.kind = .file
                self.progress.fileOperationKind = .downloading
                if #available(iOS 16.0, *) {
                    self.progress.fileURL = URL(filePath: String(dict["tmpfilename"]!)!)
                } else {
                    // Fallback on earlier versions
                }
                self.progress.completedUnitCount = Int64(dict["downloaded_bytes"]!) ?? -1
                self.progress.totalUnitCount = Int64(Double(dict["total_bytes"] ?? dict["total_bytes_estimate"] ?? Python.None) ?? -1)
                self.progress.throughput = Int(dict["speed"]!)
                self.progress.estimatedTimeRemaining = TimeInterval(dict["eta"]!)
            case "finished":
                print(#function, dict["filename"] ?? "no filename")
                files.append(String(dict["filename"]!)!)
                formats.append(info!)

                // Reset progress indicators or perform any necessary actions when downloading is complete
                self.progress.kind = nil
                self.progress.completedUnitCount = 0
                self.progress.totalUnitCount = 0
                self.progress.throughput = 0
                self.progress.estimatedTimeRemaining = 0
            default:
                print(#function, dict)
        }
    } log: { level, message in
        print(#function, level, message)

        if level == "error" || message.hasSuffix("has already been downloaded") {
            error = message
        }
    } makeTranscodeProgressBlock: {
            self.progress.localizedDescription = NSLocalizedString("Transcoding...", comment: "Progress description")
            self.progress.completedUnitCount = 0
            self.progress.totalUnitCount = 100

            let t0 = ProcessInfo.processInfo.systemUptime

            return { (progress: Double) in
                print(#function, "transcode:", progress)
                let elapsed = ProcessInfo.processInfo.systemUptime - t0
                let speed = progress / elapsed
                let ETA = (1 - progress) / speed

                guard ETA.isFinite else { return }

                self.progress.completedUnitCount = Int64(progress * 100)
                self.progress.estimatedTimeRemaining = ETA
       }
    }
    if let error {
        throw NSError(domain: "App", code: 1, userInfo: [NSLocalizedDescriptionKey: error])
    }
    return (info, files, formats)
}

Did you check #14?

HamstyDeveloper commented 6 months ago

I changed the Strip style to Debugging and additionally changed the Enable Testability value to Yes. Thank you 스크린샷 2024-02-06 오후 2 13 47

Originally posted by @dolphinysaru in https://github.com/kewlbear/YoutubeDL-iOS/issues/14#issuecomment-1928799420