Skip to content

Commit

Permalink
Refactor structure of caption.cr
Browse files Browse the repository at this point in the history
Rename CaptionsMetadata to Metadata
Nest Metadata under Captions
Unnest LANGUAGES constant from Metadata to main Captions module
  • Loading branch information
syeopite committed Aug 24, 2023
1 parent 3509752 commit 1f7592e
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 78 deletions.
2 changes: 1 addition & 1 deletion src/invidious/frontend/watch_page.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Invidious::Frontend::WatchPage
getter full_videos : Array(Hash(String, JSON::Any))
getter video_streams : Array(Hash(String, JSON::Any))
getter audio_streams : Array(Hash(String, JSON::Any))
getter captions : Array(Invidious::Videos::CaptionMetadata)
getter captions : Array(Invidious::Videos::Captions::Metadata)

def initialize(
@full_videos,
Expand Down
6 changes: 3 additions & 3 deletions src/invidious/videos.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct Video
property updated : Time

@[DB::Field(ignore: true)]
@captions = [] of Invidious::Videos::CaptionMetadata
@captions = [] of Invidious::Videos::Captions::Metadata

@[DB::Field(ignore: true)]
property adaptive_fmts : Array(Hash(String, JSON::Any))?
Expand Down Expand Up @@ -215,9 +215,9 @@ struct Video
keywords.includes? "YouTube Red"
end

def captions : Array(Invidious::Videos::CaptionMetadata)
def captions : Array(Invidious::Videos::Captions::Metadata)
if @captions.empty? && @info.has_key?("captions")
@captions = Invidious::Videos::CaptionMetadata.from_yt_json(info["captions"])
@captions = Invidious::Videos::Captions::Metadata.from_yt_json(info["captions"])
end

return @captions
Expand Down
146 changes: 74 additions & 72 deletions src/invidious/videos/caption.cr
Original file line number Diff line number Diff line change
@@ -1,107 +1,109 @@
require "json"

module Invidious::Videos
struct CaptionMetadata
property name : String
property language_code : String
property base_url : String
module Captions
struct Metadata
property name : String
property language_code : String
property base_url : String

property auto_generated : Bool
property auto_generated : Bool

def initialize(@name, @language_code, @base_url, @auto_generated)
end
def initialize(@name, @language_code, @base_url, @auto_generated)
end

# Parse the JSON structure from Youtube
def self.from_yt_json(container : JSON::Any) : Array(CaptionMetadata)
caption_tracks = container
.dig?("playerCaptionsTracklistRenderer", "captionTracks")
.try &.as_a
# Parse the JSON structure from Youtube
def self.from_yt_json(container : JSON::Any) : Array(Captions::Metadata)
caption_tracks = container
.dig?("playerCaptionsTracklistRenderer", "captionTracks")
.try &.as_a

captions_list = [] of CaptionMetadata
return captions_list if caption_tracks.nil?
captions_list = [] of Captions::Metadata
return captions_list if caption_tracks.nil?

caption_tracks.each do |caption|
name = caption["name"]["simpleText"]? || caption["name"]["runs"][0]["text"]
name = name.to_s.split(" - ")[0]
caption_tracks.each do |caption|
name = caption["name"]["simpleText"]? || caption["name"]["runs"][0]["text"]
name = name.to_s.split(" - ")[0]

language_code = caption["languageCode"].to_s
base_url = caption["baseUrl"].to_s
language_code = caption["languageCode"].to_s
base_url = caption["baseUrl"].to_s

auto_generated = false
if caption["kind"]? && caption["kind"] == "asr"
auto_generated = true
auto_generated = false
if caption["kind"]? && caption["kind"] == "asr"
auto_generated = true
end

captions_list << Captions::Metadata.new(name, language_code, base_url, auto_generated)
end

captions_list << CaptionMetadata.new(name, language_code, base_url, auto_generated)
return captions_list
end

return captions_list
end

def timedtext_to_vtt(timedtext : String, tlang = nil) : String
# In the future, we could just directly work with the url. This is more of a POC
cues = [] of XML::Node
tree = XML.parse(timedtext)
tree = tree.children.first

tree.children.each do |item|
if item.name == "body"
item.children.each do |cue|
if cue.name == "p" && !(cue.children.size == 1 && cue.children[0].content == "\n")
cues << cue
def timedtext_to_vtt(timedtext : String, tlang = nil) : String
# In the future, we could just directly work with the url. This is more of a POC
cues = [] of XML::Node
tree = XML.parse(timedtext)
tree = tree.children.first

tree.children.each do |item|
if item.name == "body"
item.children.each do |cue|
if cue.name == "p" && !(cue.children.size == 1 && cue.children[0].content == "\n")
cues << cue
end
end
break
end
break
end
end
result = String.build do |result|
result << <<-END_VTT
WEBVTT
Kind: captions
Language: #{tlang || @language_code}
result = String.build do |result|
result << <<-END_VTT
WEBVTT
Kind: captions
Language: #{tlang || @language_code}
END_VTT
END_VTT

result << "\n\n"
result << "\n\n"

cues.each_with_index do |node, i|
start_time = node["t"].to_f.milliseconds
cues.each_with_index do |node, i|
start_time = node["t"].to_f.milliseconds

duration = node["d"]?.try &.to_f.milliseconds
duration = node["d"]?.try &.to_f.milliseconds

duration ||= start_time
duration ||= start_time

if cues.size > i + 1
end_time = cues[i + 1]["t"].to_f.milliseconds
else
end_time = start_time + duration
end
if cues.size > i + 1
end_time = cues[i + 1]["t"].to_f.milliseconds
else
end_time = start_time + duration
end

# start_time
result << start_time.hours.to_s.rjust(2, '0')
result << ':' << start_time.minutes.to_s.rjust(2, '0')
result << ':' << start_time.seconds.to_s.rjust(2, '0')
result << '.' << start_time.milliseconds.to_s.rjust(3, '0')
# start_time
result << start_time.hours.to_s.rjust(2, '0')
result << ':' << start_time.minutes.to_s.rjust(2, '0')
result << ':' << start_time.seconds.to_s.rjust(2, '0')
result << '.' << start_time.milliseconds.to_s.rjust(3, '0')

result << " --> "
result << " --> "

# end_time
result << end_time.hours.to_s.rjust(2, '0')
result << ':' << end_time.minutes.to_s.rjust(2, '0')
result << ':' << end_time.seconds.to_s.rjust(2, '0')
result << '.' << end_time.milliseconds.to_s.rjust(3, '0')
# end_time
result << end_time.hours.to_s.rjust(2, '0')
result << ':' << end_time.minutes.to_s.rjust(2, '0')
result << ':' << end_time.seconds.to_s.rjust(2, '0')
result << '.' << end_time.milliseconds.to_s.rjust(3, '0')

result << "\n"
result << "\n"

node.children.each do |s|
result << s.content
node.children.each do |s|
result << s.content
end
result << "\n"
result << "\n"
end
result << "\n"
result << "\n"
end
return result
end
return result
end

# List of all caption languages available on Youtube.
Expand Down
2 changes: 1 addition & 1 deletion src/invidious/videos/transcript.cr
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module Invidious::Videos
# Convert into array of TranscriptLine
lines = self.parse(initial_data)

# Taken from Invidious::Videos::CaptionMetadata.timedtext_to_vtt()
# Taken from Invidious::Videos::Captions::Metadata.timedtext_to_vtt()
vtt = String.build do |vtt|
vtt << <<-END_VTT
WEBVTT
Expand Down
2 changes: 1 addition & 1 deletion src/invidious/views/user/preferences.ecr
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
<label for="captions[0]"><%= translate(locale, "preferences_captions_label") %></label>
<% preferences.captions.each_with_index do |caption, index| %>
<select class="pure-u-1-6" name="captions[<%= index %>]" id="captions[<%= index %>]">
<% Invidious::Videos::CaptionMetadata::LANGUAGES.each do |option| %>
<% Invidious::Videos::Captions::LANGUAGES.each do |option| %>
<option value="<%= option %>" <% if preferences.captions[index] == option %> selected <% end %>><%= translate(locale, option.blank? ? "none" : option) %></option>
<% end %>
</select>
Expand Down

0 comments on commit 1f7592e

Please sign in to comment.