Предлагается рассмотреть программу на python, которая позволит в рамках бесплатного тарифа «Яндекс.диска» нарезать и заливать видеофайлы, размеры которых превышают 1 Гб (требование тарифа), получать ссылки на них. Никакой магии — только api «yandex.диска» и немного python.
Предисловие. Небольшое
Погрузившись в познание нейросетей мимо меня как-то мимо прошла новость о том, что ресурс, на который была возложена миссия по спасению и переносу видеофайлов стал платным. Речь, конечно, о «Яндекс. диске».
Какое-то время с этим можно было мириться, так как для «старых» аккаунтов вроде как были небольшие послабления — еще можно было залить файл с превышением размера, только если он по габаритам помещался в оставшееся место на облаке. Но потом и эту «лавочку прикрыли» — только не более 1Гб.
В воздухе повисла пауза и необходимость как-то заливать файлы либо вообще забыть про ресурс.
Но, как говорится, «старый друг лучше новых двух», поэтому пришлось заглянуть в api «яндекс. диска», а, заодно, поискать быстрый splitter видео, чтобы экономить целых 199 р. в месяц на «старом друге».
К программе нарезки видео
Идея программы проста: дробим видео по 1Гб, заливаем на «яндекс.диск» (далее по тексту — «Я»), делаем ссылки на части видео публичными (доступ по ссылке), получаем ссылки на видео в txt.
В поисках проекта по корректному и быстрому разрезанию видео нагитхабилось несколько репозиториев. Но не все они оказались рабочими, а из рабочих — быстрыми и результативными.
В итоге, самым результативной оказалась программа (назовем ее split.py), использующая ffmpeg:
import subprocess,os
class Split:
def __init__(self,maxSize,path):
'''Size of the chunks in MB and path to save the pieces'''
self.maxSize = int((maxSize-10) * 1024)
self.path = path
def splitVid(self,file):
'''File to split'''
video_size = os.path.getsize(file)
video_size = video_size / 1024
video_duration = subprocess.run(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", file], stdout=subprocess.PIPE).stdout.decode('utf-8')
pieces_duration = round(float(video_duration) / (int(video_size / self.maxSize) + 1))
subprocess.run(["ffmpeg", "-i", file, "-c", "copy", "-f", "segment", "-segment_time", str(pieces_duration), "-loglevel", "quiet", "-reset_timestamps", "1", f"{self.path}part%02d_vid.mp4"])
Вызывать ее будем из другого скрипта (example.py):
from split import Split
splitter = Split(1000,"./")
splitter.splitVid("video.mkv")
где 1000 — это размер в Мб, который превышать нельзя, а video.mkv — видео для нарезки.
Вроде бы все в части нарезки видео. Но, оказалось, с Я не все так просто.
Общение с api «яндекс.диска»
Для того, чтобы можно было пользоваться ресурсом, необходим токен, благо он бесплатный.
Получить его просто, пройдя ряд шагов, не вызывающих сложностей по созданию приложения на «яндексе». На получении токена останавливаться не будем, статей на эту тему достаточно.
Перейдем непосредственно к программе.
В основу положена библиотека из репозитория — yadisk.
Устанавливается вспомогательный модуль как обычно —
pip install yadisk
Есть также ее async сестра — yadisk-async.
С ее использованием при общении с Я тоже не все безоблачно, но об этом позднее.
Однако в пределах одного взятого файла (видеофрагмента большого размера) async проигрывает по скорости загрузки, проверено экспериментально.
В репо yadisk есть простые команды для общения с Я по api.
Мы перейдем сразу к рекурсивной загрузке всех файлов из папки на нашем локальном диске.
import posixpath
import os
import yadisk
#windows version
from time import sleep
y = yadisk.YaDisk(token="токен")
# Создаёт новую папку "/test-dir"
print(y.mkdir("/test-dir"))
to_dir = "/test-dir"
from_dir = "to_upload"
#проверка свободного места
#print(y.get_disk_info().FIELDS)
fields=y.get_disk_info().FIELDS
free_space=(fields['total_space']-fields['used_space'])/1048576
trash=fields['trash_size']/1048576
print(f"свободное место на yandex disk: {round(free_space,2)} Мб", )
print(f"корзина занимает: {round(trash,2)} Мб", )
def recursive_upload(y, from_dir, to_dir):
for root, dirs, files in os.walk(from_dir):
p = root.split(from_dir)[1].strip(os.path.sep)
dir_path = posixpath.join(to_dir, p)
#print(dir_path)
#try:
#y.mkdir(dir_path)
#except yadisk.exceptions.PathExistsError as e:
#print(e)
#pass
for file in files:
file_path = posixpath.join(dir_path, file)
#print(file_path)
#p_sys = p.replace("/", os.path.sep)
in_path = os.path.join(from_dir, file)
print(in_path)
try:
y.upload(in_path, file_path)
except yadisk.exceptions.PathExistsError as e:
print(e)
#pass
recursive_upload(y, from_dir, to_dir)
print("залито")
#print(list(y.listdir("/test-dir")))
for f in list(y.listdir("/test-dir")):
f.publish() # make file public
with open ("links.txt", "a") as l:
for f in list(y.listdir("/test-dir")):
print(f.FIELDS["public_url"]) #public url
l.write(f.FIELDS["public_url"]+'\n')
print('готово')
Программа для os windows. Разберем код.
Из локальной папки to_upload заливаются в папку test-dir на Я все ранее нарезанные видеофрагменты.
Также предварительно выводится свободное место на yandex disk и размер заполненной корзины, чтобы самому оценить вероятность успеха, а не дожидаться ошибки.
Далее, с помощью
f.publish()
файлы становится доступными по ссылке, получив которую их можно передать любому для скачивания.
Далее, все публичные ссылки на файлы собираются в локальный файл links.txt.
Таким же способом можно заливать не только файты в mp4, но и любые другие.
И тут пришло время поговорить о нюансах.
Нюансы api «яндекс.диска»
Как выяснилось, заливать файлы со стандартными расширениями файлов, если эти файлы значительных размеров — mp4, db и т.д. — дело не благодарное. По непонятным причинам они либо не заливаются вовсе либо процесс загрузки достаточно долгий.
На этот факт также указывается в документации.
Выход из сложившейся ситуации простой — заливать файлы, удалив у них расширения. То есть вместо «video.mp4», закачивать «video».
Поэтому, в программу рекурсивной загрузки файлов добавим одну строку, которая будет удалять расширение файла перед загрузкой:
os.rename(f'{from_dir}/{file}',f'{from_dir}/{file.split(".")[0]}')
Блок кода примет вид:
for file in files:
os.rename(f'{from_dir}/{file}',f'{from_dir}/{file.split(".")[0]}')
file_path = posixpath.join(dir_path, file)
Ремарка по async версию программы
Переименование файлов перед загрузкой на ресурс Я не спасает программу по async загрузке от вылета. Это происходит только при заливе крупных файлов и, вероятно, из-за timeout — issue.
Тем не менее, версию программы с async можно заставить работать, увеличив timeout.
Например, рабочий код (протестирован на видео фрагментах) может выглядеть вот так:
async def main():
print("token valid: ",await y.check_token())
print("заливаю...")
async with aiofiles.open("ghosted_1", "rb") as f:
await y.upload(f, "/test-dir/ghosted_1",timeout=100)
print("залил")
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
Послесловие
Оглядываясь назад, можно задать вопрос — стоят ли 199 р. таких трудов? Все равно ведь залитые видеофрагменты дробятся по 1Гб и при просмотре с ресурса Я будут немного вызывать неудобства при просмотре. Однако, этот вопрос — на обсуждение. Здесь лишь приведен вариант решения проблемы, с которой столкнулся автор.
Комментарии (3)
click0
18.09.2023 09:20+3ffmpeg
для разбиения корректно большего видео-контейнера иrclone
чтоб залить эти файлы в любой облако :)
zartarn
18.09.2023 09:20Как я помню, WebDavMailRuCloud (работает и с яндексом и с мейлру) умеет на лету делить при загрузке, и объединять при скачке, и все это по webdav
noRoman
Еще вариант в terminal: