Skip to content

Commit

Permalink
feat: change ytdl method and support Mac
Browse files Browse the repository at this point in the history
  • Loading branch information
Markkop committed Aug 4, 2023
1 parent af0ac84 commit ba3ff25
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 563 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@

## About

This app **downloads** youtube videos using [youtube-dl](https://youtube-dl.org/) and **cuts** them with [ffmpeg](https://www.ffmpeg.org/) given starting and ending times.
Currently it only supports **Windows** e **Linux**.
This app **downloads** youtube videos and **cuts** them with [ffmpeg](https://www.ffmpeg.org/) given starting and ending times.

## Usage

Download the [latest release](https://github.com/Markkop/yt-dlandcut/releases/latest) for Linux (**.appImage**) or Windows (**.exe**) at the [releases](https://github.com/Markkop/yt-dlandcut/releases/) page.
The first time you run this app (or if required `binaries` are not found), the app will download youtube-dl and ffmpeg files according to your OS.
Currently they're being download from [youtube-dl](https://github.com/ytdl-org/youtube-dl/releases/latest) and [ffmpeg-static](https://github.com/eugeneware/ffmpeg-static/releases/latest) latest releases.
Download the [latest release](https://github.com/Markkop/yt-dlandcut/releases/latest) for Linux (**.appImage**), Windows (**.exe**) or MacOS (**.dmg**) at the [releases](https://github.com/Markkop/yt-dlandcut/releases/) page.
The first time you run this app (or if required `binaries` are not found), the app will download ffmpeg files according to your OS.
After finishing, a folder inside your home folder named `yt-dlandcut` will contain the files.
If you run into any problem, please [let me know](https://twitter.com/heymarkkop).

Expand Down
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "yt-dlandcut",
"version": "2.1.0",
"version": "3.0.0",
"description": "Download and Cut an Youtube video by its starting and ending times",
"repository": "github:markkop/yt-dlandcut",
"main": "build/electron/main.js",
"scripts": {
"postinstall": "electron-builder install-app-deps",
"start": "yarn transpile && electron . --trace-warnings",
"start": "export DEV=true && yarn transpile && electron . --trace-warnings",
"transpile": "babel src --out-dir build",
"pack": "electron-builder --dir",
"build": "yarn transpile && electron-builder",
Expand All @@ -26,7 +26,7 @@
"axios": "^0.19.2",
"electron-context-menu": "^1.0.0",
"fluent-ffmpeg": "^2.1.2",
"youtube-dl": "^3.5.0"
"ytdl-core": "^4.11.5"
},
"devDependencies": {
"@babel/cli": "^7.8.4",
Expand All @@ -52,6 +52,10 @@
"target": "portable",
"icon": "public/256x256.ico"
},
"mac": {
"target": "dmg",
"icon": "public/256x256.icns"
},
"publish": "github"
}
}
Binary file added public/256x256.icns
Binary file not shown.
3 changes: 3 additions & 0 deletions src/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export function convertToMp3(inputPath, outputPath, fileName) {
updateStatus(message)
})
.on('error', function (err) {
console.error(err)
updateStatus(`❌ Error while converting: ${err}`)
reject(false)
})
Expand All @@ -70,6 +71,8 @@ export function convertToMp3(inputPath, outputPath, fileName) {
const message = `✅ Video has been converted with success to ${outputFilePath}`
updateStatus(message)
resolve(outputFilePath)
} else {
console.error(err)
}
})
.saveToFile(outputFilePath)
Expand Down
86 changes: 42 additions & 44 deletions src/download.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
import fs from 'fs'
import path from 'path'
import youtubedl from 'youtube-dl'
import { checkAndCreateFolder, updateStatus } from './helpers'
import axios from 'axios'
import { binariesPath, youtubeDlFilePath, ffmpegFilePath } from './settings'
import { binariesPath, ffmpegFilePath } from './settings'
import ytdl from 'ytdl-core'

/**
* Get Youtube's video title
* @param { String } url
* @returns { Promise<String> }
*/
export function getVideoTitle(url = '') {
return new Promise((resolve, reject) => {
function callback(err, info) {
if (err) {
reject(err)
}
const { title } = info

resolve(title)
}
youtubedl.getInfo(url, callback)
})
export async function getVideoTitle(url = '') {
try {
const info = await ytdl.getBasicInfo(url);
const { title } = info.videoDetails;
return title;
} catch (error) {
console.error(error);
throw error;
}
}

/**
* Download youtube video
* @param { String } youtubeUrl youtube video url
* @param { String } downloadPath folder path
* @param { String } fileName file name without extension
* @returns { Promise<String|Boolean>} downloaded file path or false if fail
*/
export function downloadFromYoutube(youtubeUrl, downloadPath, fileName, overwriteFile) {
return new Promise((resolve, reject) => {

export async function downloadFromYoutube(youtubeUrl, downloadPath, fileName, overwriteFile) {
return new Promise(async (resolve, reject) => {
try {
checkAndCreateFolder(downloadPath)
const filePath = path.join(downloadPath, `${fileName}.mp4`)
Expand All @@ -43,13 +34,28 @@ export function downloadFromYoutube(youtubeUrl, downloadPath, fileName, overwrit
return resolve(filePath)
}

const video = youtubedl(youtubeUrl, ['--format=18', '--no-cache-dir'])
video.on('info', function (info) {
const size = info.size / 1000000
const message = `⚙️ Starting download video ${info.title} with size ${size.toFixed(2)}MB`
updateStatus(message)
const info = await ytdl.getInfo(youtubeUrl)
const format = ytdl.chooseFormat(info.formats, {
filter: "audioandvideo",
quality: "highest"
})

updateStatus(`⬇️ Downloading from ${youtubeUrl}`)
const video = ytdl.downloadFromInfo(info, {
quality: format.itag
})

let lastPercent = 0;

video.on('progress', (chunkLength, downloaded, total) => {
const percent = Math.floor((downloaded / total) * 100);
if (percent % 25 === 0 && percent !== lastPercent) {
const message = `⬇️ Downloading video ${fileName}: ${percent}% downloaded`;
updateStatus(message);
lastPercent = percent;
}
});

video.pipe(fs.createWriteStream(filePath))

video.on('end', function () {
Expand Down Expand Up @@ -80,32 +86,23 @@ export function checkAndDownloadBinaries() {
let success = true
const isWin = process.platform === 'win32'
const ffmpegFileName = isWin ? 'ffmpeg.exe' : 'ffmpeg'
const youtubeDlFileName = isWin ? 'youtube-dl.exe' : 'youtube-dl'

const hasYoutubeDl = fs.existsSync(youtubeDlFilePath)
const hasFfmpeg = fs.existsSync(ffmpegFilePath)

if (!hasYoutubeDl && !hasFfmpeg) {
if (!hasFfmpeg) {
updateStatus(
"💡 It looks like this is your first time running this app, I'll download the required files it needs to work. ;)"
)
}
if (!hasYoutubeDl) {
updateStatus(`⚙️ File ${youtubeDlFilePath} not found, downloading...`)
// const youtubeDlUrl = `https://github.com/ytdl-org/youtube-dl/releases/latest/download/${youtubeDlFileName}`
const youtubeDlUrl = `https://github.com/ytdl-org/ytdl-nightly/releases/download/2023.08.01/${youtubeDlFileName}`
success = await downloadFile(youtubeDlUrl, binariesPath, youtubeDlFileName)
}

if (!hasFfmpeg) {
updateStatus(`️ File ${ffmpegFilePath} not found, downloading...`)
updateStatus('⌛️ This one is kinda big, it may take a while :o')
const ffmpegDownloadName = `${process.platform}-${process.arch}`
updateStatus(`️ File ${ffmpegFilePath} not found, downloading...`)
// updateStatus('⌛️ This one is kinda big, it may take a while :o')
const ffmpegDownloadName = `ffmpeg-${process.platform}-${process.arch}`
const ffmpegUrl = `https://github.com/eugeneware/ffmpeg-static/releases/latest/download/${ffmpegDownloadName}`
success = await downloadFile(ffmpegUrl, binariesPath, ffmpegFileName)
}

youtubedl.setYtdlBinary(youtubeDlFilePath)
if (success) {
return resolve(true)
} else {
Expand All @@ -125,7 +122,7 @@ async function downloadFile(url, downloadPath, fileName) {
axios.defaults.adapter = require('axios/lib/adapters/http')

checkAndCreateFolder(downloadPath)
updateStatus(`️ Downloading ${url} to ${downloadPath}`)
updateStatus(`️ Downloading ${url} to ${downloadPath}`)

const downloadFilePath = path.join(downloadPath, fileName)
const writer = fs.createWriteStream(downloadFilePath)
Expand All @@ -138,8 +135,9 @@ async function downloadFile(url, downloadPath, fileName) {
return new Promise((resolve, reject) => {
writer.on('finish', () => {
updateStatus(`✅ Download finished`)
if (process.platform === 'linux') {
fs.chmodSync(downloadFilePath, 0o755)
if (process.platform === 'linux' || process.platform === 'darwin') {
updateStatus(`✅ File permissions applied`)
fs.chmodSync(downloadFilePath, 0o777)
}
resolve(path)
})
Expand Down
3 changes: 3 additions & 0 deletions src/electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ function createWindow() {
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: false,
nodeIntegration: true,
sandbox: false,
},
})

Expand Down
22 changes: 19 additions & 3 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import fs from 'fs'
import { shell } from 'electron'
import { version as currentVersion } from '../package.json'
import axios from 'axios'
import os from 'os'
import { exec } from 'child_process'

/**
* Check if a detected version is newer than current
Expand Down Expand Up @@ -77,9 +79,23 @@ export function getDuration(startTime, endTime) {
* @param { String } path
*/
export function openItem(path) {
shell.openItem(path)
const message = `⚙️ Opening ${path} `
updateStatus(message)
if (typeof shell?.openItem === 'function') {
shell.openItem(path);
} else {
if (os.type() === 'Windows_NT') {
// On Windows
exec(`start "" "${path}"`);
} else if (os.type() === 'Darwin') {
// On MacOS
exec(`open "${path}"`);
} else {
// On Linux
exec(`xdg-open "${path}"`);
}
}

const message = `⚙️ Opening ${path}`;
updateStatus(message);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function downloadAndCut(youtubeUrl, startTime, endTime, options) {
openItem(convertedFile)
}

updateStatus('🎉 Finished! Check your files in your home folder.')
updateStatus(`🎉 Finished! Check your files in your ${outputPath} folder.`)
} catch (error) {
updateStatus(`🤕 Something bad happened :c. Here's what you can try:
- Clean your ${binariesPath} folder and try again
Expand Down
8 changes: 4 additions & 4 deletions src/settings.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { remote } from 'electron'
import os from 'os';
import path from 'path'

const isWin = process.platform === 'win32'

const homePath = remote.app.getPath('home')
const isDev = process.env.DEV === 'true'
const homePath = isDev ? '' : (remote?.app.getPath('home') || os.homedir())
const basePath = path.resolve(homePath, 'yt-dlandcut')
const binariesPath = path.resolve(basePath, 'binaries')
const youtubeDlFilePath = path.resolve(binariesPath, isWin ? 'youtube-dl.exe' : 'youtube-dl')
const ffmpegFilePath = path.resolve(binariesPath, isWin ? 'ffmpeg.exe' : 'ffmpeg')

export { homePath, basePath, binariesPath, youtubeDlFilePath, ffmpegFilePath }
export { homePath, basePath, binariesPath, ffmpegFilePath }
Loading

0 comments on commit ba3ff25

Please sign in to comment.