@ -148,26 +148,32 @@ statistics = {
}
}
if config . statistics_enabled
if config . statistics_enabled
spawn do
spawn do
loop do
statistics = {
statistics = {
" version " = > " 2.0 " ,
" version " = > " 2.0 " ,
" software " = > SOFTWARE ,
" software " = > SOFTWARE ,
" openRegistrations " = > config . registration_enabled ,
" openRegistrations " = > config . registration_enabled ,
" usage " = > {
" usage " = > {
" users " = > {
" users " = > {
" total " = > PG_DB . query_one ( " SELECT count(*) FROM users " , as : Int64 ) ,
" total " = > PG_DB . query_one ( " SELECT count(*) FROM users " , as : Int64 ) ,
" activeHalfyear " = > PG_DB . query_one ( " SELECT count(*) FROM users WHERE CURRENT_TIMESTAMP - updated < '6 months' " , as : Int64 ) ,
" activeHalfyear " = > PG_DB . query_one ( " SELECT count(*) FROM users WHERE CURRENT_TIMESTAMP - updated < '6 months' " , as : Int64 ) ,
" activeMonth " = > PG_DB . query_one ( " SELECT count(*) FROM users WHERE CURRENT_TIMESTAMP - updated < '1 month' " , as : Int64 ) ,
" activeMonth " = > PG_DB . query_one ( " SELECT count(*) FROM users WHERE CURRENT_TIMESTAMP - updated < '1 month' " , as : Int64 ) ,
} ,
} ,
" metadata " = > {
" updatedAt " = > Time . utc . to_unix ,
" lastChannelRefreshedAt " = > PG_DB . query_one? ( " SELECT updated FROM channels ORDER BY updated DESC LIMIT 1 " , as : Time ) . try & . to_unix || 0 ,
} ,
} ,
}
} ,
" metadata " = > {
" updatedAt " = > Time . utc . to_unix ,
" lastChannelRefreshedAt " = > PG_DB . query_one? ( " SELECT updated FROM channels ORDER BY updated DESC LIMIT 1 " , as : Time ) . try & . to_unix || 0 _i64 ,
} ,
}
loop do
sleep 1 . minute
sleep 1 . minute
Fiber . yield
Fiber . yield
statistics [ " usage " ] . as ( Hash ) [ " users " ] . as ( Hash ) [ " total " ] = PG_DB . query_one ( " SELECT count(*) FROM users " , as : Int64 )
statistics [ " usage " ] . as ( Hash ) [ " users " ] . as ( Hash ) [ " activeHalfyear " ] = PG_DB . query_one ( " SELECT count(*) FROM users WHERE CURRENT_TIMESTAMP - updated < '6 months' " , as : Int64 )
statistics [ " usage " ] . as ( Hash ) [ " users " ] . as ( Hash ) [ " activeMonth " ] = PG_DB . query_one ( " SELECT count(*) FROM users WHERE CURRENT_TIMESTAMP - updated < '1 month' " , as : Int64 )
statistics [ " metadata " ] . as ( Hash ( String , Int64 ) ) [ " updatedAt " ] = Time . utc . to_unix
statistics [ " metadata " ] . as ( Hash ( String , Int64 ) ) [ " lastChannelRefreshedAt " ] = PG_DB . query_one? ( " SELECT updated FROM channels ORDER BY updated DESC LIMIT 1 " , as : Time ) . try & . to_unix || 0 _i64
end
end
end
end
end
end
@ -3223,35 +3229,35 @@ get "/api/v1/storyboards/:id" do |env|
storyboard = storyboard [ 0 ]
storyboard = storyboard [ 0 ]
end
end
webvtt = << - END_VTT
String . build do | str |
WEBVTT
str << << - END_VTT
WEBVTT
END_VTT
END_VTT
start_time = 0 . milliseconds
start_time = 0 . milliseconds
end_time = storyboard [ :interval ] . milliseconds
end_time = storyboard [ :interval ] . milliseconds
storyboard [ :storyboard_count ] . times do | i |
storyboard [ :storyboard_count ] . times do | i |
host_url = make_host_url ( config , Kemal . config )
host_url = make_host_url ( config , Kemal . config )
url = storyboard [ :url ] . gsub ( " $M " , i ) . gsub ( " https://i9.ytimg.com " , host_url )
url = storyboard [ :url ] . gsub ( " $M " , i ) . gsub ( " https://i9.ytimg.com " , host_url )
storyboard [ :storyboard_height ] . times do | j |
storyboard [ :storyboard_height ] . times do | j |
storyboard [ :storyboard_width ] . times do | k |
storyboard [ :storyboard_width ] . times do | k |
webvtt += << - END_CUE
str << << - END_CUE
#{start_time}.000 --> #{end_time}.000
#{start_time}.000 --> #{end_time}.000
#{url}#xywh=#{storyboard[:width] * k},#{storyboard[:height] * j},#{storyboard[:width]},#{storyboard[:height]}
#{url}#xywh=#{storyboard[:width] * k},#{storyboard[:height] * j},#{storyboard[:width]},#{storyboard[:height]}
END_CUE
END_CUE
start_time += storyboard [ :interval ] . milliseconds
start_time += storyboard [ :interval ] . milliseconds
end_time += storyboard [ :interval ] . milliseconds
end_time += storyboard [ :interval ] . milliseconds
end
end
end
end
end
end
end
webvtt
end
end
get " /api/v1/captions/:id " do | env |
get " /api/v1/captions/:id " do | env |
@ -3321,7 +3327,7 @@ get "/api/v1/captions/:id" do |env|
caption = caption [ 0 ]
caption = caption [ 0 ]
end
end
url = caption . baseUrl + " &tlang= #{ tlang } "
url = " #{ caption . baseUrl } &tlang= #{ tlang } "
# Auto-generated captions often have cues that aren't aligned properly with the video,
# Auto-generated captions often have cues that aren't aligned properly with the video,
# as well as some other markup that makes it cumbersome, so we try to fix that here
# as well as some other markup that makes it cumbersome, so we try to fix that here
@ -3329,46 +3335,47 @@ get "/api/v1/captions/:id" do |env|
caption_xml = client . get ( url ) . body
caption_xml = client . get ( url ) . body
caption_xml = XML . parse ( caption_xml )
caption_xml = XML . parse ( caption_xml )
webvtt = << - END_VTT
webvtt = String . build do | str |
WEBVTT
str << << - END_VTT
Kind : captions
WEBVTT
Language : #{tlang || caption.languageCode}
Kind : captions
Language : #{tlang || caption.languageCode}
END_VTT
END_VTT
caption_nodes = caption_xml . xpath_nodes ( " //transcript/text " )
caption_nodes = caption_xml . xpath_nodes ( " //transcript/text " )
caption_nodes . each_with_index do | node , i |
caption_nodes . each_with_index do | node , i |
start_time = node [ " start " ] . to_f . seconds
start_time = node [ " start " ] . to_f . seconds
duration = node [ " dur " ]? . try & . to_f . seconds
duration = node [ " dur " ]? . try & . to_f . seconds
duration || = start_time
duration || = start_time
if caption_nodes . size > i + 1
if caption_nodes . size > i + 1
end_time = caption_nodes [ i + 1 ] [ " start " ] . to_f . seconds
end_time = caption_nodes [ i + 1 ] [ " start " ] . to_f . seconds
else
else
end_time = start_time + duration
end_time = start_time + duration
end
end
start_time = " #{ start_time . hours . to_s . rjust ( 2 , '0' ) } : #{ start_time . minutes . to_s . rjust ( 2 , '0' ) } : #{ start_time . seconds . to_s . rjust ( 2 , '0' ) } . #{ start_time . milliseconds . to_s . rjust ( 3 , '0' ) } "
start_time = " #{ start_time . hours . to_s . rjust ( 2 , '0' ) } : #{ start_time . minutes . to_s . rjust ( 2 , '0' ) } : #{ start_time . seconds . to_s . rjust ( 2 , '0' ) } . #{ start_time . milliseconds . to_s . rjust ( 3 , '0' ) } "
end_time = " #{ end_time . hours . to_s . rjust ( 2 , '0' ) } : #{ end_time . minutes . to_s . rjust ( 2 , '0' ) } : #{ end_time . seconds . to_s . rjust ( 2 , '0' ) } . #{ end_time . milliseconds . to_s . rjust ( 3 , '0' ) } "
end_time = " #{ end_time . hours . to_s . rjust ( 2 , '0' ) } : #{ end_time . minutes . to_s . rjust ( 2 , '0' ) } : #{ end_time . seconds . to_s . rjust ( 2 , '0' ) } . #{ end_time . milliseconds . to_s . rjust ( 3 , '0' ) } "
text = HTML . unescape ( node . content )
text = HTML . unescape ( node . content )
text = text . gsub ( / <font color=" # [a-fA-F0-9]{6}"> / , " " )
text = text . gsub ( / <font color=" # [a-fA-F0-9]{6}"> / , " " )
text = text . gsub ( / < \/ font> / , " " )
text = text . gsub ( / < \/ font> / , " " )
if md = text . match ( / (?<name>.*) : (?<text>.*) / )
if md = text . match ( / (?<name>.*) : (?<text>.*) / )
text = " <v #{ md [ " name " ] } > #{ md [ " text " ] } </v> "
text = " <v #{ md [ " name " ] } > #{ md [ " text " ] } </v> "
end
end
webvtt += << - END_CUE
str << << - END_CUE
#{start_time} --> #{end_time}
#{start_time} --> #{end_time}
#{text}
#{text}
END_CUE
END_CUE
end
end
end
else
else
url += " &format=vtt "
webvtt = client . get ( " #{ url } &format=vtt " ) . body
webvtt = client . get ( url ) . body
end
end
if title = env . params . query [ " title " ]?
if title = env . params . query [ " title " ]?
@ -4833,43 +4840,6 @@ get "/videoplayback" do |env|
end
end
end
end
response = HTTP :: Client :: Response . new ( 403 )
5 . times do
begin
client = make_client ( URI . parse ( host ) , region )
response = client . head ( url , headers )
break
rescue Socket :: Addrinfo :: Error
if ! mns . empty?
mn = mns . pop
end
fvip = " 3 "
host = " https://r #{ fvip } --- #{ mn } .googlevideo.com "
rescue ex
end
end
if response . headers [ " Location " ]?
url = URI . parse ( response . headers [ " Location " ] )
host = url . host
env . response . headers [ " Access-Control-Allow-Origin " ] = " * "
url = url . full_path
url += " &host= #{ host } "
if region
url += " ®ion= #{ region } "
end
next env . redirect url
end
if response . status_code >= 400
env . response . status_code = response . status_code
next
end
if url . includes? " &file=seg.ts "
if url . includes? " &file=seg.ts "
if CONFIG . disabled? ( " livestreams " )
if CONFIG . disabled? ( " livestreams " )
env . response . status_code = 403
env . response . status_code = 403
@ -4957,11 +4927,7 @@ get "/videoplayback" do |env|
if location = response . headers [ " Location " ]?
if location = response . headers [ " Location " ]?
location = URI . parse ( location )
location = URI . parse ( location )
location = " #{ location . full_path } &host= #{ location . host } "
location = " #{ location . full_path } &host= #{ location . host } #{ region ? " ®ion= #{ region } " : " " } "
if region
location += " ®ion= #{ region } "
end
env . redirect location
env . redirect location
break
break