Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://angg.twu.net/youtube-db/toolbox.lua -- http://angg.twu.net/youtube-db/toolbox.lua.html -- (find-angg "youtube-db/toolbox.lua") -- -- Note that it uses functions from here: -- http://angg.twu.net/youtube-db/edrxlib.lua -- http://angg.twu.net/youtube-db/edrxlib.lua.html -- (find-angg "youtube-db/edrxlib.lua") -- (find-angg "LUA/lua50init.lua") -- and I use eepitch to use this script interactively, as a toolbox... -- (find-eepitch-intro) -- I use this to keep a big archive of videos: -- http://angg.twu.net/linkdasruas.html -- http://angg.twu.net/linkdasruas2.html -- The docs are here, but they are in Portuguese: -- http://angg.twu.net/ferramentas-para-ativistas.html -- If this looks interesting to you, PLEASE GET IN TOUCH!!!! -- eduardoochs@gmail.com -- «.basic-tools» (to "basic-tools") -- «.bigstrs» (to "bigstrs") -- «.video-fnames» (to "video-fnames") -- «.dates» (to "dates") -- «.shell-functions» (to "shell-functions") -- «.YScripts» (to "YScripts") -- «.YoutubeDB» (to "YoutubeDB") -- «.YoutubeDB-add» (to "YoutubeDB-add") -- «.YoutubeDB-register» (to "YoutubeDB-register") -- «.YoutubeDB-register-fnames» (to "YoutubeDB-register-fnames") -- «.YoutubeDB-read-run-ls» (to "YoutubeDB-read-run-ls") -- «.YoutubeDB-traverse» (to "YoutubeDB-traverse") -- «.YoutubeDB-gets» (to "YoutubeDB-gets") -- «.YoutubeDB-dump» (to "YoutubeDB-dump") -- «.YoutubeDB-missing-titles» (to "YoutubeDB-missing-titles") -- «.YoutubeDB-missing-dates» (to "YoutubeDB-missing-dates") -- «.YoutubeDB-dates_cat» (to "YoutubeDB-dates_cat") -- «.YoutubeDB-titles_cat» (to "YoutubeDB-titles_cat") -- «.YoutubeDB-script_cp_angg» (to "YoutubeDB-script_cp_angg") -- «.YoutubeDB-script_dl_angg» (to "YoutubeDB-script_dl_angg") -- «.YoutubeDB-dates-titles-cat» (to "YoutubeDB-dates-titles-cat") -- «.YoutubeDB-filter» (to "YoutubeDB-filter") -- «.YoutubeDB-copy-dates-into» (to "YoutubeDB-copy-dates-into") -- «.YoutubeDB-txt2_line» (to "YoutubeDB-txt2_line") -- «.YoutubeDB-line-to-fname» (to "YoutubeDB-line-to-fname") -- «.template-dynamic.html» (to "template-dynamic.html") -- «.write_html_dynamic» (to "write_html_dynamic") -- «.simple_toplevel» (to "simple_toplevel") -- «.simple_toplevel2» (to "simple_toplevel2") -- «basic-tools» (to ".basic-tools") eval = function (str) return assert(loadstring(str))() end -- if arg and arg[1] then -- local cmd = arg[1] -- if cmd == "-sort_td" then sort_by_tag_and_date(arg[2]) -- else eval(arg[1]) -- end -- end identity = function (...) return ... end -- (find-es "lua5" "cow-and-coy") coy = coroutine.yield cow = coroutine.wrap -- (find-blogme3file "youtube.lua" "meta_first_time") meta_first_time = function () local T = {} return function (hash) if not T[hash] -- if hash is not yet in T then T[hash] = "present"; return true else return false end end end -- like "cat L | sort | uniq" sort_uniq = function (L) return Set.from(L):ks() end ppfo = function () local L = {} local print = function (str) table.insert(L, str.."\n") end local printf = function (fmt, ...) table.insert(L, format(fmt, ...)) end local output = function () return table.concat(L) end return print, printf, output end no_coding = function (str) if str:match "^[^\n]*coding:[^\n]*\n" then return (str:match "^[^\n]*\n(.*)") else return str end end -- ____ _ _ -- | __ )(_) __ _ ___| |_ _ __ ___ -- | _ \| |/ _` / __| __| '__/ __| -- | |_) | | (_| \__ \ |_| | \__ \ -- |____/|_|\__, |___/\__|_| |___/ -- |___/ -- -- String traversers, usually for the contents of files. -- gen_nonempty_lines: for each non-empty line in bigstr, yield it. -- gen_tag_hash_title: for each youtube-ish line in bigstr, yield -- "tag,hash,title". See: -- (find-blogme3file "youtube.lua" "gen_video_files") -- gen_hash_fname_dir_stem: for each line like -- "dir/title-youtubeid.mp4" in bigstr, yield "hash,fname,dir,stem". -- -- The code calls this: (find-angg "LUA/lua50init.lua" "youtube_split") -- -- «bigstrs» (to ".bigstrs") -- gen_nonempty_lines = function (bigstr) return bigstr:gmatch("[^\n]+") end -- gen_tag_hash_title = function (bigstr) -- bigstr has video urls with titles -- return cow(function () -- for line in gen_nonempty_lines(bigstr) do -- local a, hash, b, title, c = youtube_split_url0(line) -- -- For each youtube-ish line yield tag,hash,title -- if a then -- local tag = a:match("%[(.*)%]") -- coy(tag or "", hash, title) -- end -- end -- end) -- end gen_tag_hash_title = function (bigstr) -- bigstr has video urls with titles return cow(function () for line in gen_nonempty_lines(bigstr) do local pre, hash, b, title, c = youtube_split_url0(line) -- For each youtube-ish line yield tag,hash,title,dates if pre then local tag, dates = pre_to_tag_and_dates(pre) coy(tag or "", hash, title, dates) end end end) end gen_hash_fname_dir_stem = function (bigstr) -- bigstr is a list of .mp4s return cow(function () for fname in gen_nonempty_lines(bigstr) do -- local a, hash = fname:match "^(.-)-(...........)%.mp4$" local a, hash, ext = fname:match "^(.-)-(...........)%.([mf][pl][4v])$" if a then local dir, stem = a:match "^(.*/)([^/]*)$" coy(hash, fname, dir or "", stem or a) end end end) end -- _ -- | |___ -- | / __| -- | \__ \ -- |_|___/ -- -- «video-fnames» (to ".video-fnames") -- video_extensions = {mp4=true} -- video_extension = function (ext, exts) end -- video_fname_split = function (fname) end -- gen_video_fnames = function (bigstr, exts) end -- gen_video_fnames_ls = function (dir, exts) end -- register_video_fnames = function (bigstr, exts) end -- register_video_fnames_ls = function (dir, exts) end video_extensions = {mp4=true} video_extension = function (ext, exts) return ext and (exts or video_extensions)[ext] end video_fname_split = function (fname) if not fname then return end local pat = "^(.-)-(...........)%.([a-z0-9]+)$" local a, hash, ext = fname:match(pat) if not a then return end local dir, stem = a:match "^(.*/)([^/]*)$" if not dir then dir,stem = "",a end return dir, stem, hash, ext end gen_video_fnames = function (bigstr, exts) return cow(function () for fname in gen_nonempty_lines(bigstr) do local dir, stem, hash, ext = video_fname_split(fname) if video_extension(ext, exts) then coy(fname, dir, stem, hash, ext) end end end) end gen_video_fnames_ls = function (dir, exts) return cow(function () for i,fname0 in ipairs(sorted(ee_ls(dir) or {})) do -- print(i, fname0) local fname = dir..fname0 local dir, stem, hash, ext = video_fname_split(fname) if video_extension(ext, exts) then coy(fname, dir, stem, hash, ext) end end end) end -- ____ _ -- | _ \ __ _| |_ ___ ___ -- | | | |/ _` | __/ _ \/ __| -- | |_| | (_| | || __/\__ \ -- |____/ \__,_|\__\___||___/ -- -- Some support for dates. The format is usually like this: -- (find-anggfile "linkdasruas.txt" "26/out/2013") -- Something like "26/out/2013" is a "bdate" (a Brazilian date). -- Something like "20131016" is an "ndate" (a numeric date). -- «dates» (to ".dates") month_numbers = { jan=1, feb=2, fev=2, mar=3, abr=4, apr=4, mai=5, may=5, jun=6, jul=7, ago=8, aug=8, set=9, sep=9, out=10, oct=10, nov=11, dez=12 } -- bdate_to_ndate = function (bdate) local dd,mmm,yyyy = bdate:match "^([0-9]+)/([A-Za-z]+)/([0-9]+)$" if not dd then return end local mm = month_numbers[mmm] -- PP(dd, mmm, mm) if mm then return format("%04d%02d%02d", yyyy, mm, dd) end end date_to_ndate = function (date) if date:match "^[0-9]+$" and #date==8 then return date end return bdate_to_ndate(date) end pre_to_tag_and_dates = function (pre) local tag = "" local dates = {} for _,word in ipairs(split(pre)) do local t = word:match "^%[(.-)%]$" if t then tag = t else local d = date_to_ndate(word) if d then table.insert(dates, d) end end end return tag, dates end -- line_to_tag_and_dates = function (line) -- local a,u = line:match "^(.-)(http.*)$" -- if not a then return end -- end -- line_to_tag_and_date = function (line) local a,u = line:match "^(.-)(http.*)$" if not a then return end local t,_,r = a:match "^(%[(.-)%]) *(.*)$" -- PP(t,_,s,r) local tag = t or "" local date = (r and bdate_to_ndate(r)) or "" return tag, date end -- -- See: (find-angg ".emacs" "youtube-db.lua") -- This is for editing (find-angg "linkdasruas.txt") sort_by_tag_and_date0 = function (bigstr) local L = {} for i,line in ipairs(splitlines(bigstr)) do local tag, date = line_to_tag_and_date(line) local key = format("%s %s %05d", (tag or ""), (date or ""), i) -- PP(key) table.insert(L, {key, line}) -- PP(L[#L]) end local f = function (a, b) return a[1] < b[1] end table.sort(L, f) local g = function (lk) return lk[2].."\n" end return mapconcat(g, L) end sort_by_tag_and_date = function (fname) local bigstr = fname and ee_readfile(fname) or io.read "*a" io.write(sort_by_tag_and_date0(bigstr)) end -- _ _ _ -- ___| |__ ___| | | -- / __| '_ \ / _ \ | | -- \__ \ | | | __/ | | -- |___/_| |_|\___|_|_| -- -- «shell-functions» (to ".shell-functions") --[==[ download_script0 = function (options) return (([[ ydl () {( cd $mp4dir/ && youtube-dl -t -f 18 --restrict-filenames "http://www.youtube.com/watch?v=$1" )} ydl () { echo -n > "$mp4dir/Fake_video-$1.mp4" } ]]):gsub("$(%w+)", options)) end title_script0 = function (options) return (([[ mkdir $tmpdir/ ytitle () { youtube-dl -e "http://www.youtube.com/watch?v=$2" > $tmpdir/$2.title0 recode h..l1 < $tmpdir/$2.title0 > $tmpdir/$2.title echo $1 $2 $(cat $tmpdir/$2.title) } ytitle () { echo "Fake title" > $tmpdir/$2.title echo $1 $2 $(cat $tmpdir/$2.title) } ]]):gsub("$(%w+)", options)) end date_script0 = function (options) return (([[ mkdir $tmpdir/ ydate () { youtube-dl --get-filename -o "%(upload_date)s" \ "http://www.youtube.com/watch?v=$2" > $tmpdir/$2.date } ydate () { echo 20001234 > $tmpdir/$2.date echo $1 $2 $(cat $tmpdir/$2.date) } ]]):gsub("$(%w+)", options)) end title_cat = function (hash, options) local dir = (options and options.tmpdir) or "/tmp/ydbtmp" local fname = dir.."/"..hash..".title" local ok,contents = pcall(function () return readfile(fname) end) if ok then return (contents:gsub("\n$", "")) end end date_cat = function (hash, options) local dir = (options and options.tmpdir) or "/tmp/ydbtmp" local fname = dir.."/"..hash..".date" local ok,contents = pcall(function () return readfile(fname) end) if ok then return (contents:gsub("\n$", "")) end end --]==] filecontents = function (fname) local ok,contents = pcall(function () return readfile(fname) end) if ok then contents = contents:gsub("\n$", "") if contents ~= "" then return contents end end end -- __ ______ _ _ -- \ \ / / ___| ___ _ __(_)_ __ | |_ ___ -- \ V /\___ \ / __| '__| | '_ \| __/ __| -- | | ___) | (__| | | | |_) | |_\__ \ -- |_| |____/ \___|_| |_| .__/ \__|___/ -- |_| -- -- «YScripts» (to ".YScripts") YScripts = Class { type = "YScripts", new = function (A) return YScripts(A or {}) end, fake = function (A) A = A or {}; A.fake = 1; return YScripts(A) end, real = function (A) A = A or {}; return YScripts(A) end, __index = { expand = function (ys, fmt) return (fmt:gsub("$(%w+)", ys)) end, -- ywatch = "http://www.youtube.com/watch", mp4dir = "videos", -- without trailing "/" tmpdir = "/tmp/ydbtmp", -- without trailing "/" sleep_real = "sleep 150", -- sleep_real = "wait", sleep_fake = "# sleep 150", nprocesses = 50, cpdir = nil, -- write_script = function (ys, fname, bigstr) ee_writefile(fname, bigstr) end, line = function (ys, fmt) return ys:expand(fmt.."\n") end, lines = function (ys, fmts) return mapconcat(function (fmt) return ys:line(fmt) end, fmts) end, hash_line = function (ys, fmt, h) ys.h = h; return ys:expand(fmt.."\n") end, hash_lines0 = function (ys, fmt, hashes) return map(function (h) return ys:hash_line(fmt, h) end, hashes) end, fname_lines = function (ys, fmt, fnames) local f = function (fname) ys.fname = fname return ys:expand(fmt.."\n") end return mapconcat(f, fnames) end, with_sleep = function (ys, lines, sleep) if sleep then local i = ys.nprocesses + 1 while i <= #lines do table.insert(lines, i, sleep.."\n") i = i + ys.nprocesses + 1 end end return table.concat(lines) end, script = function (ys, defs, fmt, sleep, hashes) local s0 = ys:lines(defs) local hl = ys:hash_lines0(fmt, hashes) local s1 = ys:with_sleep(hl, sleep) return s0..s1 end, cp_mp4 = function (ys, fnames) return ys:fname_lines("cp -sv $fname $cpdir", fnames) end, dl_mp4 = function (ys, hashes) return ys:script(ys.fake and ys.dl_mp4_fake or ys.dl_mp4_real, "ydl_mp4 $h", nil, hashes) end, dl_date = function (ys, hashes) return ys:script(ys.fake and ys.dl_date_fake or ys.dl_date_real, ys.fake and "ydl_date $h" or "ydl_date $h &", ys.fake and ys.sleep_fake or ys.sleep_real, hashes) end, dl_title = function (ys, hashes) return ys:script(ys.fake and ys.dl_title_fake or ys.dl_title_real, ys.fake and "ydl_title $h" or "ydl_title $h &", ys.fake and ys.sleep_fake or ys.sleep_real, hashes) end, dl_mp4_fake = { 'mkdir $mp4dir/', 'ydl_mp4 () {', ' echo -n > "$mp4dir/Fake_video-$1.mp4"', '}', }, dl_mp4_real = { 'mkdir $mp4dir/', 'ydl_mp4 () {(', ' cd $mp4dir/ &&', ' youtube-dl -t -f 18 --restrict-filenames "$ywatch?v=$1"', ')}', }, dl_date_fake = { 'mkdir $tmpdir/', 'ydl_date () {', ' echo 20001234 > $tmpdir/$1.date', ' echo $1 $(cat $tmpdir/$1.date)', '}', }, dl_date_real = { 'mkdir $tmpdir/', 'ydl_date () {', ' youtube-dl --get-filename -o "%(upload_date)s" \\', ' "$ywatch?v=$1" > $tmpdir/$1.date', '}', }, dl_title_fake = { 'mkdir $tmpdir/', 'ydl_title () {', ' echo "Fake title" > $tmpdir/$1.title', ' echo $1 $(cat $tmpdir/$1.title)', '}', }, dl_title_real = { 'mkdir $tmpdir/', 'ydl_title () {', ' youtube-dl -e "$ywatch?v=$1" > $tmpdir/$1.title', ' echo $1 $(cat $tmpdir/$1.title)', '}', }, date_cat = function (ys, hash) return hash and filecontents(ys.tmpdir.."/"..hash..".date") end, title_cat = function (ys, hash) return hash and filecontents(ys.tmpdir.."/"..hash..".title") end, }, } -- __ __ _ _ ____ ____ -- \ \ / /__ _ _| |_ _ _| |__ ___| _ \| __ ) -- \ V / _ \| | | | __| | | | '_ \ / _ \ | | | _ \ -- | | (_) | |_| | |_| |_| | |_) | __/ |_| | |_) | -- |_|\___/ \__,_|\__|\__,_|_.__/ \___|____/|____/ -- -- «YoutubeDB» (to ".YoutubeDB") YoutubeDB = Class { type = "YoutubeDB", new = function () return YoutubeDB {hashes = {}, hashes_list = {}} end, __tostring = function (ydb) return format("(YoutubeDB with %d hashes in hashes_list)", #ydb.hashes_list) end, __mul = function (ydb, ydb2) return ydb:filter(function (hash,e) return ydb2:has(hash) end) end, __sub = function (ydb, ydb2) return ydb:filter(function (hash,e) return not ydb2:has(hash) end) end, __index = { has = function (ydb, hash) return ydb.hashes[hash] end, has_hash = function (ydb, hash) return ydb.hashes[hash] end, first_date = function (ydb, hash) return ydb.hashes[hash].dates[1] end, first_title = function (ydb, hash) return ydb.hashes[hash].titles[1] end, first_fname = function (ydb, hash) return ydb.hashes[hash].fnames[1] end, first_hash = function (ydb) return ydb.hashes_list[1] end, n = function (ydb) return #(keys(ydb.hashes)) end, n0 = function (ydb) return #(ydb.hashes_list) end, nrepetitions = function (ydb) return ydb:n0() - ydb:n() end, hsorted = function (ydb) return sorted(keys(ydb.hashes)) end, -- -- very-low-level stuff, experimental -- «YoutubeDB-add» (to ".YoutubeDB-add") add_hash = function (ydb, hash) if not ydb.hashes[hash] then ydb.hashes[hash] = {tags={}, titles={}, fnames={}, dates={}} table.insert(ydb.hashes_list, hash) end end, add_tag = function (ydb, hash, tag) if tag then ydb.hashes[hash].tags[tag] = tag end end, add_tags = function (ydb, hash, tags) for tag in (tags or ""):gmatch("[^,]+") do ydb:add_tag(hash, tag) end end, add_title = function (ydb, hash, title) if title and title ~= "" and title ~= ydb.hashes[hash].titles[1] then table.insert(ydb.hashes[hash].titles, title) end end, add_date = function (ydb, hash, date) if date then table.insert(ydb.hashes[hash].dates, date) end end, add_dates = function (ydb, hash, dates) for i,date in ipairs(dates or {}) do ydb:add_date(hash, date) end end, -- -- Functions to store more entries into the database -- «YoutubeDB-register» (to ".YoutubeDB-register") register_video_hash = function (ydb, hash) -- became "add_hash" if not ydb.hashes[hash] then ydb.hashes[hash] = {tags={}, titles={}, fnames={}, dates={}} table.insert(ydb.hashes_list, hash) end end, register_video = function (ydb, tags, hash, title, dates) ydb:register_video_hash(hash) ydb:add_tags (hash, tags) ydb:add_title(hash, title) ydb:add_dates(hash, dates) end, register_videos = function (ydb, bigstr, forced_tag) bigstr = bigstr:gsub("/youtu%.be/", "/www.youtube.com/watch?v=") -- *** bigstr = bigstr:gsub("/shorts/", "watch?v=") -- *** for tag,hash,title,dates in gen_tag_hash_title(bigstr) do ydb:register_video(forced_tag or tag, hash, title, dates) end return ydb end, register_videos_read = function (ydb, fname, forced_tag) return ydb:register_videos(readfile(ee_expand(fname)), forced_tag) end, register_videos_run = function (ydb, script, forced_tag) return ydb:register_videos(getoutput(script), forced_tag) end, -- -- «YoutubeDB-register-fnames» (to ".YoutubeDB-register-fnames") register_video_fname = function (ydb, hash, fname) ydb:register_video_hash(hash) local fnames = ydb.hashes[hash].fnames table.insert(fnames, fname) end, register_video_fnames = function (ydb, bigstr, exts) for fname,dir,stem,hash,ext in gen_video_fnames(bigstr, exts) do ydb:register_video_fname(hash, fname) end return ydb end, register_video_fnames_ls = function (ydb, dir, exts) for fname,dir,stem,hash,ext in gen_video_fnames_ls(dir, exts) do ydb:register_video_fname(hash, fname) end return ydb end, -- -- Shorthands for register_xxx methods: -- «YoutubeDB-read-run-ls» (to ".YoutubeDB-read-run-ls") read0 = function (ydb, bigstr, forced_tag) return ydb:register_videos(bigstr, forced_tag) end, read = function (ydb, fname, forced_tag) return ydb:register_videos_read(fname, forced_tag) end, run = function (ydb, script, forced_tag) return ydb:register_videos_run(script, forced_tag) end, ls0 = function (ydb, bigstr, exts) return ydb:register_video_fnames(bigstr, exts) end, ls = function (ydb, dir, exts) return ydb:register_video_fnames_ls(dir, exts) end, -- -- Low-level functions to traverse the database -- «YoutubeDB-traverse» (to ".YoutubeDB-traverse") gen_hashes = function (ydb) return cow(function () local first_time = meta_first_time() for _,hash in ipairs(ydb.hashes_list) do if first_time(hash) then coy(hash, ydb.hashes[hash]) end end end) end, gen_hash_tags_titles = function (ydb) return cow(function () for hash,entry in ydb:gen_hashes() do coy(hash, entry.tags, entry.titles) end end) end, -- gen_hashes_without_titles = function (ydb) -- return cow(function () -- for hash,tags,titles in ydb:gen_hash_tags_titles() do -- if #titles == 0 then coy(hash) end -- end -- end) -- end, -- hashes_without_titles = function (ydb) -- local Hs = {} -- for hash,tags,titles in ydb:gen_hash_tags_titles() do -- if #titles == 0 and #tags == 0 then table.insert(Hs, hash) end -- end -- return Hs -- end, hashes_without_titles = function (ydb) return ydb:title_no().hashes_list end, -- -- Functions that return things from the database (as strings) -- «YoutubeDB-gets» (to ".YoutubeDB-gets") atags = function (ydb, hash) return table.concat(sorted(keys(ydb.hashes[hash].tags)), ",") end, adates = function (ydb, hash) local ds = Set.from(ydb.hashes[hash].dates):ks() return mapconcat(function (d) return d.." " end, ds) end, hash_to_line = function (ydb, hash, nl) local ak = ydb:atags(hash) local ak2 = (ak == "") and "" or "["..ak.."]" return ak2.." "..ydb:adates(hash).. youtube_make_url(hash) .." ".. (ydb.hashes[hash].titles[1] or "").. (nl or "") end, hash_to_line_nl = function (ydb, hash) return ydb:hash_to_line(hash, "\n") end, -- all_hashes = function (f) local L = {} f = f or identity for hash in ydb:gen_hash_tags_titles() do table.insert(L, f(hash)) end return L end, all_by_tags = function (ydb) local A = {} for hash,tags,titles in ydb:gen_hash_tags_titles() do local key = ydb:atags(hash) -- like "", "a", "p,i", etc if not A[key] then A[key] = {} end table.insert(A[key], hash) end return A end, gen_all_by_tags = function (ydb) return cow(function () local A = ydb:all_by_tags() local ks = sorted(keys(A)) for i,key in ipairs(ks) do local list_of_hashes = A[key] coy(key, list_of_hashes) end end) end, -- -- «YoutubeDB-dump» (to ".YoutubeDB-dump") dump_tags = function (ydb) for tag,listofhashes in ydb:gen_all_by_tags() do print(tag, #listofhashes) -- print tag and how many items it has end end, -- Print all items whose tags are in a list L (default: all tags) dump_by_tags = function (ydb, L) local allbytags = ydb:all_by_tags() L = L or keys(allbytags) for _,t in ipairs(L) do for _,h in ipairs(allbytags[t]) do print(ydb:hash_to_line(h)) end end end, dump_all = function (ydb) for hash,tags,titles in ydb:gen_hash_tags_titles() do print(ydb:hash_to_line(hash)) end end, dump_untagged = function (ydb) local f = function (hash) if ydb:atags(hash) == "" then return ydb:hash_to_line(hash, "\n") end return nil end return table.concat(ydb:all_hashes(f)) end, -- -- Scripts to get the missing titles -- «YoutubeDB-missing-titles» (to ".YoutubeDB-missing-titles") -- was: "script_yt =" new_titles_script = function (ydb, options) local pr, prf, out = ppfo() local Hs = sorted(ydb:title_no():hash_valid().hashes_list) for j=1,#Hs,50 do pr("# "..(j-1)) for i=j,min(j+49,#Hs) do local c = (#Hs[i] == 11) and "" or "# " pr(c.."ytitle "..i.." "..Hs[i].." &") end pr("# sleep 150") pr("") end return title_script0(options)..out() end, new_titles_dump = function (ydb) local pr, prf, out = ppfo() for _,hash in ipairs(ydb:title_no():hash_valid().hashes_list) do local title = title_cat(hash) if title and title ~= "" then pr(" "..youtube_make_url(hash).." "..title) end end return out(lines) end, -- -- Scripts to get the missing dates -- «YoutubeDB-missing-dates» (to ".YoutubeDB-missing-dates") -- Work in progress... see: (find-es "youtube" "upload-date") new_dates_script = function (ydb, options) local pr, prf, out = ppfo() local Hs = sorted(ydb:date_no():hash_valid().hashes_list) for j=1,#Hs,50 do pr("# "..(j-1)) for i=j,min(j+49,#Hs) do local c = (#Hs[i] == 11) and "" or "# " pr(c.."ydate "..i.." "..Hs[i].." &") end pr("# sleep 150") pr("") end return date_script0(options)..out() end, new_dates_dump = function (ydb) local pr, prf, out = ppfo() for _,hash in ipairs(ydb:date_no():hash_valid().hashes_list) do local date = date_cat(hash) if date and date ~= "" then pr(" "..date.." "..youtube_make_url(hash)) end end return out(lines) end, -- -- «YoutubeDB-dates_cat» (to ".YoutubeDB-dates_cat") -- «YoutubeDB-titles_cat» (to ".YoutubeDB-titles_cat") -- These functions use the global variable "ys" (a YScript). dates_cat = function (ydb) for hash,entry in pairs(ydb.hashes) do ydb:add_date(hash, ys:date_cat(hash)) end return ydb end, titles_cat = function (ydb) for hash,entry in pairs(ydb.hashes) do ydb:add_title(hash, ys:title_cat(hash)) end return ydb end, cp_script = function (ydb) return ys:cp_mp4(ydb:fnames_list()) end, mp4s_script = function (ydb) return ys:dl_mp4(ydb:hsorted()) end, dates_script = function (ydb) return ys:dl_date(ydb:hsorted()) end, titles_script = function (ydb, fname) local str = ys:dl_title(ydb:hsorted()) if fname then ee_writefile(fname, str) return format("(Wrote %d bytes into %s)", #str, fname) end -- return ys:dl_title(ydb:hsorted()) return str end, -- -- «YoutubeDB-script_cp_angg» (to ".YoutubeDB-script_cp_angg") -- «YoutubeDB-script_dl_angg» (to ".YoutubeDB-script_dl_angg") -- h = function (ydb) return keys(ydb.hashes) end h = function (ydb) return Set.fromarray(ydb.hashes_list) end, video_fnames_list0 = function (ydb, hashes) local mp4s = {} for hash in hashes:gen() do table.insert(mp4s, ydb.hashes[hash].fnames[1]) end return sorted(mp4s) end, video_fnames_list = function (ydb, hashes) local f = function (fname) return fname.."\n" end return mapconcat(f, ydb:video_fnames_list0(hashes)) end, script_copy_video_files = function(ydb, hashes, targetdir, switches) switches = switches or "-sv" targetdir = targetdir or "." local f = function (fname) return format("cp %s %s %s\n", switches, fname, targetdir) end return mapconcat(f, ydb:video_fnames_list0(hashes)) end, script_cp_angg = function(ydb, hashes) print("Copies to make: "..hashes:n()) return "cd ~/TH/L/manifs/\n"..ydb:script_copy_video_files(hashes) end, script_dl_angg = function(ydb, hashes, dir, options) -- print("Downloads to make: "..hashes:n()) local f = function (hash) return "ydl "..hash.."\n" end return "cd "..(dir or "/sda5/videos/manifs/").."\n".. download_script0(options).. mapconcat(f, hashes:ks()) end, -- -- 2014jul23 -- «YoutubeDB-dates-titles-cat» (to ".YoutubeDB-dates-titles-cat") -- dates_cat = function (ydb, options) -- for hash,entry in pairs(ydb.hashes) do -- ydb:add_date(hash, date_cat(hash, options)) -- end -- return ydb -- end, -- titles_cat = function (ydb, options) -- for hash,entry in pairs(ydb.hashes) do -- ydb:add_title(hash, title_cat(hash, options)) -- end -- return ydb -- end, fnames_list = function (ydb) local L = {} for hash,entry in pairs(ydb.hashes) do local fname = entry.fnames[1] if not fname then error(hash.." has no fname") end table.insert(L, fname) end return sorted(L) end, -- write_lst0 = function (ydb) local mp4s = {} for hash,entry in pairs(ydb.hashes) do local fname = ydb.hashes[hash].fnames[1] if fname then table.insert(mp4s, fname) end end table.sort(mp4s) local f = function (fname) return fname.."\n" end return mapconcat(f, mp4s) end, write_lst = function (ydb, fname) ee_writefile(fname, ydb:write_lst0()) end, write_script_dl = function (ydb, fname, dir, options) ee_writefile(fname, ydb:script_dl_angg(ydb:h(), dir, options)) end, write_script_gd = function (ydb, fname, options) ee_writefile(fname, ydb:new_dates_script(options)) end, write_script_gt = function (ydb, fname, options) ee_writefile(fname, ydb:new_titles_script(options)) end, write_new_txt = function (ydb, fname1, fname2) local bigstr1 = ee_readfile(fname1) -- local bigstr2 = ydb:copy_dates_into(bigstr1) local bigstr2 = ydb:txt_to_txt2(bigstr1) ee_writefile(fname2, bigstr2) end, -- write_script_cp0 = function (ydb, targetdir) local f = function (fname) return format("cp -sv %s %s\n", fname, targetdir or ".") end return mapconcat(f, ydb:fnames_list()) end, write_script_cp = function (ydb, fname, targetdir) ee_writefile(fname, ydb:write_script_cp0(targetdir)) end, -- -- «YoutubeDB-filter» (to ".YoutubeDB-filter") -- Note that all filters create "shallow subsets" of their input -- ydbs! If we do -- ydb2 = ydb:filter(function (h,e) ... end) -- and both ydb and ydb2 have entries for "agiven_hash", then -- ydb.hashes["agiven_hash"] and ydb2.hashes["agiven_hash"] are -- the same table, and changing one changes the other one too! filter = function (ydb, f) local ydb2 = YoutubeDB.new() for hash in ydb:gen_hashes() do local entry = ydb.hashes[hash] if f(hash, entry) then ydb2.hashes[hash] = entry table.insert(ydb2.hashes_list, hash) end end return ydb2 end, err_yes = function (ydb) return ydb:filter(L "h,e -> e.tags.err") end, err_no = function (ydb) return ydb:filter(L "h,e -> not e.tags.err") end, title_yes = function (ydb) return ydb:filter(L "h,e -> #e.titles > 0") end, title_no = function (ydb) return ydb:filter(L "h,e -> #e.titles == 0") end, date_yes = function (ydb) return ydb:filter(L "h,e -> #e.dates > 0") end, date_no = function (ydb) return ydb:filter(L "h,e -> #e.dates == 0") end, tag_yes = function (ydb, tag) return ydb:filter(function (h,e) return e.tags[tag] end) end, tag_no = function (ydb, tag) return ydb:filter(function (h,e) return not e.tags[tag] end) end, tags_eq = function (ydb, tags) return ydb:filter(function (h,e) return ydb:atags(h) == tags end) end, hash_valid = function (ydb) return ydb:filter(L "h,e -> #h == 11") end, -- del_tag = function (ydb, tag) -- beware of the shall copy thing!!! for hash,entry in pairs(ydb.hashes) do entry.tags[tag] = nil end return ydb end, -- -- «YoutubeDB-copy-dates-into» (to ".YoutubeDB-copy-dates-into") copy_date_into_line = function (ydb, li) local yli = YoutubeDB.new():register_videos(li) if #yli.hashes_list == 0 then return li else local hash = yli.hashes_list[1] if ydb:has(hash) then for i,d in ipairs(sort_uniq(ydb.hashes[hash].dates)) do yli:add_date(hash, d) end end return yli:hash_to_line(hash) end end, copy_dates_into = function (ydb, bigstr) local f = function (li) return ydb:copy_date_into_line(li) end return (bigstr:gsub("([^\n]+)", f)) end, -- -- «YoutubeDB-txt2_line» (to ".YoutubeDB-txt2_line") -- This is a hack used to produce a myvideos.txt2 from a myvideos.txt txt2_line = function (ydb, li) local yli = YoutubeDB.new():read0(li) local hash = yli:first_hash() if not hash then return li end if not ydb:has(hash) then return li end return ydb:hash_to_line(hash) end, txt_to_txt2 = function (ydb, bigstr) local f = function (li) return ydb:txt2_line(li) end return (bigstr:gsub("([^\n]+)", f)) end, -- -- «YoutubeDB-line-to-fname» (to ".YoutubeDB-line-to-fname") url_to_fname = function (ydb, url) local ydbli = YoutubeDB.new():read0(url) local hash = ydbli.hashes_list[1]; if not hash then return end local entry = ydb.hashes[hash]; if not entry then return end local fname = entry.fnames[1]; if not fname then return end return fname end, }, } -- «template-dynamic.html» (to ".template-dynamic.html") --[==[ * (eepitch-shell) * (eepitch-kill) * (eepitch-shell) # (find-TH "linkdasruas2") cat > /tmp/template-dynamic.blogme <<'%%%' [htmlize [J $TITLE] [VIDEO_INDEX link_das_ruas $TXT $LST ] ] %%% # makeL cd /tmp/ lua51 ~/blogme3/blogme3.lua \ -o /tmp/template-dynamic.html \ -i /tmp/template-dynamic.blogme # (find-fline "/tmp/template-dynamic.html") cp -v /tmp/template-dynamic.html ~/youtube-db/ # (find-ydb "template-dynamic.html") --]==] -- «bug» -- «write_html_dynamic» (to ".write_html_dynamic") write_html_dynamic = function (fnametxt, fnamelst, fnamehtml) local txt = no_coding(ee_readfile(fnametxt)) local lst = ee_readfile(fnamelst) local A = {TITLE="Videos", TXT=txt, LST=lst} local template = ee_readfile(youtubedbdir.."template-dynamic.html") local html = template:gsub("$(%w+)", A) ee_writefile(fnamehtml, html) end -- «simple_toplevel» (to ".simple_toplevel") simple_toplevel = function (A) error "OBSOLETE - DELETED" end -- «simple_toplevel2» (to ".simple_toplevel2") simple_toplevel2 = function (ys, stem) local o = {} local p = function (str) print((str:gsub("$(%w+)", o))) end local pc = p local pd = p local mp4_dir = ys.mp4dir o.stem = stem o.mp4dir = mp4_dir:gsub("/$", "") -- wrong o.tmpdir = "/tmp/ydbtmp" -- wrong -- local Bigdb = YoutubeDB.new() -- stub local Bigmp4 = YoutubeDB.new() -- stub -- -- Read $stem.txt and videos/*.mp4, -- calculate their intersection and difference, -- generate $stem.lst and $stem.html. -- -- Suffixes: a=all, u=unique, r=repetitions -- i=intersection, o=other -- local Txt = YoutubeDB.new():read(stem..".txt") local Mp4 = YoutubeDB.new():ls(mp4_dir.."/") local Txti, Txto = Txt * Mp4, Txt - Mp4 local Mp4i, Mp4o = Mp4 * Txt, Mp4 - Txt o.Ta = Txt:n0(); o.Tu = Txt:n(); o.Tr = Txt:nrepetitions() o.Ma = Txt:n0(); o.Mu = Txt:n(); o.Mr = Txt:nrepetitions() o.Ti = Txti:n(); o.To = Txto:n() o.Mi = Mp4i:n(); o.Mo = Mp4o:n() for _,str in ipairs(split("Ta Tu Tr Ma Mu Mr Ti To Mi Mo" )) do o[str:lower()] = tostring(o[str]):gsub(".", " ") end -- p "Reading: $stem.txt $Ta videos ($Tu ids + $Tr repetitions)" p "Reading: $mp4dir/*.mp4 $Ma mp4s ($Mu ids + $Mr repetitions)" -- p " mp4s: $Mr+($Mo+$Mi)" -- p " .txt: $mr $mo($Ti+$To)+$Tr" -- p " $mr $mo $Mi $to $tr mp4s mentioned in the .txt" -- p " $mr $Mo $mi $to $tr mp4s not mentioned in the .txt" -- p " $Mr $mo $mi $to $tr mp4s that are repetitions" -- p " $mr $mo $Ti $to $tr urls in the .txt have local mp4s" -- p " $mr $mo $ti $To $tr urls in the .txt don't have local mp4s" -- p " $mr $mo $ti $to $Tr urls in the .txt are repetitions" p " ____________" p " | Mp4s |" p " | _____|_______" p " | M-T | | .txt | Mp4s not in the .txt: $Mo" p " | | M*T | | Mp4s mentioned in the .txt: $Mi" p " |______|_____| T-M | Urls in the .txt without local mp4s: $To" p " | |" p " |_____________|" p "" p "Generating: $stem.lst ($Ti mp4s listed)" Mp4i:write_lst(stem..".lst") if ys.nohtml then p "Not generating $stem.html (due to \"-nohtml\")" else write_html_dynamic(stem..".txt", stem..".lst", stem..".html") p "Generating: $stem.html (dynamic version, with javascript)" end -- -- -- Calculate "Mp4less", "Dateless", "Titleless". -- Note that "Mp4less", "Dateless", etc are subsets of Txt, -- created by filters like ":date_no()" using shallow copies; this -- means that when we add dates and titles to their entries these -- changes will be propagated back to the full "Txt" structure. -- -- Suffixes: l="-less" -- local Mp4less = Txt - Mp4 -- a subset of Txt local Dateless = Txt:date_no() -- a subset of Txt local Titleless = Txt:title_no() -- a subset of Txt o.Ml = Mp4less:n() o.Datel = Dateless:n() o.Titlel = Titleless:n() p "" p "Missing mp4s (not in $mp4dir/*.mp4): $Ml" p "Missing dates (not in $stem.txt): $Datel" p "Missing titles (not in $stem.txt): $Titlel" -- -- Find as many missing mp4s, dates, and titles as possible that -- can be copied or read from disk (i.e., without downloads from -- youtube). -- -- Suffixes: f = found, d = needs download. -- Dateless:dates_cat() -- this adds fields to Txt Titleless:titles_cat() -- this adds fields to Txt -- local Mp4f = Bigmp4 * Mp4less -- not a subset of Txt local Mp4d = Mp4less - Mp4f -- a subset of Txt local Datef = Dateless:date_yes() -- a subset of Txt local Dated = Dateless:date_no() -- a subset of Txt local Titlef = Titleless:title_yes() -- a subset of Txt local Titled = Titleless:title_no() -- a subset of Txt o.Mf = Mp4f:n() o.Md = Mp4d:n() o.Datef = Datef:n() o.Dated = Dated:n() o.Titlef = Titlef:n() o.Titled = Titled:n() pc"Mp4s found in Big_mp4_db: $Mf" pd"Dates found in Big_video_db: $Datef" pd"Titles found in Big_video_db: $Titlef" p "Reading: /tmp/ydb/*.date ($Datef missing dates found there)" p "Reading: /tmp/ydb/*.title ($Titlef missing titles found there)" pc"Mp4s to copy: $Mf" p "Mp4s to download: $Md" p "Dates to download: $Dated" p "Titles to download: $Titled" -- -- Generate scripts to copy/download the missing mp4s, dates and titles p "" pc"Generating: $stem.cp ($Mf mp4s to copy) (NOT YET)" p "Generating: $stem.dlv ($Md mp4s to download)" p "Generating: $stem.dld ($Dated dates to download)" p "Generating: $stem.dlt ($Titled titles to download)" p "Generating: $stem.sh with all the scripts above combined" local script_cp = ys:cp_mp4(Mp4f:fnames_list()) .. "\n" local script_dlv = ys:dl_mp4(Mp4d:hsorted()) .. "\n" local script_dld = ys:dl_date(Dated:hsorted()) .. "\n" local script_dlt = ys:dl_title(Titled:hsorted()) .. "\n" local script_sh = script_cp..script_dld..script_dlt..script_dlv ys:write_script(stem..".cp", script_cp) ys:write_script(stem..".dld", script_dld) ys:write_script(stem..".dlt", script_dlt) ys:write_script(stem..".dlv", script_dlv) ys:write_script(stem..".sh", script_sh) -- -- Generate stem.txt2. -- Note that some operations above may have added dates and titles -- to "Txt", so it may have more information than initially. p "" p "Generating: $stem.txt2" Txt:write_new_txt(stem..".txt", stem..".txt2") -- -- Tell the user how to use the scripts p("") p("# Now run these, in any order (the #-ed lines are not needed):") -- p("source $stem.dld") -- p("source $stem.dlt") -- p("source $stem.dlv") p("source $stem.sh") p("tkdiff $stem.txt $stem.txt2") p("cp -v $stem.txt2 $stem.txt") end -- Local Variables: -- coding: raw-text-unix -- modes: (lua-mode fundamental-mode) -- End: