Skip to content

Instantly share code, notes, and snippets.

@max1220
Created July 9, 2019 14:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save max1220/9db93e507a1c55c60a44458c1b8ca17d to your computer and use it in GitHub Desktop.
Save max1220/9db93e507a1c55c60a44458c1b8ca17d to your computer and use it in GitHub Desktop.
A Chrome(-ium) Bookmark downloader for youtube videos
return {
-- file extensions that are possibly a downloaded youtube video
file_extensions = {"mp3", "opus", "m4a", "ogg"},
-- directorys to scan for youtube id's
local_dirs = {
"/media/your/music/library/folder/that/contains/youtube_dl/downloaded/videos/"
},
-- where to store the downloaded files
download_dir = "/media/your/music/library/folder/some/download/folder/",
-- The filename of the bookmarks JSON file
bookmarks_file = "/home/max/.config/chromium/Default/Bookmarks",
-- list of folder names that are scanned for youtube id's(recursivly)
bookmarks_folders = {
"Music"
},
-- where to write the list of failed downloads
failed_file = "failed.txt",
-- enable better output using terminal escape sequences(disable if writing to log)
enable_term = true,
-- (internal) command used to find the files in a folder
_find_cmd = "find %s -type f",
-- (internal) command used to download a youtube URL to a path
_download_cmd = "cd %s; youtube-dl -x %s"
}
#!/usr/bin/env lua5.1
local config = require("config")
local json = require("cjson")
local function youtube_id_from_path(filepath)
for _, extension in ipairs(config.file_extensions) do
local lower = filepath:lower()
if lower:match("%-...........%." .. extension .. "$") then
local youtube_id = filepath:match("%-(...........)%." .. extension .. "$")
return youtube_id
end
end
end
local function find_local_files()
local match = {}
local miss = {}
for _, local_dir in ipairs(config.local_dirs) do
local proc = io.popen(config._find_cmd:format(local_dir))
for line in proc:lines() do
local youtube_id = youtube_id_from_path(line)
if youtube_id then
table.insert(match, {
filepath = line,
youtube_id = youtube_id
})
else
table.insert(miss, {
filepath = line,
})
end
end
proc:close()
end
return match, miss
end
local function get_bookmark_folder_recursive(folder, folder_name)
for _,child in ipairs(folder.children) do
if child.type == "folder" then
if child.name == folder_name then
return child
end
local ret = get_bookmark_folder_recursive(child, folder_name)
if ret then
return ret
end
end
end
end
local function get_bookmark_urls_recursive(folder, urls)
local urls = urls or {}
for _,child in ipairs(folder.children) do
if child.type == "url" then
table.insert(urls, child)
elseif child.type == "folder" then
get_bookmark_urls_recursive(child, urls)
end
end
end
local function youtube_id_from_url(url)
local args = url:match("%?(.+)$")
if args then
local youtube_id = args:match("&?v=(...........)")
return youtube_id
end
end
local function get_bookmarks_urls()
local json_f = assert(io.open(config.bookmarks_file, "rb"), "Can't open bookmarks file!")
local obj = json.decode(json_f:read("*a"))
json_f:close()
local _urls = {}
for _,folder_name in ipairs(config.bookmarks_folders) do
local folder = assert(get_bookmark_folder_recursive(obj.roots.bookmark_bar, folder_name), "Can't find folder in bookmarks!")
get_bookmark_urls_recursive(folder, _urls)
end
local urls = {}
for _,entry in ipairs(_urls) do
local youtube_id = youtube_id_from_url(entry.url)
if youtube_id then
table.insert(urls, {
youtube_id = youtube_id,
entry = entry
})
end
end
return urls
end
local function download_youtube_id(youtube_id)
local youtube_url = "https://youtube.com/watch?v=" .. youtube_id
local ret = os.execute(config._download_cmd:format(config.download_dir, youtube_url))
--local ret = os.execute(("echo %s; sleep 1; echo %s; sleep 1"):format(config.download_dir, youtube_url))
return ret == 0
end
local function download_youtube_ids(entries)
local len = #entries
local failed = {}
local j = 0
for i, entry in ipairs(entries) do
if config.enable_term then
print(("\027[2J\027[HDownloading %d/%d, ok: %d, failed: %d ... "):format(i, len, j, #failed))
else
print(("Downloading %d/%d, ok: %d, failed: %d ... "):format(i, len, j, #failed))
end
print()
if download_youtube_id(entry.youtube_id) then
j = j + 1
else
print("Download failed!")
table.insert(failed, entry)
end
end
return failed
end
print("Collecting bookmarks...")
local bookmarks_ids = get_bookmarks_urls()
print("Collecting files...")
local local_ids = find_local_files()
local missing_files = {}
local by_youtube_id = {}
for _,entry in ipairs(local_ids) do
by_youtube_id[entry.youtube_id] = entry
end
for _,entry in ipairs(bookmarks_ids) do
if not by_youtube_id[entry.youtube_id] then
table.insert(missing_files, entry)
end
end
print("total bookmarks:", #bookmarks_ids)
print("total files:", #local_ids)
print("total missing files:", #missing_files)
print("Starting download...")
local failed = download_youtube_ids(missing_files)
print("total failed:", #failed)
if config.failed_file then
local failed_f = assert(io.open(config.failed_file, "w"))
for i, fail in ipairs(failed) do
failed_f:write("https://youtube.com/watch?v=" .. fail.youtube_id)
end
end
print("Done!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment