關於youtube-dl pipe to ffmpeg的用法請教(已解決)

前言: 很多人都不曉得在YouTube上面無論標題寫的什麼無損 flac, 24 bit 甚至 32 bit 音樂格式, 其實最高的音質就是 160kbps opus, 因為 youtube 都會將音樂部分以 opus 或是 mp4a (AAC 或 HEAAC) 格式重新壓縮過, 很多人對 opus 格式很陌生, 有興趣的網友可以自行 google opus 看看, 這個格式非常優秀, 有些網站透過頻域分析發現 opus 是在同樣的 bit rate 下音質最好的有損壓縮格式, 基本上 >= 160kbs 的 opus 格式的音樂就可被承認是 transparent (意思是幾乎無法區分與來源音樂的差異了), 故google 現已將所有影片聲音格式以 opus 方式來儲存(早期的影片也陸續在更換格式中, 有些還未更換的影片仍用 m4a), 其中最高的音質就設定在 160kbps opus, 例如下圖, 標題雖然說音樂是 24bit 192khz
https://youtu.be/D-h6MoF7HLA
關於youtube-dl pipe to ffmpeg的用法請教(已解決)

但最後卻仍是 160 bps opus 為最佳儲存格式
用 youtube-dl -F https://youtu.be/D-h6MoF7HLA 查看所有格式
關於youtube-dl pipe to ffmpeg的用法請教(已解決)


故一些 app 強調可以抓取 youtube 上的音樂,並以 flac 或是 wav 格式儲存, 其實只是將 opus 再轉成 flac或wav 而已, 它已不再是無損音樂, 只是充胖子而已, 所以若要抓取 youtube 上最高音質的音樂,就只有 160kbps opus。

正文開始, 想知道方法的直接跳到 5F 有完整的程式可以參考

我是分隔線===============

小弟想寫個shell script抓取youtube上的音樂(opus格式).

youtube-dl -f bestaudio -o %'(title)s%(ext)s' ${1} -x --audio-format "opus" --audio-quality 0

運作的很好,可是畢竟認識 .opus的手機app不多.故想透過ffmpeg進行變换封裝格式但不重新編碼(recode),避免因recode音質再次變差

ffmepg -i a.opus -codec copy a.ogg可以完美的運行,可是就必須分成兩個動作.

動作一:透過youtube-dl抓取opus音樂並存入硬碟.
動作二:透過ffmpeg至硬碟抓取.opus檔進行轉檔成.ogg

在linux or Dos shell下有一些很好用的指令,例如pipe,下面的例子是利用
youtube-dl將影片抓取後直接(不需存到硬碟)透過pipe以及stdin, stdout將輸出餵給vlc做播放動作
youtube-dl -o - "$url" ¦ vic -

所以我也想透過pipe以及stdin, stdout的方式,重寫shell script.

方法一:(不成功).
youtube-dl -f bestaudio ${1} -o - ¦ ffmpeg -i pipe:0 -codec copy pipe:1 ¦ cat > out.ogg
錯誤訊息: fixed output name but more than one file to download.

方法二:(不成功).
youtube-dl -f bestaudio ${1} -o - ¦ ffmpeg -i pipe:0 -codec copy pipe:1
錯誤訊息: fixed output name but more than one file to download.

看起來無論指不指定輸出檔都會得到相同的錯誤,也分段測試過 ¦前的動作確定只會抓一個檔案

測試了很久始終無法解決,在此請教熟悉linux shell command的前輩能提點一二,感謝!
文章關鍵字
亂寫了一下 僅供參考(環境為Windows 10的Linux子系統 安裝Debian)


指令(全部為同一行):
url="https://www.youtube.com/watch?v=xxGUHWcf5To" && name=$(youtube-dl --get-title "$url") && youtube-dl -f bestaudio "$url" -o - | ffmpeg -y -i - -codec copy -f ogg "$name".ogg && unset name && unset url
s912a42 wrote:
亂寫了一下 僅供參考...(恕刪)


感謝大大熱心解答
小弟我參考大大的寫法,也試出來了,以下是我的寫法,非常感謝

title=$(youtube-dl -s -e ${1})
youtube-dl -f bestaudio ${1} -o - --exec 'ffmpeg -i - -codec copy pipe:1' > ${title}.ogg


剛剛測試下載 playlist, 發現會有問題

可以請大大測試一下這個playlist嗎? 再次感謝

https://www.youtube.com/playlist?list=PLlSd7HE5NvCUH5f1NxkFarm9p0gqYJIon
jocoliu wrote:
感謝大大熱心解答小弟...(恕刪)


大概知道問題在哪裡了
name=$(youtube-dl --get-title "$url")

若是一次抓取一個檔案,上面的指令沒有問題,可是若是一次抓取一個清單的歌(>1首歌), 此時 name 的內容就出現問題了

看來應該是無解了, 只好採取兩段方式處理了, 先抓取 *.webm(codec:opus), 再用 ffmpeg 變更容器為 ogg(codec:opus), 這樣就能一次抓取整個歌單的歌, anyway, 還是非常感謝 S912a42 大大的熱心解答
以下是小弟我最後的寫法
youtube-dl -f bestaudio -o '%(title)s.%(ext)s' ${1}

for file in *.webm; do ffmpeg -i "$file" -codec copy "${file%%.webm*}".ogg; done && rm *.webm
問題終於解決了, 用我有限的知識撰寫的 bash shell script, 在高手眼裡可能認為程式寫的有點拙劣, 因為我業餘的, 只花了幾天參考鳥哥的教學文章努力解決我遇到的問題, 以下是完整的程式碼, 給有興趣的人參考使用

程式說明, 利用 youtube-dl 在android 手機模擬終端下執行, 下載 youtube 上的高音質格式音樂(webm with 160kbps opus codec ), 並直接轉換容器格式為普遍較普遍的 ogg, 音樂未重新編碼, 所以不會再有轉換損失。

使用方式
bash 此程式.sh youtube網址 start_no

start_no 是在下載歌曲清單的所有歌曲時,可以指定從第幾首歌開始下載, 不指定時會自動從第一首歌開始下載

#!/bin/bash

declare -i n

clear #clear screen

n=0 #set counter to 0
start=${2} #get the second parameter from user
test -z $start && start=1 # if the 2nd parameter is empty then set it to 1, you can ignore the 2nd parameter if you want start from 1

echo '============= start from '$((start))

while read TITLE
do
n=n+1
name[$((n))]="$TITLE"

echo fetching title "#$((n)) ${name[$((n))]}"

done < <(youtube-dl -s -e -i --playlist-start $((start)) ${1} | sed 's/[@\&\|\^\<\>\?\%\#\{\}\$\*\,\;\"\/]/-/g') #remove special characters
# get song's titles and put them into array name[]

n=0 #reset counter

youtube-dl -s -f bestaudio -g -i --playlist-start $((start)) ${1} | while read URLS #fetch url & read one by one
do
n=n+1
youtube-dl -f bestaudio -i --quiet "$URLS" --buffer-size 16k --http-chunk-size 10M -o - | ffmpeg -loglevel error -y -i - -codec copy -f ogg -ignore_unknown /data/data/com.termux/files/home/storage/exsd/"${name[$((n))]}".ogg && echo "====== #$((n)) ${name[$((n))]} was downloaded ======" || rm /data/data/com.termux/files/home/storage/exsd/"${name[$((n))]}".ogg

#fetch songs & change container to ogg from webm by ffmpeg without changing opus codec, removed it if fetching failed(ex. mp4a) or you will get an ogg file with empty content
done

exit 0



jocoliu wrote:
問題終於解決了, 用...(恕刪)


再次小更新, 5F 的程式在遇到音樂格式是以 m4a 方式儲存時會自動忽略下載並移除不正確的檔案, 更新版則在遇到 bestaudio 僅為 m4a 格式時, 則下載 m4a 格式, 不再經由 ffmpeg 進行 container 變更

#!/bin/bash

declare -i n

clear #clear screen

n=0 #set counter to 0
start=${2} #get the second parameter from user
test -z $start && start=1 # if the 2nd parameter is empty then set it to 1, you can ignore the 2nd parameter if you want start from 1

echo '============= start from '$((start))

while read TITLE
do
n=n+1
name[$((n))]="$TITLE"

echo fetching title "#$((n)) ${name[$((n))]}"

done < <(youtube-dl -s -e -i --playlist-start $((start)) ${1} | sed 's/[@\&\|\^\<\>\?\%\#\{\}\$\*\,\;\"\/]/-/g') #remove special characters
# get song's titles and put them into array name[]

n=0 #reset counter

youtube-dl -s -f bestaudio -g -i --playlist-start $((start)) ${1} | while read URLS #fetch url & read one by one
do
n=n+1
youtube-dl -f bestaudio -i --quiet "$URLS" --buffer-size 16k --http-chunk-size 10M -o - | ffmpeg -loglevel quiet -y -i - -codec copy -f ogg -ignore_unknown /data/data/com.termux/files/home/storage/exsd/"${name[$((n))]}".ogg && echo "====== #$((n)) ${name[$((n))]}.ogg was downloaded ======"
|| ( rm /data/data/com.termux/files/home/storage/exsd/"${name[$((n))]}".ogg; youtube-dl -f bestaudio -i --quiet "$URLS" --buffer-size 16k --http-chunk-size 10M -o /data/data/com.termux/files/home/storage/exsd/"${name[$((n))]}".m4a; echo "====== #$((n)) ${name[$((n))]}.m4a was downloaded ======" )

#fetch songs & change container to ogg from webm by ffmpeg without changing opus codec, remove the incorrect ogg file and redownload the song with m4a format if fetching failed(ex. if the bestaudio is m4a)

done

exit 0
jocoliu wrote:
再次小更新, 5F ...(恕刪)


再次更新, 前一版本總共會 query youtube 三次, 此次更新減少 query 次數, 加快下載速度
另外加上 time code 及 總共花費時間


#!/bin/bash

declare -i start

declare -i n

declare -i j

declare -i k

clear #clear screen

n=0 #set counter to 0
start=${2} #get the second parameter from user
[ $((start)) == 0 ] && start=1 # if the 2nd parameter is empty then set it to 1, you can ignore the 2nd parameter if you want start from 1

echo $(date) | awk '{printf $4}'
echo " ====== start from #$((start))"

now=$SECONDS

while read TITLE
do
n=n+1
name[$n]="$TITLE"
#get url, title and put them into array

[ $(($n*2/2)) == $(($(($n/2))*2)) ] && ( echo $(date) | awk '{printf $4}'; echo " fetching title #$((($n-2)/2+start)) ${name[$n]%-*} )" )


done < <(youtube-dl -s -f bestaudio --get-url --get-filename -i --playlist-start $((start)) ${1})

echo ======= start to download music files ======

for (( i=1; i<=n; i=i+1))
do

if [ $((i*2/2)) == $(($((i/2))*2)) ]; then
j=j+1
title[$j]="${name[i]}" #name[even] is title
t=${title[$j]}
ext="${t##*.}" #get file extension
t="${t%-*}" #get file name but remove id

if [ $ext == "webm" ]; then

youtube-dl -f bestaudio -i --quiet "${url[$k]}" --buffer-size 16k --http-chunk-size 10M -o - | ffmpeg -loglevel quiet -y -i - -codec copy -f ogg -ignore_unknown /data/data/com.termux/files/home/storage/exsd/"$t".ogg && ( echo $(date) | awk '{printf $4}'; echo " #$((k-1+start)) ===== $t.ogg downloaded" )
else
youtube-dl -f bestaudio -i --quiet "${url[$k]}" --buffer-size 16k --http-chunk-size 10M -o /data/data/com.termux/files/home/storage/exsd/"$t".m4a && ( echo $(date) | awk '{printf $4}'; echo " #$((k-1+start)) ===== $t.m4a downloaded" )
fi

else
k=k+1
url[$k]="${name[i]}" #name[odd] is url

fi

done

echo spent $(( SECONDS - now )) seconds

exit 0


jocoliu wrote:
再次更新, 前一版本...(恕刪)


假日再一發, 持續修改流程, 此版速度比上一版再加快約 25%




#!/bin/bash

declare -i start

declare -i n


clear #clear screen

n=0 #set counter to 0
start=${2} #get the second parameter from user
[ $((start)) == 0 ] && start=1 # if the 2nd parameter is empty then set it to 1, you can ignore the 2nd parameter if you want start from 1

echo $(date) | awk '{printf $4}'
echo " ====== download from #$((start)) "
echo -e downloading ......

now=$SECONDS

while read data
do
n=n+1
name[$n]="$data"
#get id, url, title and put them into array

if [ $((n%3)) == 1 ]; then
id=${name[$n]}
elif [ $((n%3)) == 2 ]; then
url=${name[$n]}
else
title=${name[$n]/-$id/} #get title but remove id
ext="${title##*.}" #get ext
title=${title/.$ext/} #remove ext from title

if [ $ext == "webm" ]; then
youtube-dl -f bestaudio -i --quiet $url --buffer-size 16k --http-chunk-size 10M -o - | ffmpeg -loglevel quiet -y -i - -codec copy -f ogg -ignore_unknown /data/data/com.termux/files/home/storage/exsd/"$title".ogg && ( echo $(date) | awk '{printf $4}'; echo " #$((n/3+start-1)) $title.ogg was downloaded" )
else
youtube-dl -f bestaudio -i --quiet $url --buffer-size 16k --http-chunk-size 10M -o /data/data/com.termux/files/home/storage/exsd/"$title".m4a && ( echo $(date) | awk '{printf $4}'; echo " #$((n/3-1+start)) $title.m4a was downloaded" )
fi

fi

done < <(youtube-dl -s -f bestaudio --get-id --get-url --get-filename -i --playlist-start $((start)) ${1})

echo spent $(( SECONDS - now )) seconds

exit 0

請問有人遇過這問題嗎??
下youtube-dl exe -F "影片網址"
照理說下這指令,可以看到這網址能夠下載那些Resolution的影片,但我卻出現以下error訊息,而且只有4K影片會這樣,1080p的就不會,求解


這幾天youtube變更網頁格式,第三方app都在修復問題當中。過幾天會比較穩定。
關閉廣告
文章分享
評分
評分
複製連結

今日熱門文章 網友點擊推薦!