Guest User

chapter_frame_older

a guest
Apr 13th, 2024
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.08 KB | Source Code | 0 0
  1. # DOES NOT APPEND CHAPTERS CORRECTLY even if you delete chapter file after loading and resave
  2. --[[
  3.   * chapter_make_read.lua v.2022-06-24
  4.   *
  5.   * AUTHORS: dyphire
  6.   * License: MIT
  7.   * link: https://github.com/dyphire/mpv-scripts
  8. --]]
  9. -- Implementation read and automatically load the namesake external chapter file.
  10. -- The external chapter files should conform to the following formats.
  11. -- Note: The time should strictly follow the 12-bit format of 'hh:mm:ss.sss' !!
  12. -- Note: The file encoding should be UTF-8 and the linebreak should be Unix(LF).
  13. -- Note: The script also supports reading OGM format and MediaInfo format in addition to the following formats.
  14. --[[
  15. 00:00:00.000 A part
  16. 00:00:40.312 OP
  17. 00:02:00.873 B part
  18. 00:10:44.269 C part
  19. 00:22:40.146 ED
  20. --]]
  21.  
  22. -- This script also supports marks and creates external chapter files, usage:
  23. -- Note: It can also be used to export the existing chapter information of the playing file.
  24. -- add bindings to input.conf:
  25. -- key script-message-to chapter_make_read create_chapter
  26. -- key script-message-to chapter_make_read write_chapter
  27. -- key script-message-to chapter_make_read write_chapter_xml
  28.  
  29. local msg = require 'mp.msg'
  30. local utils = require 'mp.utils'
  31. local options = require "mp.options"
  32.  
  33. local o = {
  34.     read_external_chapter = true,
  35.     -- Specifies the extension of the external chapter file.
  36.     chapter_flie_ext = "_chapter.chp",
  37.     -- Specifies the subpath of the same directory as the playing file as the external chapter file path.
  38.     -- Note: The external chapter file is read from the subdirectory first.
  39.     -- If the file does not exist, it will next be read from the same directory as the playing file.
  40.     external_chapter_subpath = "chapters",
  41. }
  42.  
  43. (require 'mp.options').read_options(o)
  44.  
  45. local function read_chapter(func)
  46.     local f = io.open(chapter_fullpath, "r")
  47.     if not f then return end
  48.     local contents = {}
  49.     for line in f:lines() do
  50.             table.insert(contents, (func(line)))
  51.     end
  52.     f:close()
  53.     return contents
  54. end
  55.  
  56. local function read_chapter_table()
  57.     local line_pos = 0
  58.     return read_chapter(function(line)
  59.         local h, m, s, t, n, l
  60.         if line:match("^%d+:%d+:%d+") ~= nil then
  61.             h, m, s = line:match("^(%d+):(%d+):(%d+.%d*)")
  62.             t = h*3600 + m*60 + s
  63.             if line:match("^%d+:%d+:%d+.%d*[,%s].*") ~= nil then
  64.                 n = line:match("^%d+:%d+:%d+.%d*[,%s](.*)")
  65.                 n = n:gsub(":%s%a?%a?:", "")
  66.                     :gsub("^%s*(.-)%s*$", "%1")
  67.             end
  68.             l = line
  69.             line_pos = line_pos + 1
  70.         elseif line:match("^CHAPTER%d+=%d+:%d+:%d+") ~= nil then
  71.             h, m, s = line:match("^CHAPTER%d+=(%d+):(%d+):(%d+.%d*)")
  72.             t = h*3600 + m*60 + s
  73.             l = line
  74.             line_pos = line_pos + 1
  75.         elseif line:match("^CHAPTER%d+NAME=.*") ~= nil then
  76.             n = line:gsub("^CHAPTER%d+NAME=", "")
  77.             n = n:gsub("^%s*(.-)%s*$", "%1")
  78.             l = line
  79.             line_pos = line_pos + 1
  80.         else return end
  81.         return {found_title = n, found_time = t, found_line = l}
  82.     end)
  83. end
  84.  
  85. local function mark_chapter()
  86.     if not o.read_external_chapter then return end
  87.     local all_chapters = mp.get_property_native("chapter-list")
  88.     local chapter_index = 0
  89.     local chapters_time = {}
  90.     local chapters_title = {}
  91.     local path = mp.get_property("path")
  92.     local dir, filename = utils.split_path(path)
  93.     local fpath = dir:gsub("\\", "/")
  94.     local fname = mp.get_property("filename/no-ext")
  95.     local chapter_fliename = fname .. o.chapter_flie_ext
  96.     chapter_fullpath = fpath .. o.external_chapter_subpath .. "/" .. chapter_fliename
  97.     if io.open(chapter_fullpath, "r") == nil then
  98.         chapter_fullpath = fpath .. chapter_fliename
  99.     end
  100.     list_contents = read_chapter_table()
  101.  
  102.     if not list_contents then return end
  103.     for i = 1, #list_contents do
  104.         local chapter_time = tonumber(list_contents[i].found_time)
  105.         if chapter_time ~= nil and chapter_time >= 0 then
  106.             table.insert(chapters_time, chapter_time)
  107.         end
  108.         if list_contents[i].found_title ~= nil then
  109.             table.insert(chapters_title, list_contents[i].found_title)
  110.         end
  111.     end
  112.     if not chapters_time[1] then return end
  113.    
  114.     table.sort(chapters_time, function(a, b) return a < b end)
  115.    
  116.     for i = 1, #chapters_time do
  117.         chapter_index = chapter_index + 1
  118.         all_chapters[chapter_index] = {
  119.             title = chapters_title[i] or ("Chapter " .. string.format("%02.f", chapter_index)),
  120.             time = chapters_time[i]
  121.         }
  122.     end
  123.  
  124.     table.sort(all_chapters, function(a, b) return a['time'] < b['time'] end)
  125.  
  126.     mp.set_property_native("chapter-list", all_chapters)
  127.     msg.info("load external chapter flie successful: " .. chapter_fliename)
  128. end
  129.  
  130. local function create_chapter()
  131.     local time_pos = mp.get_property_number("time-pos")
  132.     local frame_pos = mp.get_property_number("estimated-frame-number")
  133.     local frame_pos_osd = mp.get_property_osd("estimated-frame-number")
  134.     local curr_chapter = mp.get_property_number("chapter")
  135.     local chapter_count = mp.get_property_number("chapter-list/count")
  136.     local all_chapters = mp.get_property_native("chapter-list")
  137.     mp.osd_message(frame_pos_osd, 1)
  138.  
  139.     if chapter_count == 0 then
  140.         all_chapters[1] = {
  141.             title = frame_pos .. " " .. "Chapter 01",
  142.             time = time_pos
  143.         }
  144.         -- We just set it to zero here so when we add 1 later it ends up as 1
  145.         -- otherwise it's probably "nil"
  146.         curr_chapter = 0
  147.         -- note that mpv will treat the beginning of the file as all_chapters[0] when using pageup/pagedown
  148.         -- so we don't actually have to worry if the file doesn't start with a chapter
  149.     else
  150.         -- to insert a chapter we have to increase the index on all subsequent chapters
  151.         -- otherwise we'll end up with duplicate chapter IDs which will confuse mpv
  152.         -- +2 looks weird, but remember mpv indexes at 0 and lua indexes at 1
  153.         -- adding two will turn "current chapter" from mpv notation into "next chapter" from lua's notation
  154.         -- count down because these areas of memory overlap
  155.         for i = chapter_count, curr_chapter + 2, -1 do
  156.             all_chapters[i + 1] = all_chapters[i]
  157.         end
  158.         all_chapters[curr_chapter+2] = {
  159.             title = frame_pos .. " " .. "Chapter " .. string.format("%02.f", curr_chapter+2),
  160.             time = time_pos
  161.         }
  162.     end
  163.     mp.set_property_native("chapter-list", all_chapters)
  164.     mp.set_property_number("chapter", curr_chapter+1)
  165. end
  166.  
  167. local function format_time(seconds)
  168.     local result = ""
  169.     if seconds <= 0 then
  170.         return "00:00:00.000";
  171.     else
  172.         hours = string.format("%02.f", math.floor(seconds/3600))
  173.         mins = string.format("%02.f", math.floor(seconds/60 - (hours*60)))
  174.         secs = string.format("%02.f", math.floor(seconds - hours*60*60 - mins*60))
  175.         msecs = string.format("%03.f", seconds*1000 - hours*60*60*1000 - mins*60*1000 - secs*1000)
  176.         result = hours..":"..mins..":"..secs.."."..msecs
  177.     end
  178.     return result
  179. end
  180.  
  181. local function write_chapter()
  182.     local euid = mp.get_property_number("estimated-frame-count")
  183.     local chapter_count = mp.get_property_number("chapter-list/count")
  184.     local all_chapters = mp.get_property_native("chapter-list")
  185.     local insert_chapters = ""
  186.     local curr = nil
  187.  
  188.     for i = 1, chapter_count, 1 do
  189.         curr = all_chapters[i]
  190.         local time_pos = format_time(curr.time)
  191.         local next_chapter = curr.title .. "\n"
  192.         insert_chapters = insert_chapters .. next_chapter
  193.     end
  194.  
  195.     local chapters = insert_chapters
  196.  
  197.     local path = mp.get_property("path")
  198.     dir, name_ext = utils.split_path(path)
  199.     local name = string.sub(name_ext, 1, (string.len(name_ext)-4))
  200.     local out_path = utils.join_path(dir, name .. o.chapter_flie_ext)
  201.     local file = io.open(out_path, "w")
  202.     if file == nil then
  203.         dir = utils.getcwd()
  204.         out_path = utils.join_path(dir, "create" .. o.chapter_flie_ext)
  205.         file = io.open(out_path, "w")
  206.     end
  207.     if file == nil then
  208.         mp.error("Could not open chapter file for writing.")
  209.         return
  210.     end
  211.     file:write(chapters)
  212.     file:close()
  213.     mp.osd_message("Export file to: " .. out_path, 3)
  214. end
  215.  
  216. local function write_chapter_xml()
  217.     local euid = mp.get_property_number("estimated-frame-count")
  218.     local chapter_count = mp.get_property_number("chapter-list/count")
  219.     local all_chapters = mp.get_property_native("chapter-list")
  220.     local insert_chapters = ""
  221.     local curr = nil
  222.  
  223.     for i = 1, chapter_count, 1 do
  224.         curr = all_chapters[i]
  225.         local time_pos = format_time(curr.time)
  226.  
  227.         if i == 1 and curr.time ~= 0 then
  228.             local first_chapter="    <ChapterAtom>\n      <ChapterUID>"..math.random(1000, 9000).."</ChapterUID>\n      <ChapterFlagHidden>0</ChapterFlagHidden>\n      <ChapterFlagEnabled>1</ChapterFlagEnabled>\n      <ChapterDisplay>\n        <ChapterString>Prologue</ChapterString>\n        <ChapterLanguage>eng</ChapterLanguage>\n      </ChapterDisplay>\n      <ChapterTimeStart>0</ChapterTimeStart>\n    </ChapterAtom>\n"
  229.             insert_chapters = insert_chapters..first_chapter
  230.         end
  231.  
  232.         local next_chapter="      <ChapterAtom>\n        <ChapterDisplay>\n          <ChapterString>"..string.sub(curr.title, string.find(curr.title, " ") + 1).."</ChapterString>\n          <ChapterLanguage>eng</ChapterLanguage>\n        </ChapterDisplay>\n        <ChapterUID>"..math.random(1000, 9000).."</ChapterUID>\n        <ChapterTimeStart>"..string.sub(curr.title, 1, string.find(curr.title, " ") - 1).."</ChapterTimeStart>\n        <ChapterFlagHidden>0</ChapterFlagHidden>\n        <ChapterFlagEnabled>1</ChapterFlagEnabled>\n      </ChapterAtom>\n"
  233.         insert_chapters = insert_chapters..next_chapter
  234.     end
  235.  
  236.     local chapters="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<Chapters>\n  <EditionEntry>\n    <EditionFlagHidden>0</EditionFlagHidden>\n    <EditionFlagDefault>0</EditionFlagDefault>\n    <EditionUID>"..euid.."</EditionUID>\n"..insert_chapters.."  </EditionEntry>\n</Chapters>"
  237.  
  238.     local path = mp.get_property("path")
  239.     dir, name_ext = utils.split_path(path)
  240.     local name = string.sub(name_ext, 1, (string.len(name_ext)-4))
  241.     local out_path = utils.join_path(dir, name.."_chapter.xml")
  242.     local file = io.open(out_path, "w")
  243.     if file == nil then
  244.         dir = utils.getcwd()
  245.         out_path = utils.join_path(dir, "create_chapter.xml")
  246.         file = io.open(out_path, "w")
  247.     end
  248.     if file == nil then
  249.         mp.error("Could not open chapter file for writing.")
  250.         return
  251.     end
  252.     file:write(chapters)
  253.     file:close()
  254.     mp.osd_message("Export file to: "..out_path, 3)
  255. end
  256.  
  257. mp.add_hook("on_preloaded", 50, mark_chapter)
  258.  
  259. mp.register_script_message("create_chapter", create_chapter, {repeatable=true})
  260. mp.register_script_message("write_chapter", write_chapter, {repeatable=false})
  261. mp.register_script_message("write_chapter_xml", write_chapter_xml, {repeatable=false})
Advertisement
Add Comment
Please, Sign In to add comment