SERVICE.Name = "Direct Media"
SERVICE.Id = "dm5"
SERVICE.Base = "res"
SERVICE.PrefetchMetadata = true
SERVICE.FileExtensions = {
"webm", "mp4", "mov", "mkv", "avi", "flv", "wmv", "m4v", "3gp", "ogv", "ts", "m2ts",
"m3u8", "mpd",
"png", "jpg", "jpeg", "gif", "webp", "avif", "apng", "bmp", "tiff", "tif", "ico", "svg"
}
DEFINE_BASECLASS( "mp_service_browser" )
local VideoMimeTypes = {
webm = "video/webm",
mp4 = "video/mp4",
mov = "video/quicktime",
mkv = "video/x-matroska",
avi = "video/x-msvideo",
flv = "video/x-flv",
wmv = "video/x-ms-wmv",
m4v = "video/mp4",
["3gp"] = "video/3gpp",
ogv = "video/ogg",
ts = "video/mp2t",
m2ts = "video/mp2t",
m3u8 = "application/x-mpegURL",
mpd = "application/dash+xml"
}
local function IsStreamingFormat( ext )
ext = string.lower( ext or "" )
return ext == "m3u8" or ext == "mpd"
end
function SERVICE:IsTimed()
if self._istimed == nil then
self._istimed = self:Duration() > 0
end
return self._istimed
end
if CLIENT then
local JS_Pause = "if(window.MediaPlayer) MediaPlayer.pause();"
local JS_Play = "if(window.MediaPlayer) MediaPlayer.play();"
local JS_Volume = "if(window.MediaPlayer) MediaPlayer.volume = %s;"
local JS_Seek = [[
if (window.MediaPlayer) {
var seekTime = %s;
var curTime = window.MediaPlayer.currentTime;
var diffTime = Math.abs(curTime - seekTime);
if (diffTime > 5) {
window.MediaPlayer.currentTime = seekTime;
}
}
]]
local VideoEmbedHTML = [[
♪
Audio Only Stream
(No 3D audio support)
]]
local ImageEmbedHTML = [[
Failed to load image
]]
function SERVICE:GetHTML()
local url = self.url
local path = self.urlinfo.path
local ext = path and path:match("[^/]+%.(%S+)$") or ""
if VideoMimeTypes[ext] then
return VideoEmbedHTML:format(url)
else
return ImageEmbedHTML:format(url)
end
end
function SERVICE:OnBrowserReady( browser )
if self._Paused then
self.Browser:RunJavascript( JS_Play )
self._Paused = nil
return
end
BaseClass.OnBrowserReady( self, browser )
local html = self.WrapHTML( self:GetHTML() )
browser:SetHTML( html )
end
function SERVICE:Pause()
BaseClass.Pause( self )
if IsValid(self.Browser) then
self.Browser:RunJavascript(JS_Pause)
end
end
function SERVICE:SetVolume( volume )
if not IsValid(self.Browser) then return end
local js = JS_Volume:format( volume )
self.Browser:RunJavascript(js)
end
function SERVICE:Sync()
local seekTime = self:CurrentTime()
if IsValid(self.Browser) and self:IsTimed() and seekTime > 0 then
self.Browser:RunJavascript(JS_Seek:format(seekTime))
end
end
function SERVICE:IsMouseInputEnabled()
return IsValid( self.Browser )
end
function SERVICE:PreRequest( callback )
local path = self.urlinfo.path
local ext = path and path:match("[^/]+%.(%S+)$") or ""
if IsStreamingFormat(ext) then
self._metaDuration = 0
self._isStreaming = true
callback()
elseif VideoMimeTypes[ext] then
MediaPlayerUtils.GatherVideoDuration( self.url, function(success, response)
if success then
self._metaDuration = response
self._isStreaming = false
callback()
return
end
callback(response)
end )
else
self._metaDuration = 0
callback()
end
end
function SERVICE:NetWriteRequest()
net.WriteUInt( self._metaDuration or 0, 16 )
net.WriteBool( self._isStreaming or false )
end
end
if SERVER then
function SERVICE:GetMetadata( callback )
local cached, found = self:GetCachedMetadata()
if found then
callback(cached)
return
end
local metadata = {}
local path = self.urlinfo.path
local ext = path and path:match("[^/]+%.(%S+)$") or ""
if not VideoMimeTypes[ext] then
local filename = path and string.match(path, "([^/]+)%.%w-$")
metadata.title = filename or self.url
else
metadata.title = self.url
end
metadata.duration = self._metaDuration or 0
self:SetMetadata(metadata, true)
MediaPlayer.Metadata:Save(self)
callback(self._metadata)
end
function SERVICE:NetReadRequest()
if not self.PrefetchMetadata then return end
self._metaDuration = net.ReadUInt( 16 )
self._isStreaming = net.ReadBool()
end
end