Module:XVideosRedList
Jump to navigation
Jump to search
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"
-- Tab suffix added after the main suffix
local TAB_SUFFIX = "#_tabRed"
-- 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"
-- Genre categories mapping
local GENRE_CATEGORIES = {
["Gay"] = "XVideos RED (gay) models",
["Straight"] = "XVideos RED (heterosexual) models",
["Bisexual"] = "XVideos RED (bisexual) models",
["Transexual"] = "XVideos RED (transsexual) models"
}
-- Genre display order priority
local GENRE_PRIORITY = {
["Gay"] = 1,
["Straight"] = 2,
["Bisexual"] = 3,
["Transexual"] = 4
}
-- 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)
if not input or input == '' then
return nil, false
end
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
-- Function to format multiple genres with proper icons and formatting
-- @param genreList table: List of normalized genre names
-- @return string: Formatted genres with icons and proper separators
local function formatGenres(genreList)
if #genreList == 0 then
return '<span style="color:red;font-weight:bold;">' .. i18n.errors.noGenreProvided .. '</span>'
end
-- Sort genres according to priority order
table.sort(genreList, function(a, b)
return (GENRE_PRIORITY[a] or 99) < (GENRE_PRIORITY[b] or 99)
end)
local formattedGenres = {}
for _, genre in ipairs(genreList) do
if iconMap[genre] then
table.insert(formattedGenres, string.format('[[File:%s|13px|link=]] %s', iconMap[genre], genre))
else
table.insert(formattedGenres, genre)
end
end
-- Format with appropriate separators based on the number of genres
if #formattedGenres == 1 then
return formattedGenres[1]
elseif #formattedGenres == 2 then
return formattedGenres[1] .. ' & ' .. formattedGenres[2]
else
local result = ""
for i = 1, #formattedGenres - 1 do
if i == #formattedGenres - 1 then
result = result .. formattedGenres[i] .. ' & '
else
result = result .. formattedGenres[i] .. ', '
end
end
return result .. formattedGenres[#formattedGenres]
end
end
-- Function to format country flag using frame:expandTemplate
-- @param country string: The country code or name
-- @param frame table: The current frame object for template expansion
-- @return string: Formatted country flag or empty string if country is nil or empty
local function formatCountryFlag(country, frame)
if not country or country == "" then
return ""
end
-- Use frame:expandTemplate to process the flagicon template
local flagTemplate = frame:expandTemplate{
title = 'flagicon',
args = { country }
}
return flagTemplate .. ' '
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
-- Process multiple genres
local genreList = {}
local invalidGenres = {}
-- Process primary genre
local primaryGenre = args.genre or channel.genre or ''
local normalizedGenre, isValid = normalizeGenre(primaryGenre)
if normalizedGenre then
table.insert(genreList, normalizedGenre)
if not isValid then
table.insert(invalidGenres, primaryGenre)
end
end
-- Process additional genres (genre2, genre3, genre4)
for i = 2, 4 do
local genreArg = args['genre'..i]
if genreArg and genreArg ~= '' then
local normalized, valid = normalizeGenre(genreArg)
if normalized and not table.contains(genreList, normalized) then
table.insert(genreList, normalized)
if not valid then
table.insert(invalidGenres, genreArg)
end
end
end
end
-- Check if at least one valid genre was provided
if #genreList == 0 then
return formatError(i18n.errors.noGenreProvided)
end
-- Format genres with appropriate icons and separators
local genreOutput = formatGenres(genreList)
-- Log any invalid genres for debugging
for _, invalidGenre in ipairs(invalidGenres) do
mw.log(string.format(i18n.errors.invalidGenre, invalidGenre))
end
-- Handle channel URL and country flag
local channelUrl = ""
local channelDisplay = channel.name
local countryFlag = formatCountryFlag(channel.country, frame)
if channel.channel and channel.channel ~= '' then
channelUrl = BASE_URL .. channel.channel .. URL_SUFFIX .. TAB_SUFFIX
channelDisplay = string.format('%s[%s %s]', countryFlag, 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 categories based on namespace and genres
local ns = mw.title.getCurrentTitle().namespace
if ns == 0 then
-- Add default category for all entries
result = result .. '[[Category:' .. DEFAULT_CATEGORY .. ']]'
-- Add genre-specific categories
for _, genre in ipairs(genreList) do
if GENRE_CATEGORIES[genre] then
result = result .. '[[Category:' .. GENRE_CATEGORIES[genre] .. ']]'
end
end
-- 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