Actions

Module

Module:VideoGallery

From Dune Awakening DB

Revision as of 22:50, 1 June 2025 by Operator (talk | contribs) (Created page with "-- Module:VideoGallery -- Handles video gallery display with database integration local p = {} -- Database connection function local function getDB() return mw.ext.LuaSQLite.open('duneawakening') end -- Convert seconds to MM:SS format local function formatDuration(seconds) if not seconds or seconds == 0 then return "" end local minutes = math.floor(seconds / 60) local secs = seconds % 60 return string.format("%d:%02d", minutes, secs) en...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Documentation for this module may be created at Module:VideoGallery/doc

-- Module:VideoGallery
-- Handles video gallery display with database integration

local p = {}

-- Database connection function
local function getDB()
    return mw.ext.LuaSQLite.open('duneawakening')
end

-- Convert seconds to MM:SS format
local function formatDuration(seconds)
    if not seconds or seconds == 0 then
        return ""
    end
    local minutes = math.floor(seconds / 60)
    local secs = seconds % 60
    return string.format("%d:%02d", minutes, secs)
end

-- Get video thumbnail URL
local function getVideoThumbnail(youtubeId)
    -- YouTube thumbnail URL pattern
    return string.format("https://img.youtube.com/vi/%s/mqdefault.jpg", youtubeId)
end

-- Parse video notes from markdown-style text
local function parseVideoNotes(notes)
    if not notes or notes == "" then
        return "<p>No notes available for this video.</p>"
    end
    
    -- Convert markdown-style formatting
    -- Headers: ### becomes <h3>
    notes = notes:gsub("###%s*([^\n]+)", "<h3>%1</h3>")
    
    -- Lists: lines starting with - become <li>
    notes = notes:gsub("\n%-%s*([^\n]+)", "\n<li>%1</li>")
    
    -- Wrap consecutive <li> items in <ul>
    notes = notes:gsub("(<li>.-</li>)\n(<li>)", "%1\n<ul>\n%2")
    notes = notes:gsub("(</li>)\n([^<])", "%1\n</ul>\n%2")
    
    -- Line breaks
    notes = notes:gsub("\n\n", "</p><p>")
    notes = "<p>" .. notes .. "</p>"
    
    return notes
end

-- Main function to render videos for a category
function p.renderVideos(frame)
    local category = frame.args.category or "featured"
    local db = getDB()
    
    -- Define tag mappings for each category
    local categoryTags = {
        featured = "Featured",
        leveling = "Leveling",
        crafting = "Crafting",
        building = "Building",
        pve = "PVE",
        pvp = "PVP"
    }
    
    local tagName = categoryTags[category] or "Featured"
    
    -- Query to get videos by tag and group by purpose
    local query = [[
        SELECT DISTINCT
            v.video_id,
            v.youtube_id,
            v.title,
            v.channel_title,
            v.duration_sec,
            v.purpose,
            v.description,
            v.video_notes,
            v.published_at
        FROM videos v
        JOIN video_tags vt ON v.video_id = vt.video_id
        JOIN tags t ON vt.tag_id = t.tag_id
        WHERE t.tag_name = ?
        ORDER BY v.purpose, v.published_at DESC
    ]]
    
    local stmt = db:prepare(query)
    stmt:bind_values(tagName)
    
    local videos = {}
    local purposes = {}
    local purposeOrder = {}
    
    -- Group videos by purpose
    for row in stmt:rows() do
        local purpose = row.purpose or "General"
        
        if not videos[purpose] then
            videos[purpose] = {}
            table.insert(purposeOrder, purpose)
        end
        
        table.insert(videos[purpose], {
            video_id = row.video_id,
            youtube_id = row.youtube_id,
            title = row.title,
            channel_title = row.channel_title,
            duration = formatDuration(row.duration_sec),
            duration_sec = row.duration_sec,
            description = row.description,
            video_notes = row.video_notes,
            published_at = row.published_at
        })
    end
    
    stmt:finalize()
    
    -- Build HTML output
    local output = {}
    
    -- Special ordering for certain categories
    local purposeOrderMap = {
        leveling = {
            "Leveling 1-20",
            "Leveling 21-40",
            "Leveling 41-60",
            "Leveling Tips",
            "General"
        },
        crafting = {
            "Basic Crafting",
            "Advanced Crafting",
            "Crafting Stations",
            "Crafting Tips",
            "General"
        },
        building = {
            "Base Building",
            "Advanced Structures",
            "Defense Building",
            "Building Tips",
            "General"
        }
    }
    
    -- Use custom order if available
    if purposeOrderMap[category] then
        purposeOrder = purposeOrderMap[category]
    end
    
    -- Render each purpose section
    for _, purpose in ipairs(purposeOrder) do
        if videos[purpose] then
            table.insert(output, string.format([[
<div class="video-section">
    <div class="video-section-header">
        <h3 class="section-title">%s</h3>
    </div>
    <div class="video-grid">]], purpose))
            
            -- Render video cards
            for _, video in ipairs(videos[purpose]) do
                local thumbnail = getVideoThumbnail(video.youtube_id)
                
                table.insert(output, string.format([[
        <div class="video-card" data-video-id="%s" data-youtube-id="%s">
            <div class="video-thumbnail" style="background-image: url('%s');">
                <span class="video-duration">%s</span>
            </div>
            <div class="video-info">
                <div class="video-title">%s</div>
                <div class="video-channel">%s</div>
            </div>
        </div>]], 
                    video.video_id,
                    video.youtube_id,
                    thumbnail,
                    video.duration,
                    mw.text.encode(video.title),
                    mw.text.encode(video.channel_title)
                ))
            end
            
            table.insert(output, [[
    </div>
</div>]])
        end
    end
    
    db:close()
    
    if #output == 0 then
        return '<div class="video-section"><p style="text-align: center; color: #E3BB7A;">No videos found for this category.</p></div>'
    end
    
    return table.concat(output, "\n")
end

-- Function to get all video data as JSON for JavaScript
function p.getVideoData(frame)
    local db = getDB()
    
    local query = [[
        SELECT 
            v.video_id,
            v.youtube_id,
            v.title,
            v.channel_title,
            v.author,
            v.duration_sec,
            v.purpose,
            v.description,
            v.video_notes,
            v.published_at,
            GROUP_CONCAT(t.tag_name) as tags
        FROM videos v
        LEFT JOIN video_tags vt ON v.video_id = vt.video_id
        LEFT JOIN tags t ON vt.tag_id = t.tag_id
        GROUP BY v.video_id
        ORDER BY v.published_at DESC
    ]]
    
    local stmt = db:prepare(query)
    local videoData = {}
    
    for row in stmt:rows() do
        local tags = {}
        if row.tags then
            for tag in string.gmatch(row.tags, "[^,]+") do
                table.insert(tags, mw.text.trim(tag))
            end
        end
        
        videoData[tostring(row.video_id)] = {
            video_id = row.video_id,
            youtube_id = row.youtube_id,
            title = row.title,
            channel_title = row.channel_title,
            author = row.author,
            duration = formatDuration(row.duration_sec),
            duration_sec = row.duration_sec,
            purpose = row.purpose,
            description = row.description,
            video_notes = parseVideoNotes(row.video_notes),
            published_at = row.published_at,
            tags = tags
        }
    end
    
    stmt:finalize()
    db:close()
    
    return mw.text.jsonEncode(videoData)
end

-- Function to render a single video player (called via AJAX)
function p.renderVideoPlayer(frame)
    local videoId = frame.args.video_id or frame.args[1]
    
    if not videoId then
        return '<div class="error">No video ID provided</div>'
    end
    
    local db = getDB()
    
    local query = [[
        SELECT 
            v.*,
            GROUP_CONCAT(t.tag_name) as tags
        FROM videos v
        LEFT JOIN video_tags vt ON v.video_id = vt.video_id
        LEFT JOIN tags t ON vt.tag_id = t.tag_id
        WHERE v.video_id = ?
        GROUP BY v.video_id
    ]]
    
    local stmt = db:prepare(query)
    stmt:bind_values(videoId)
    
    local video = nil
    for row in stmt:rows() do
        video = row
        break
    end
    
    stmt:finalize()
    db:close()
    
    if not video then
        return '<div class="error">Video not found</div>'
    end
    
    -- Parse published date
    local publishedDate = video.published_at:match("(%d%d%d%d%-%d%d%-%d%d)")
    
    -- Build player HTML
    local output = string.format([[
<div class="video-embed-container">
    <iframe src="https://www.youtube.com/embed/%s?rel=0&modestbranding=1" 
            frameborder="0" 
            allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" 
            allowfullscreen>
    </iframe>
</div>

<div class="video-details-header">
    <h2 class="video-main-title">%s</h2>
    <div class="video-meta">
        <span class="video-author">👤 %s</span>
        <span class="video-date">📅 %s</span>
    </div>
</div>

<div class="video-notes-section">
    <div class="notes-header">Video Notes</div>
    <div class="notes-content">
        %s
    </div>
</div>]],
        video.youtube_id,
        mw.text.encode(video.title),
        mw.text.encode(video.author or video.channel_title),
        publishedDate,
        parseVideoNotes(video.video_notes)
    )
    
    return output
end

return p