Documentation for this module may be created at Module:XVideosRedList/doc
-- This module handles the display of XVideos RED channel information
local p = {}
-- Load the i18n submodule for text strings and error messages
local i18n = require('Module:XVideosRedList/i18n')
-- Base URL for XVideos RED
local BASE_URL = "https://www.xvideos.red/"
-- Suffix to append to all XVideos RED URLs
local URL_SUFFIX = "?sxcaf=TARTFSJC35"
-- Main header URL for title link
local HEADER_URL = "https://www.xvideos.red/gay?sxcaf=TARTFSJC35"
-- Default category for all channels
local DEFAULT_CATEGORY = "XVideos RED models"
-- Helper function to check if a table contains a specific value
-- @param tbl table: The table to search
-- @param value any: The value to find
-- @return boolean: True if the value is found, false otherwise
function table.contains(tbl, value)
for _, v in pairs(tbl) do
if v == value then return true end
end
return false
end
-- Helper function to format error message in red bold text
-- @param error string: The error message
-- @return string: Formatted error message
local function formatError(error)
return string.format('|-\n| colspan="5" style="text-align:center;vertical-align:middle;color:red;font-weight:bold;" | %s\n', error)
end
-- Table of genres with their aliases for normalization
-- Each key is a standard genre name, and the value is a list of aliases or abbreviations
local genres = {
Straight = {"s", "str", "str8"},
Gay = {"g", "homo", "h", "homosexual"},
Transexual = {"t", "trans", "tra", "trs"},
Bisexual = {"b", "bi", "bisex"}
}
-- Table of icons for standard genres
-- Maps standard genre names to their corresponding icon file names
local iconMap = {
["Straight"] = "StraightIconPNG.png",
["Gay"] = "GayIconPNG.png",
["Transexual"] = "TranssexualIconPNG.png",
["Bisexual"] = "BisexualIconPNG.png",
}
-- Function to normalize genre input to a standard genre name
-- @param input string: The genre input to normalize
-- @return string: The standard genre name if matched, otherwise the original input
-- @return boolean: True if the genre is valid, false otherwise
local function normalizeGenre(input)
local lowerInput = input:lower() -- Convert input to lowercase for case-insensitive comparison
for standard, aliases in pairs(genres) do
if lowerInput == standard:lower() or table.contains(aliases, lowerInput) then
return standard, true
end
end
return input, false -- Return original input if no match is found, plus flag indicating no match
end
-- Main function to handle channel information display
-- @param frame table: The frame object passed from the template
-- @return string: The formatted table row or an error message
function p.channel(frame)
-- Get template arguments
local args = require('Module:Arguments').getArgs(frame, {wrappers = 'Template:XVideosRedList'})
-- Check if channel parameter is provided
if not args.channel or args.channel == '' then
return formatError(i18n.errors.channelIsEmpty)
end
-- Get channel data
local channel, error = getChannel(args.channel)
if error then
return formatError(error)
elseif channel then
-- Format channel data, prioritizing template arguments over module data
local alias = args.alias or channel.alias or 'N/A'
-- Fix for empty notes field
local notes = args.notes or channel.notes or 'N/A'
if notes == "" then
notes = 'N/A'
end
-- Normalize and process genre with icon
local inputGenre = args.genre or channel.genre or ''
if inputGenre == '' then
return formatError(i18n.errors.noGenreProvided)
end
local normalizedGenre, isValidGenre = normalizeGenre(inputGenre)
local genreOutput
if isValidGenre and iconMap[normalizedGenre] then
genreOutput = string.format('[[File:%s|13px|link=]] %s', iconMap[normalizedGenre], normalizedGenre)
else
-- If genre is invalid, display error in the genre cell
genreOutput = string.format('<span style="color:red;font-weight:bold;">%s</span>',
string.format(i18n.errors.invalidGenre, inputGenre))
-- Still log for debugging
mw.log(string.format(i18n.errors.invalidGenre, inputGenre))
end
-- Handle channel URL
local channelUrl = ""
local channelDisplay = channel.name
if channel.channel and channel.channel ~= '' then
channelUrl = BASE_URL .. channel.channel .. URL_SUFFIX
channelDisplay = string.format('[%s %s]', channelUrl, channel.name)
end
-- Format row with gathered data
local result = string.format(
'|-\n| style="text-align:center;vertical-align:middle;" | %s\n| style="text-align:center;vertical-align:middle;" | %s\n| style="text-align:center;vertical-align:middle;" | %s\n| style="text-align:center;vertical-align:middle;" | %s\n| style="display:none;" |\n',
channelDisplay,
genreOutput,
alias,
notes
)
-- Add standard category for all entries in main namespace
local ns = mw.title.getCurrentTitle().namespace
if ns == 0 then
result = result .. '[[Category:' .. DEFAULT_CATEGORY .. ']]'
-- Add tracking category for main namespace
result = result .. '[[Category:Articles using XVideosRedList]]'
elseif ns == 2 then
result = result .. '[[Category:User pages using XVideosRedList]]'
elseif ns == 118 then
result = result .. '[[Category:Draft pages using XVideosRedList]]'
end
return result
else
return formatError(string.format(i18n.errors.channelNotFound, args.channel))
end
end
-- Function to find and retrieve channel data from appropriate module
-- @param name string: The name of the channel to find
-- @return table|string: The channel data table or an error message
function getChannel(name)
if not name then return nil, i18n.errors.channelIsEmpty end
local modulesToTry = {}
local firstLetter = name:sub(1, 1):upper()
table.insert(modulesToTry, firstLetter)
-- Handle "The" prefix
local nameWithoutThe = name:lower():gsub("^the%s*", ""):gsub("%s*", "")
if nameWithoutThe ~= name:lower() then
local theFirstLetter = nameWithoutThe:sub(1, 1):upper()
if theFirstLetter ~= firstLetter then
table.insert(modulesToTry, theFirstLetter)
end
end
for _, module in ipairs(modulesToTry) do
local success, channelModule = pcall(require, 'Module:XVideosRedList/' .. module)
if success then
if type(channelModule) == "table" and type(channelModule.channels) == "table" then
local matchedChannels = {}
-- First try direct match or aliases
for _, channel in pairs(channelModule.channels) do
if channel.name:lower() == name:lower() or
(channel.aliases and table.contains(channel.aliases, name:lower())) then
table.insert(matchedChannels, channel)
end
end
-- If found matches, return results
if #matchedChannels > 1 then
return nil, string.format(i18n.errors.duplicateChannels, name)
elseif #matchedChannels == 1 then
return matchedChannels[1]
end
-- If no direct matches found, try redirects
if channelModule.redirects and channelModule.redirects[name:lower()] then
return getChannel(channelModule.redirects[name:lower()])
end
else
mw.log(string.format(i18n.errors.invalidModuleStructure, module))
return nil, string.format(i18n.errors.invalidModuleStructure, module)
end
else
mw.log(string.format(i18n.errors.moduleLoadError, module))
end
end
return nil, string.format(i18n.errors.channelNotFound, name)
end
-- Function to generate the table header with XVideos RED logo link
-- @return string: The formatted table header
function p.tableHeader()
local headerLink = string.format('[[File:Xvideosredlogo.svg|24px|link=]] [%s XVideos RED]', HEADER_URL)
return string.format('! colspan="5" | %s\n|-\n!style="width: 30%%; text-align: center; background-color:#E7E7E7;" |Studio name\n!style="width: 20%%; text-align: center; background-color:#E7E7E7;" |Genre\n!style="width: 25%%; text-align: center; background-color:#E7E7E7;" |Alias\n!style="width: 25%%; text-align: center; background-color:#E7E7E7;" |Notes\n!style="display:none;" |\n', headerLink)
end
return p