Files
Simplaudio/addons/godot-yt-dlp/src/yt_dlp.gd
T

235 lines
6.4 KiB
GDScript

extends Node
signal setup_completed
signal _update_completed
enum Video {MP4, WEBM}
enum Audio {AAC, FLAC, MP3, M4A, OPUS, VORBIS, WAV}
const Downloader = preload("res://addons/godot-yt-dlp/src/downloader.gd")
const yt_dlp_sources: Dictionary = {
"Linux": "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp",
"Windows": "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe",
"macOS": "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos",
}
const ffmpeg_sources: Dictionary = {
"ffmpeg": "https://github.com/Nolkaloid/godot-youtube-dl/releases/latest/download/ffmpeg.exe",
"ffprobe": "https://github.com/Nolkaloid/godot-youtube-dl/releases/latest/download/ffprobe.exe",
}
var _downloader: Downloader
var _thread: Thread = Thread.new()
var _is_setup: bool = false
func is_setup() -> bool:
return _is_setup
func download(url: String) -> Download:
if not _is_setup:
push_error(self, "Not set up.")
return null
return Download.new(url)
func FilesMissing() -> bool:
var executable_name: String = "yt-dlp.exe" if OS.get_name() == "Windows" else "yt-dlp"
if OS.get_name() == "Windows":
if not FileAccess.file_exists("user://%s" % executable_name):
return true
if not FileAccess.file_exists("user://ffmpeg.exe"):
return true
if not FileAccess.file_exists("user://ffprobe.exe"):
return true
elif OS.get_name() == "Linux":
var stuff = OS.execute("bash",PackedStringArray(["-c","ffprobe"]))
print(stuff)
if stuff != 1:
return true
var stuff2 = OS.execute("bash",PackedStringArray(["-c","ffmpeg"]))
print(stuff2)
if stuff2 != 1:
return true
return false
func setup() -> void:
_downloader = Downloader.new()
var executable_name: String = "yt-dlp.exe" if OS.get_name() == "Windows" else "yt-dlp"
if not FileAccess.file_exists("user://%s" % executable_name):
_downloader.download(yt_dlp_sources[OS.get_name()], "user://%s" % executable_name)
await _downloader.download_completed
else:
_thread.start(_update_yt_dlp.bind(executable_name))
await _update_completed
# Wait for the next idle frame to join thread
await (Engine.get_main_loop() as SceneTree).process_frame
_thread.wait_to_finish()
await _setup_ffmpeg()
if OS.get_name() == "Linux":
OS.execute("chmod", PackedStringArray(["+x", OS.get_user_data_dir() + "/yt-dlp"]))
_is_setup = true
setup_completed.emit()
func _setup_ffmpeg() -> void:
if not FileAccess.file_exists("user://ffmpeg.exe"):
if OS.get_name() == "Windows":
_downloader.download(ffmpeg_sources["ffmpeg"], "user://ffmpeg.exe")
await _downloader.download_completed
print(OS.get_distribution_name())
elif OS.get_distribution_name() in ["Ubuntu","Linux Mint","Debian"]:
var stuff = OS.execute("bash",PackedStringArray(["-c","ffmpeg"]))
print(stuff)
if stuff !=1:
push_error("FFMPEG NOT INSTALLED")
print(OS.get_distribution_name())
else:
print(OS.get_distribution_name())
if not FileAccess.file_exists("user://ffprobe.exe"):
if OS.get_name() == "Windows":
_downloader.download(ffmpeg_sources["ffprobe"], "user://ffprobe.exe")
print(OS.get_distribution_name())
await _downloader.download_completed
elif OS.get_name() == "Linux":
var stuff = OS.execute("bash",PackedStringArray(["-c","ffprobe"]))
print(stuff)
if stuff != 1:
push_error("FFPROBE NOT INSTALLED")
print(OS.get_distribution_name())
else:
print(OS.get_distribution_name())
func _update_yt_dlp(filename: String) -> void:
OS.execute("%s/%s" % [OS.get_user_data_dir(), filename], ["--update"])
_thread_finished.call_deferred(_update_completed)
func _thread_finished(name: Signal) -> void:
if name != null:
name.emit()
class Download extends RefCounted:
signal download_completed
signal completely_finished
enum Status {
READY,
DOWNLOADING,
COMPLETED,
}
var _status: Status = Status.READY
var _thread: Thread = null
# Fields
var _url: String
var _destination: String = "user://"
var _file_name: String = "YOUTUBEDOWNLAOD"
var _convert_to_audio: bool = false
var _renameAudioToDiffName:bool = false
var _video_format: Video = Video.WEBM
var _audio_format: Audio = Audio.MP3
var _download_playlist:bool
func _init(url: String):
_url = url
_file_name += Time.get_datetime_string_from_system();
func set_destination(destination: String) -> Download:
_destination = destination
print("destination set: " + destination)
return self
func set_file_name(file_name: String) -> Download:
_file_name = file_name
_renameAudioToDiffName = true
return self
func set_video_format(format: Video) -> Download:
_video_format = format
return self
func convert_to_audio(format: Audio) -> Download:
_audio_format = format
_convert_to_audio = true
return self
func get_status() -> Status:
return _status
func start() -> Download:
if not _status == Status.READY:
push_error(self, "Download previously started.")
return self
_status = Status.DOWNLOADING
_destination = ProjectSettings.globalize_path(_destination)
_thread = Thread.new()
_thread.start(_execute_on_thread)
reference()
return self
func _execute_on_thread() -> void:
var executable: String = OS.get_user_data_dir() + \
("/yt-dlp.exe" if OS.get_name() == "Windows" else "/yt-dlp")
var options_and_arguments: Array = []
if _convert_to_audio:
var format: String = (Audio.keys()[_audio_format] as String).to_lower()
options_and_arguments.append_array(["-x", "--audio-format", format])
else:
var format: String
match _video_format:
Video.WEBM:
format = "bestvideo[ext=webm]+bestaudio"
Video.MP4:
format = "bestvideo[ext=mp4]+m4a"
options_and_arguments.append_array(["--format", format])
var file_path: String = "{destination}" \
.format({
"destination": _destination
})
options_and_arguments.append_array(["--embed-metadata","--embed-thumbnail",str("-o" + "%(title)s.%(ext)s")])
options_and_arguments.append_array(["--no-continue", "-P", file_path, _url])
if _download_playlist:
options_and_arguments.append("--yes-playlist")
else:
options_and_arguments.append("--no-playlist")
print(options_and_arguments)
var output: Array = []
OS.execute(executable, PackedStringArray(options_and_arguments), output)
self._thread_finished.call_deferred()
func _thread_finished():
_status = Status.COMPLETED
self.download_completed.emit()
self.completely_finished.emit()
_thread.wait_to_finish()
unreference()