P.s. если с линуксом "на вы", то лучше найти какой-нибудь другой способ
- Если внутри freyr происходит ошибка (однажды выбило переполнение стека, в другой раз оно просто зависло) - работа до конца не завершится даже у уже загруженных файлов, а точнее не будут записаны метаданные
- Оно может не скачать трек по причине 403 или отсутствия источников, и в статистике не выведет, какие конкретно треки это были
- Голым беш скриптом автоматизация невозможна из-за того, что freyr пишет напрямую в tty 😕
- Линукс (wsl сойдёт, но не тестировался)
- Докер
Докер нужен для того, чтобы перенаправить вывод с freyr в переменную bash (других способов в голову не пришло)
Создаём Dockerfile
:
FROM freyrcli/freyrjs
# Не знаю нужно ли, но мне лень тестировать без рута
USER root
ENTRYPOINT ["sh"]
(стоит уточнить спустя несколько месяцев, что можно использоовать параметр --entrypoint, но мне лень переписывать и тестировать)
В той же папке делаем docker build . -t herobrine1st/freyr
Делаем docker run --rm -ti herobrine1st/freyr freyr test
. Если он говорит, что не нашёл сервис - всё хорошо, интернет есть. Если интернета нет (он так и напишет), то команда sysctl -w net.ipv4.ip_forward=1
c последующим рестартом докера мне всегда помогали. Если не поможет - гугл
Данный этап может выполняться любыми способами. В данном случае мы получаем треки с плейлистов аккаунта.
Идём в репозиторий с утилитой, сохраняем свои треки в текстовый формат.
Затем делаем grep track "ваш файл" | awk '{print $NF}' | sed 's/\r//' | sort | uniq > tracks.txt
В файле tracks.txt
должно появиться много строк вида spotify:track:5nkzM2Pcoml4C8in9ozjwA
#!/bin/bash
path="/home/herobrine1st/Music" # указать путь
container_id=$(docker container run -d -v $path:/data herobrine1st/freyr -c "while true; do sleep 1; done")
echo "Running in $container_id"
if [[ -f failed.txt ]]; then
cp failed.txt "failed-$(date -Iseconds).txt"
fi
if [[ ! -f tracks.txt ]]; then
mv failed.txt tracks.txt
fi # else resume
for line in $(<tracks.txt); do
out=$(docker exec -t $container_id freyr $line)
if [[ "${out,,}" =~ failed|error ]]; then
echo $line failed
echo $line >> failed.txt
else
echo $line success
fi
done
mv tracks.txt "tracks-$(date -Iseconds).txt"
docker container stop $container_id
docker container rm $container_id
Копируем в download.sh, указываем путь, куда произойдёт скачивание.
Выполнить download.sh (bash download.sh
). Скрипт будет вызывать freyr, чтобы вызвать музыку, а если не сможет - сохранит в файл failed.txt, который использует на следующем запуске.
Треки, которые не удалось скачать, могут скачаться через впн или через некоторое время (нужно вызвать скрипт ещё раз). Если треки не скачиваются - значит freyr не может найти их на ютубе.
Чтобы убедиться в том, что треки не могут скачаться по причине отсутствия источника, можно вызвать cat failed.txt | xargs freyr
при установленном в PATH freyr.
Это происходит из-за того, что freyr не учитывает номер диска. Можно починить вручную, скрипт для поиска альбомов с такими проблемами:
for album in */*; do
if [[ -f $album ]]; then
continue
fi
track_numbers=()
for file in "$album"/*.m4a; do
file=$(basename "$file")
track_numbers+=(${file:0:2})
done
seen=()
for i in "${track_numbers[@]}"; do
i=$((10#$i))
if [ -z "${seen[i]}" ]; then
seen[i]=1
else
echo $album ${track_numbers[*]}
break
fi
done
done
Затем переименовать файлы и поправить номер диска и номер трека с помощью atomicparsley (если ваш софт использует метаданные)
Ахтунг: здесь надо быть программистом, мне лень делать полную инструкцию
Пропущенные треки окажутся в файле failed.txt. Делаем запросы в апи спотифая, чтобы получить объекты треков, склеиваем все ответы в один json-файл (он будет похож на {"tracks": [{...}, {...}, ...]}
) с названием failed.json
и выполняем скрипт
import json
tracks = []
with open("failed.json") as f:
tracks = json.load(f)["tracks"]
for track in tracks:
album = track["album"]
album_name = album["name"]
album_art = sorted(
album["images"],
key=lambda x: x["width"],
reverse=True)[0]["url"]
tracks_in_album = album["total_tracks"]
track_number = track["track_number"]
disc_number = track["disc_number"]
track_name = track["name"]
explicit = track["explicit"]
artists = [artist["name"] for artist in track["artists"]]
link = track["external_urls"]["spotify"]
tracks.append({
"album": {
"name": album_name,
"cover": album_art,
"total_tracks": tracks_in_album
},
"name": track_name,
"track_number": track_number,
"disc_number": disc_number,
"artists": artists,
"link": link
})
print("Writing..")
with open("tracks.json", "w") as f:
json.dump(tracks, f)
Затем копируем tracks.json
в какую-нибудь пустую папку или делаем бекап уже скачанной музыки (на всякий пожарный; второе предпочтительнее), копируем add_metadata.sh
из этого гиста в ту же папку. Не забудьте сделать chmod +x на файле.
Затем смотрим в файл tracks.json (лучше всего браузером, там видно индексы объектов), ищем на ютубе трек (можно в любом месте, лишь бы yt-dlp умел оттуда скачивать файлы), получаем ссылку и запускаем с аргументами: индекс из массива в tracks.json и ссылка, которую вы получили в кавычках или без символа & и всего, что после него. Пример: ./add_metadata.sh индекс "ссылка"
.
Если и этот скрипт не сможет скачать, то он положит индекс и ссылку в yt-dlp-failed.txt