A UI Helper for youtube-dl

Related tags

UI UDownHelper
Overview

使用第三方框架:youtube-dl, aria2


使用教程(现在太累了,之后补充详细

  1. 安装homebrew:/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  2. 安装youtube-dl:brew install youtube-dl
  3. 安装aria2:brew install aria2
  4. 确保youtube-dl和aria2是安装在/usr/local/bin目录下,目前是写死的
  5. 快乐下载

已知问题

  • 成下载后CPU占用会飙升,正在排查 [解决方法](# 进程结束task的输出没有停止导致CPU占用过高)

学到的一些知识点:

  1. 打开文件选择框

    在本项目中,需要打开文件框来选择下载目录,这里使用的方法是NSOpenPanel

    关键代码如下

     panel.allowsMultipleSelection = false //不允许选择多项
     panel.canChooseDirectories = true //可以选择目录
     panel.canChooseFiles = false //不可以选择文件
     panel.directoryURL = URL(string: "~/Downloads") //设置默认打开文件路径为用户下载路径
     if panel.runModal() == .OK {
     	folderUrl = panel.url?.path ?? "~/Downloads" //返回文件夹path
     }
  2. 主线程与子线程の一些问题

    在之前制作SocketCS的时候,第一次接触到了DispatchQueue这个东西,当时没研究明白以为无脑DispatchQueue.main.async就完事了,但是制作这次项目的时候,因为需要联网下载视频,运行时间会比较长,我发现如果使用DispatchQueue.main.async的话,程序就会卡主不动。

    经过面向谷歌编程,我发现,问题的出现是因为我对这玩意的理解不透彻,总结来说,DispatchQueue里面有三种队列,分别是Main Queue、Global Queue、Custom Queue,main是主队列,串行运行的,与UI相关的所有操作都必须在main中完成。global是全局队列,并行运行的,运行在后台线程,是系统内共享的全局队列,custom是自定义队列,默认是串行的。

    在后台运行的子任务,如果想将数据推向UI,应使用DispatchQueue.main.async将数据传回主线程,样例如下

    DispatchQueue.global().async{
      //后台代码
      DispatchQueue.main.async{
        //后台运行代码结束后传回数据的代码放这里
      }
    }

    具体的介绍可以看这个链接:[一天精通iOS Swift多线程(GCD)

  3. 后台运行的脚本如何将控制台输出传回UI( ⭐️ 有大坑)

    因为这个项目本质上只是给youtube-dl包装了一个外壳,youtube-dl又是一个命令行程序,所以我想将youtube-dl运行时的控制台信息在SwiftUI中显示出来,程序运行的时候,我将控制台输出输出到pipe管道中,面向谷歌编程后,大概明白需要使用一段简单的代码

    DispatchQueue.main.async {
      ...
    	let pipe = Pipe()
    	task.standardOutput = pipe
    	let outHandle = pipe.fileHandleForReading
    	outHandle.readabilityHandler = { pipe in
          var line = String(data: pipe.availableData,encoding: .utf8)
          print(line)
      }
      do{
        try task.run()
      }catch{
        print("运行错误")
      }
    }
    

    readabilityHandler是OS X 10.7后开始提供的功能,可以读取管道中的实时内容。但是我在使用了这个代码之后,仍然没法Swift控制台中看到实时的信息,只有当整个下载流程结束之后,才会一次性将所有的信息打印出来,在折腾了一个多小时之后,我发现少写了一行代码……

       DispatchQueue.main.async {
         ...
       	let pipe = Pipe()
       	task.standardOutput = pipe
       	let outHandle = pipe.fileHandleForReading
       	outHandle.readabilityHandler = { pipe in
             var line = String(data: pipe.availableData,encoding: .utf8)
             print(line)
         }
         do{
           try task.run()
         }catch{
           print("运行错误")
         }
       }
       task.waitUntilExit()//←没有写这个就不会实时显示

    个人理解,如果没有使用waitUntilExit(),运行的脚本就会直接进入后台,从而readabilityHandler无法读取到脚本的控制台输出。

  4. 关闭App Sandbox

    关闭App Sandbox后的应用无法提交到App Store,但这问题不大,本身就只是小工具,如果不关闭沙盒的话,我的程序就没办法直接读取电脑中的内容,也就没办法控制youtube-dl程序进行下载了,所以,要在Entitlement文件中将该选项设置为关。

    WX20211022-195522@2x.png

  5. 文件路径使用相对路径

    起初我的文件URL写成URL(fileURLWithPath:~/Downloads)时,程序会崩溃,但如果我指定绝对路径的话,就不方便程序的传播,经过修改,可以使用相对路径,如下

    URL(fileURLWithPath: (NSString(string:"~/Downloads").expandingTildeInPath))
  6. 适配深色模式

    最初我习惯用.preferredColorScheme(.light)来强制应用使用浅色模式,这种方式在iOS上蛮好用的,因为iOS应用都是全屏运行的,并不会有什么违和感,但是在mac上运行时,系统切换到了深色模式,应用还保留在浅色模式,会非常突兀,解决的方法也很简单,在Assets里添加ColorSet即可,可以为深色模式手动设定颜色,使用的时候使用以下样例即可

    .foregroundColor(Color("textColor"))//textColor是我给ColorSet起得名字

    WX20211022-224119@2x.png

  7. 莫名其妙的环境变量引起的问题

    这个应用的基础功能是调用youtube-dl来下载视频,但关键核心其实是aria2外挂下载器来加速,但起初按照正确的命令配置后,下载的时候始终无法调用到aria2下载器加速,仍然是使用youtube-dl本身的下载功能,在琢磨了一整天仍然不知道为什么后,我尝试在代码中加入环境变量,没想到意外地解决了这个问题

    var environment =  ProcessInfo.processInfo.environment
    environment["PATH"] = "/usr/local/bin" //← youtube-dl和aria2c可执行程序所在的目录
    task.environment = environment

    561a803262669f1069cce8cf76766b49.jpg

  8. 简单的用户配置存储

    我在程序里设置了默认下载目录为~/Downloads,但也许有用户会希望能更改默认存储位置,并希望下次程序启动的时候能记住此前的选择,这样就不用每次都选择目录了。这里选择了一个非常简单的方法,在SwiftUI2.0中提出了三种新的用于数据持久性的新属性包装器

    @AppStorage
    @StateObject
    @SceneStorage

    在本项目里使用的是@AppStorage,实现了能够保存用户下载目录和可执行文件路径的功能,在Stack Overflow上有一段解释,个人觉得讲的蛮清楚的SwiftUI: What is @AppStorage property wrapper

    简单来说,@AppStorage是一种很方便的方法用于从UserDefaults里读写数据,并且使用起来跟@State修饰符一样,当数据发生变化的时候它会自动将数据保存到UserDefaults中。代码举例来说:

    @State var dirUrl = NSString(string:"~/Downloads").expandingTildeInPath

    改成以下即可:

    @AppStorage("dirUrl") var dirUrl:String = NSString(string:"~/Downloads").expandingTildeInPath

    它的功能与以下代码等价:

    @State var dirUrl: String = NSString(string:"~/Downloads").expandingTildeInPath {
        get {
            UserDefaults.standard.string(forKey: "dirUrl")
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "dirUrl")
        }
    }

    @AppStorage的行为与@State相似,当数据发生变化的时候,也会驱动UI界面重新绘制。默认情况下,@AppStorage会使用UserDefaults.standard来存储数据,但是你也可以自定义使用你自己的UserDefault存储。

  9. 进程结束task的输出没有停止导致CPU占用过高

经过很多次测试,最终猜测占用过高的原因可能跟控制台输出有关,注释掉输出部分的代码后,程序运行后不会再出现CPU占用过高的情况。控制台输出原本的代码如下:

task.standardOutput = pipe
let outHandle = pipe.fileHandleForReading
DispatchQueue.main.async {
    outHandle.readabilityHandler = { pipe in
        let line = String(data: pipe.availableData,encoding: .utf8)
            DispatchQueue.main.async {
                self.consoleOutput = line ?? "nil output"
            }
    }
    self.taskStack.append(taskTrack(process: task, taskType: opMode, runningType: runMode))
}

再次测试,发现当程序结束之后,管道的readabilityHandler仍然在不断地读取内容,导致CPU占用过高,所以应该要在程序停止后释放。

最终改成如下:

DispatchQueue.main.async {
    outHandle.readabilityHandler = { pipe in
        let line = String(data: pipe.availableData,encoding: .utf8)
        if task.isRunning {
            DispatchQueue.main.async {
                self.consoleOutput = line ?? "nil output"
            }
        }else{
            outHandle.readabilityHandler = nil //释放
        }
    }
    self.taskStack.append(taskTrack(process: task, taskType: opMode, runningType: runMode))
}

免责声明

  • 本软件只是个UI,请合法使用,仅供个人学习与交流使用,严禁用于商业以及不良用途。
  • 如有发现任何商业行为以及不良用途,软件作者有权撤销使用权。
  • 使用本软件所存在的风险将完全由其本人承担,软件作者不承担任何责任。
  • 因不当使用本软件而导致的任何意外、疏忽、合约毁坏、诽谤、版权或其他知识产权侵犯及其所造成的任何损失,本软件作者概不负责,亦不承担任何法律责任。
  • 本声明未涉及的问题请参见国家有关法律法规,当本声明与国家有关法律法规冲突时,以国家法律法规为准。
  • 本软件相关声明版权及其修改权、更新权和最终解释权均属软件作者所有。
You might also like...
Helper/wrapper for mautrix-imessage for jailbroken devices

Brooklyn This readme is out-of-date. Blame Ethan, he's working on it. Components Rubicon "The die is cast." Crosses Apple's last river between IMCore

UITest helper library for creating readable and maintainable tests

UITestHelper General information When creating UI tests you will see that you are often repeating the same pieces of code. The UITestHelper library wi

A set of helper classes and functions in Swift

SwiftTools This is a set of tools written in Swift that add some sugar and some small functionality. Mainly meant for small projects and scripts, as a

Sweet-swift - Make Swift Sweet by Gracefully Introducing Syntactic Sugar, Helper Functions and Common Utilities

Sweet Swift Make Swift Sweet by Gracefully Introducing Syntactic Sugar, Helper F

Transcription Helper - iOS application for assisting in transcribing audio files
Transcription Helper - iOS application for assisting in transcribing audio files

Transcription Helper This is an iOS application written in Objective-c for assisting the people who want to work out a piece of audio, in order to wri

iOS helper library that contains commonly used code in Uptech iOS projects

iOS helper library that contains commonly used code in Uptech iOS projects.

iONess is HTTP Request Helper for iOS platform used by HCI iOS App

iONess iONess (iOS Network Session) is HTTP Request Helper for the iOS platform used by Home Credit Indonesia iOS App. It uses Ergo as a concurrent he

An easy to integrate Model Based Google Maps Helper (SVHTTPClient, AFNetworking) That lets you Geo Code , Reverse Geocode, Get Directions , Places Autocomplete.

GoogleMapsHelper Read Me in Russian : http://gargo.of.by/googlemapshelper/ A GOOGLE MAPS Helper that help you do multiple tasks like HOW TO USE // usi

MMPlayerView - Custom AVPlayerLayer on view and transition player with good effect like youtube and facebook
MMPlayerView - Custom AVPlayerLayer on view and transition player with good effect like youtube and facebook

MMPlayerView Demo-Swift List / Shrink / Transition / Landscape MMPlayerLayer ex. use when change player view frequently like tableView / collectionVie

Open Source project for watching YouTube channel playlists.
Open Source project for watching YouTube channel playlists.

YouTube Channel Watcher An open source project build using SwiftUI and Combine that lets you monitor a variety of different YouTube channels and their

A Safari extension that redirects Twitter, YouTube, Reddit, and more to privacy friendly alternatives.
A Safari extension that redirects Twitter, YouTube, Reddit, and more to privacy friendly alternatives.

Privacy Redirect for Safari A configurable web extension that redirects Twitter, YouTube, Reddit, Google Maps, Google Search, and Google Translate to

YouTubePlayerKit  A Swift Package to easily play YouTube videos 📺
YouTubePlayerKit A Swift Package to easily play YouTube videos 📺

A Swift Package to easily play YouTube videos 📺

YouTube player for SwiftUI
YouTube player for SwiftUI

SwiftUI YouTube Player for iOS and MacOS Fully functional, SwiftUI-ready YouTube player for iOS 14+ and MacOS 11+. Actions and state are both delivere

YouTube video player for iOS, tvOS and macOS
YouTube video player for iOS, tvOS and macOS

About XCDYouTubeKit is a YouTube video player for iOS, tvOS and macOS. Are you enjoying XCDYouTubeKit? You can say thank you with a tweet. I am also a

iOS music player app that downloads music from the internet, even YouTube
iOS music player app that downloads music from the internet, even YouTube

About YouTag is an iOS music player app that downloads music from the internet, even YouTube, and manages it in a local library. Music videos can also

Lightweight YouTube Downloader for iOS
Lightweight YouTube Downloader for iOS

DownTube DownTube is a very lightweight app that allows you to download any YouTube video for offline use. Note: this app goes against YouTube's TOS a

:musical_note: A Mac app wrapper for music.youtube.com
:musical_note: A Mac app wrapper for music.youtube.com

A simple Mac app wrapper using WKWebView for YouTube Music that allows YouTube Music to run as a standalone process. Features Media Keys Keyboard shor

YouTube video player for iOS, tvOS and macOS
YouTube video player for iOS, tvOS and macOS

About XCDYouTubeKit is a YouTube video player for iOS, tvOS and macOS. Are you enjoying XCDYouTubeKit? You can say thank you with a tweet. I am also a

Swift library for embedding and controlling YouTube videos in your iOS applications via WKWebView!

YouTubePlayer Embed and control YouTube videos in your iOS applications! Neato, right? Let's see how it works. 0.7.0 Update: WKWebView breaking change

Releases(v1.0.0)
Owner
Derek Jing
小汤河职业技术学院毕业学生 我是废物!
Derek Jing
YoutubeKit is a video player that fully supports Youtube IFrame API and YoutubeDataAPI for easily create a Youtube app

YoutubeKit YoutubeKit is a video player that fully supports Youtube IFrame API and YoutubeDataAPI to easily create Youtube applications. Important Ref

Ryo Ishikawa 555 Dec 28, 2022
Google Directions API helper for iOS, written in Swift

PXGoogleDirections Google Directions API SDK for iOS, entirely written in Swift. ?? Features Supports all features from the Google Directions API as o

Romain L 268 Aug 18, 2022
Helper functions for saving text in Keychain securely for iOS, OS X, tvOS and watchOS.

Helper functions for storing text in Keychain for iOS, macOS, tvOS and WatchOS This is a collection of helper functions for saving text and data in th

Evgenii Neumerzhitckii 2.3k Dec 28, 2022
Fashion is your helper to share and reuse UI styles in a Swifty way.

Fashion is your helper to share and reuse UI styles in a Swifty way. The main goal is not to style your native apps in CSS, but use a set

Vadym Markov 124 Nov 20, 2022
Trackable is a simple analytics integration helper library. It’s especially designed for easy and comfortable integration with existing projects.

Trackable Trackable is a simple analytics integration helper library. It’s especially designed for easy and comfortable integration with existing proj

Vojta Stavik 145 Apr 14, 2022
A custom logger implementation and Task Local helper for swift-log

LGNLog A custom logger implementation and TaskLocal helper for Swift-Log. Why and how This package provides two and a half things (and a small bonus):

17:11 Games 0 Oct 26, 2021
Swift sample app for running privileged operations on macOS using a helper tool

SwiftAuthorizationSample demonstrates how to run privileged operations on macOS using a helper tool managed by launchd. This sample was created with t

null 31 Dec 20, 2022
Google Directions API helper for iOS, written in Swift

PXGoogleDirections Google Directions API SDK for iOS, entirely written in Swift. ?? Features Supports all features from the Google Directions API as o

Romain L 268 Aug 18, 2022
An extremely simple JSON helper written in Swift.

Alexander Alexander is an extremely simple JSON helper written in Swift. It brings type safety and Foundation helpers to the cumbersome task of JSON u

HODINKEE 36 Sep 15, 2022
A quick helper for setting attributed texts to UILabel.

UILabelAttributedTextHelper A quick helper for setting attributed texts to UILabel. Sample usage: label.setAttributedText( leadingText: "H

Glenn Posadas 5 Aug 24, 2022