Merge development branch into main - resolved conflict in ncaa_fb_managers.py
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 11 KiB |
BIN
assets/broadcast_logos/paramount-plus.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/broadcast_logos/prime.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 4.3 KiB |
@@ -1,404 +1,53 @@
|
||||
NCAAF
|
||||
AAMU => Alabama A&M Bulldogs
|
||||
ACU => Abilene Christian Wildcats
|
||||
ADA => Adams State Grizzlies
|
||||
ADR => Adrian Bulldogs
|
||||
AFA => Air Force Falcons
|
||||
AIC => American International Yellow Jackets
|
||||
AKR => Akron Zips
|
||||
ALA => Alabama Crimson Tide
|
||||
ALB => Albright Lions
|
||||
ALBS => Albany State (GA) Golden Rams
|
||||
ALCN => Alcorn State Braves
|
||||
ALD => Alderson Broaddus Battlers
|
||||
ALF => Alfred Saxons
|
||||
ALL => Allegheny Gators
|
||||
ALST => Alabama State Hornets
|
||||
AMH => Amherst College Mammoths
|
||||
AND => Anderson (IN) Ravens
|
||||
ANG => Angelo State Rams
|
||||
AMH => Amherst Mammoths
|
||||
ANN => Anna Maria College Amcats
|
||||
APP => Appalachian State Mountaineers
|
||||
APSU => Austin Peay Governors
|
||||
ARIZ => Arizona Wildcats
|
||||
ARK => Arkansas-Monticello Boll Weevils
|
||||
ARMY => Army Black Knights
|
||||
ARST => Arkansas State Red Wolves
|
||||
ASH => Ashland Eagles
|
||||
ASP => Assumption Greyhounds
|
||||
ARK => Arkansas Razorbacks
|
||||
ASU => Arizona State Sun Devils
|
||||
AUB => Auburn Tigers
|
||||
AUG => St. Augustine's Falcons
|
||||
AUR => Aurora Spartans
|
||||
AUS => Austin College 'Roos
|
||||
AVE => Averett Cougars
|
||||
AVI => Avila College Eagles
|
||||
AZU => Azusa Pacific Cougars
|
||||
BAK => Baker University Wildcats
|
||||
BAL => Baldwin Wallace Yellow Jackets
|
||||
BALL => Ball State Cardinals
|
||||
BAT => Bates College Bobcats
|
||||
BAY => Baylor Bears
|
||||
BC => Boston College Eagles
|
||||
BEC => Becker College Hawks
|
||||
BEL => Beloit College Buccaneers
|
||||
BEN => Benedictine University (IL) Eagles
|
||||
BENT => Bentley Falcons
|
||||
BET => Bethel (TN) Wildcats
|
||||
BGSU => Bowling Green Falcons
|
||||
BHS => Black Hills State Yellow Jackets
|
||||
BIR => Birmingham-Southern Panthers
|
||||
BKN => Bacone College Warriors
|
||||
BLA => Blackburn Beavers
|
||||
BLOM => Bloomsburg Huskies
|
||||
BLU => Bluffton Beavers
|
||||
BOW => Bowdoin Polar Bears
|
||||
BRI => British Columbia Thunderbirds
|
||||
BRWN => Brown Bears
|
||||
BST => Bemidji State Beavers
|
||||
BSU => Bowie State Bulldogs
|
||||
BUCK => Bucknell Bison
|
||||
BUE => Buena Vista Beavers
|
||||
BUF => Buffalo State Bengals
|
||||
BUFF => Buffalo Bulls
|
||||
BUT => Butler Bulldogs
|
||||
BYU => BYU Cougars
|
||||
CAL => California Lutheran Kingsmen
|
||||
CAM => Campbell Fighting Camels
|
||||
CAP => Capital University Crusaders
|
||||
CAR => Carthage College Red Men
|
||||
CARK => Central Arkansas Bears
|
||||
CAS => Castleton Spartans
|
||||
CAT => Catholic University Cardinals
|
||||
CCSU => Central Connecticut Blue Devils
|
||||
CCU => Coastal Carolina Chanticleers
|
||||
CEN => Centre College Colonels
|
||||
CHA => Chapman University Panthers
|
||||
CHI => Chicago Maroons
|
||||
CHSO => Charleston Southern Buccaneers
|
||||
CIN => Cincinnati Bearcats
|
||||
CLA => Clarion Golden Eagles
|
||||
CLEM => Clemson Tigers
|
||||
CLMB => Columbia Lions
|
||||
CLT => Charlotte 49ers
|
||||
CMU => Central Michigan Chippewas
|
||||
COE => Coe College Kohawks
|
||||
COL => Colorado School of Mines Orediggers
|
||||
COLC => Colorado College Tigers
|
||||
COLG => Colgate Raiders
|
||||
BOIS => Boise State Broncos
|
||||
BRST => Bridgewater State Bears
|
||||
BUENA => Buena Vista Beavers
|
||||
CAL => California Golden Bears
|
||||
CAR => Carroll University (WI) Pioneers
|
||||
CLA => Claremont-Mudd-Scripps College Stags
|
||||
COLBY => Colby College White Mules
|
||||
COLO => Colorado Buffaloes
|
||||
CON => Concordia-Minnesota Cobbers
|
||||
COR => Cornell College (IA) Rams
|
||||
CONN => UConn Huskies
|
||||
CP => Cal Poly Mustangs
|
||||
CRO => Crown Storm
|
||||
CSU => Colorado State Rams
|
||||
CUL => Culver-Stockton Wildcats
|
||||
CUM => Cumberland College Indians
|
||||
CUR => Curry College Colonels
|
||||
DAK => Dakota Wesleyan Tigers
|
||||
DART => Dartmouth Big Green
|
||||
DAV => Davidson Wildcats
|
||||
DAY => Dayton Flyers
|
||||
DEF => Defiance Yellow Jackets
|
||||
DEL => Delta State Statesmen
|
||||
DEN => Denison Big Red
|
||||
DEP => DePauw Tigers
|
||||
DIC => Dickinson State Blue Hawks
|
||||
DRKE => Drake Bulldogs
|
||||
DSU => Delaware State Hornets
|
||||
DEL => Delaware Blue Hens
|
||||
DUB => Dubuque Spartans
|
||||
DUKE => Duke Blue Devils
|
||||
DUQ => Duquesne Dukes
|
||||
EAS => Eastern New Mexico Greyhounds
|
||||
ECU => East Carolina Pirates
|
||||
EDI => Edinboro Fighting Scots
|
||||
EIU => Eastern Illinois Panthers
|
||||
EKU => Eastern Kentucky Colonels
|
||||
ELI => Elizabeth City State Vikings
|
||||
ELM => Elmhurst Blue Jays
|
||||
ELON => Elon Phoenix
|
||||
EMO => Emory & Henry Wasps
|
||||
EMP => Emporia State Hornets
|
||||
EMU => Eastern Michigan Eagles
|
||||
END => Endicott College Gulls
|
||||
EOR => Eastern Oregon Mountaineers
|
||||
ETSU => East Tennessee State Buccaneers
|
||||
EUR => Eureka College Red Devils
|
||||
EWU => Eastern Washington Eagles
|
||||
FAU => Florida Atlantic Owls
|
||||
FAY => Fayetteville State Broncos
|
||||
FDU => FDU-Florham Devils
|
||||
FER => Ferrum Panthers
|
||||
FIN => Findlay Oilers
|
||||
FIT => Fitchburg State Falcons
|
||||
FIU => Florida International Panthers
|
||||
ELM => Elmhurst Bluejays
|
||||
FAMU => Florida A&M Rattlers
|
||||
FLA => Florida Gators
|
||||
FOR => Fort Valley State Wildcats
|
||||
FRA => Franklin Grizzlies
|
||||
FRES => Fresno State Bulldogs
|
||||
FRO => Frostburg State Bobcats
|
||||
FRST => Ferris State Bulldogs
|
||||
FSU => Florida State Seminoles
|
||||
FTLW => Fort Lewis Skyhawks
|
||||
FUR => Furman Paladins
|
||||
GAL => Gallaudet Bison
|
||||
GAN => Gannon Golden Knights
|
||||
GASO => Georgia Southern Eagles
|
||||
GAST => Georgia State Panthers
|
||||
GEN => Geneva College Golden Tornadoes
|
||||
GEO => George Fox University Bruins
|
||||
GET => Gettysburg Bullets
|
||||
GLE => Glenville State Pioneers
|
||||
GMU => George Mason Patriots
|
||||
GRA => Grand Valley State Lakers
|
||||
GRE => Greenville Panthers
|
||||
GRI => Grinnell Pioneers
|
||||
GRO => Grove City College Wolverines
|
||||
GT => Georgia Tech Yellow Jackets
|
||||
GUI => Guilford Quakers
|
||||
GWEB => Gardner-Webb Bulldogs
|
||||
HAM => Hampden-Sydney Tigers
|
||||
HAMP => Hampton Pirates
|
||||
HAN => Hanover Panthers
|
||||
HAR => Hartwick Hawks
|
||||
HARV => Harvard Crimson
|
||||
HAS => Haskell Indian Nations Jayhawks
|
||||
GTWN => Georgetown Hoyas
|
||||
HAW => Hawai'i Rainbow Warriors
|
||||
HBU => Houston Baptist Huskies
|
||||
HC => Holy Cross Crusaders
|
||||
HEI => Heidelberg Student Princes
|
||||
HEN => Hendrix College Warriors
|
||||
HIL => Hillsdale Chargers
|
||||
HIR => Hiram College Terriers
|
||||
HOB => Hobart Statesmen
|
||||
HOU => Houston Cougars
|
||||
HOW => Howard Bison
|
||||
HUS => Husson Eagles
|
||||
IDHO => Idaho Vandals
|
||||
IDST => Idaho State Bengals
|
||||
ILL => Illinois Fighting Illini
|
||||
ILST => Illinois State Redbirds
|
||||
ILW => Illinois Wesleyan Titans
|
||||
IND => Indianapolis
|
||||
INST => Indiana State Sycamores
|
||||
IOW => Iowa Wesleyan Tigers
|
||||
IOWA => Iowa Hawkeyes
|
||||
ISU => Iowa State Cyclones
|
||||
ITH => Ithaca Bombers
|
||||
IU => Indiana Hoosiers
|
||||
JKST => Jackson State Tigers
|
||||
JMU => James Madison Dukes
|
||||
JOH => Johnson C Smith Golden Bulls
|
||||
JUN => Juniata Eagles
|
||||
JVST => Jacksonville State Gamecocks
|
||||
KAL => Kalamazoo Hornets
|
||||
KAN => Kansas Wesleyan University Coyotes
|
||||
KEN => Kenyon Lords
|
||||
KENN => Kennesaw State Owls
|
||||
KENT => Kent State Golden Flashes
|
||||
KIN => King's College (PA) Monarchs
|
||||
KNO => Knox College Prairie Fire
|
||||
KSU => Kansas State Wildcats
|
||||
KU => Kansas Jayhawks
|
||||
KUT => Kutztown Golden Bears
|
||||
KYST => Kentucky State Thorobreds
|
||||
KYW => Kentucky Wesleyan Panthers
|
||||
LA => La Verne Leopards
|
||||
LAC => Lane Dragons
|
||||
LAF => Lafayette Leopards
|
||||
LAG => LaGrange College Panthers
|
||||
LAK => Lake Forest Foresters
|
||||
LAM => Lambuth Eagles
|
||||
LAN => Langston Lions
|
||||
LAW => Lawrence Vikings
|
||||
LEB => Lebanon Valley Flying Dutchmen
|
||||
LEH => Lehigh Mountain Hawks
|
||||
LEN => Lenoir-Rhyne Bears
|
||||
LEW => Lewis & Clark Pioneers
|
||||
LIB => Liberty Flames
|
||||
LIM => Limestone Saints
|
||||
LIN => Linfield Wildcats
|
||||
LOC => Lock Haven Bald Eagles
|
||||
LOR => Loras College Duhawks
|
||||
LOU => Louisville Cardinals
|
||||
LSU => LSU Tigers
|
||||
LT => Louisiana Tech Bulldogs
|
||||
JXST => Jacksonville State Gamecocks
|
||||
LUT => Luther Norse
|
||||
LYC => Lycoming Warriors
|
||||
M-OH => Miami (OH) RedHawks
|
||||
MAC => Macalester Scots
|
||||
MAI => Maine Maritime Mariners
|
||||
MAN => Mansfield Mountaineers
|
||||
MAR => Maryville College Fighting Scots
|
||||
MAS => Mass Maritime Buccaneers
|
||||
MASS => UMass Minutemen
|
||||
MAY => Mayville State Comets
|
||||
MCM => McMurry War Hawks
|
||||
MCN => McNeese Cowboys
|
||||
MD => Maryland Terrapins
|
||||
MEM => Memphis Tigers
|
||||
MEN => Menlo College Oaks
|
||||
MER => Merchant Marine Mariners
|
||||
MERC => Mercyhurst Lakers
|
||||
MES => Colorado Mesa Mavericks
|
||||
MET => Methodist Monarchs
|
||||
MH => Mars Hill Mountain Lions
|
||||
MIAMI => Miami Hurricanes
|
||||
MICH => Michigan Wolverines
|
||||
MID => Midwestern State Mustangs
|
||||
MIL => Millsaps Majors
|
||||
MIN => Minot State Beavers
|
||||
MINN => Minnesota Golden Gophers
|
||||
MIS => Missouri Western Griffons
|
||||
MISS => Ole Miss Rebels
|
||||
MIZ => Missouri Tigers
|
||||
MNST => Minnesota State Mavericks
|
||||
MONM => Monmouth Hawks
|
||||
MONT => Montana Grizzlies
|
||||
MOR => Morningside Chiefs
|
||||
MORE => Morehead State Eagles
|
||||
MORG => Morgan State Bears
|
||||
MOU => Mount Union Raiders
|
||||
MRSH => Marshall Thundering Herd
|
||||
MRST => Marist Red Foxes
|
||||
MSST => Mississippi State Bulldogs
|
||||
MSU => Michigan State Spartans
|
||||
MTST => Montana State Bobcats
|
||||
MTSU => Middle Tennessee Blue Raiders
|
||||
MTU => Michigan Tech Huskies
|
||||
MUH => Muhlenberg Mules
|
||||
MUR => Murray State Racers
|
||||
MUS => Muskingum Fighting Muskies
|
||||
MVSU => Mississippi Valley State Delta Devils
|
||||
NAU => Northern Arizona Lumberjacks
|
||||
NAVY => Navy Midshipmen
|
||||
NBY => Newberry Wolves
|
||||
NCAT => North Carolina A&T Aggies
|
||||
NCCU => North Carolina Central Eagles
|
||||
NCST => NC State Wolfpack
|
||||
ND => Notre Dame Fighting Irish
|
||||
NDOH => Notre Dame College Falcons
|
||||
NDSU => North Dakota State Bison
|
||||
NEB => Nebraska-Kearney Lopers
|
||||
NEV => Nevada Wolf Pack
|
||||
NH => New Haven Chargers
|
||||
NICH => Nicholls Colonels
|
||||
NIU => Northern Illinois Huskies
|
||||
NMH => New Mexico Highlands Cowboys
|
||||
NMI => Northern Michigan Wildcats
|
||||
NMSU => New Mexico State Aggies
|
||||
NOR => Univ. of Northwestern-St. Paul Eagles
|
||||
NORF => Norfolk State Spartans
|
||||
NW => Northwestern Wildcats
|
||||
OBE => Oberlin Yeomen
|
||||
ODU => Old Dominion Monarchs
|
||||
OHI => Ohio Northern Polar Bears
|
||||
OHIO => Ohio Bobcats
|
||||
OKL => Oklahoma Baptist Bison
|
||||
OKST => Oklahoma State Cowboys
|
||||
OLI => Olivet College Comets
|
||||
OMA => Omaha Mavericks
|
||||
ORST => Oregon State Beavers
|
||||
OSU => Ohio State Buckeyes
|
||||
OTT => Otterbein Cardinals
|
||||
OU => Oklahoma Sooners
|
||||
PAC => Pacific (OR) Boxers
|
||||
PENN => Pennsylvania Quakers
|
||||
PIKE => Pikeville Bears
|
||||
PITT => Pittsburgh Panthers
|
||||
PRE => Presentation College Saints
|
||||
PRI => Principia College Panthers
|
||||
PRIN => Princeton Tigers
|
||||
PST => Pittsburg State Gorillas
|
||||
PSU => Penn State Nittany Lions
|
||||
MESA => Colorado Mesa Mavericks
|
||||
MIL => Millikin Big Blue
|
||||
MOR => Morehouse College Maroon Tigers
|
||||
NOR => North Park Vikings
|
||||
RED => Redlands Bulldogs
|
||||
RICE => Rice Owls
|
||||
RICH => Richmond Spiders
|
||||
RIT => Rochester Yellow Jackets
|
||||
ROB => Robert Morris (IL) Eagles
|
||||
ROS => Rose-Hulman Engineers
|
||||
RUTG => Rutgers Scarlet Knights
|
||||
SAC => Sacramento State Hornets
|
||||
SAG => Saginaw Valley Cardinals
|
||||
SDAK => South Dakota Coyotes
|
||||
SDSU => San Diego State Aztecs
|
||||
SET => Seton Hill Griffins
|
||||
SIU => Southern Illinois Salukis
|
||||
SJSU => San José State Spartans
|
||||
SLI => Slippery Rock The Rock
|
||||
SOU => Southwestern College Moundbuilders
|
||||
SPR => Springfield College Pride
|
||||
ST => St. Scholastica Saints
|
||||
STAN => Stanford Cardinal
|
||||
STE => Stevenson University Mustangs
|
||||
STET => Stetson Hatters
|
||||
STO => Stonehill College Skyhawks
|
||||
SUS => Susquehanna University River Hawks
|
||||
SUU => Southern Utah Thunderbirds
|
||||
SYR => Syracuse Orange
|
||||
TA&M => Texas A&M Aggies
|
||||
TAY => Taylor Trojans
|
||||
TEM => Temple Owls
|
||||
TEX => Texas Longhorns
|
||||
TIF => Tiffin University Dragons
|
||||
TLSA => Tulsa Golden Hurricane
|
||||
TRI => Trinity University (TX) Tigers
|
||||
TUF => Tufts University Jumbos
|
||||
TXST => Texas State Bobcats
|
||||
UAB => UAB Blazers
|
||||
UAPB => Arkansas-Pine Bluff Golden Lions
|
||||
UCD => UC Davis Aggies
|
||||
UCF => UCF Knights
|
||||
UCLA => UCLA Bruins
|
||||
UCONN => UConn Huskies
|
||||
UGA => Georgia Bulldogs
|
||||
UK => Kentucky Wildcats
|
||||
UL => Louisiana Ragin' Cajuns
|
||||
ULM => UL Monroe Warhawks
|
||||
UMD => Minnesota-Duluth Bulldogs
|
||||
UMDA => UMASS Dartmouth Corsairs
|
||||
UML => UMass Lowell River Hawks
|
||||
UNA => North Alabama Lions
|
||||
UNC => North Carolina Tar Heels
|
||||
UNCO => Northern Colorado Bears
|
||||
UND => North Dakota Fighting Hawks
|
||||
UNH => New Hampshire Wildcats
|
||||
UNI => University of Mary Marauders
|
||||
UNLV => UNLV Rebels
|
||||
UNM => New Mexico Lobos
|
||||
UNNY => Union Dutchmen
|
||||
UNT => North Texas Mean Green
|
||||
UPP => Upper Iowa Peacocks
|
||||
URI => Rhode Island Rams
|
||||
USA => South Alabama Jaguars
|
||||
USC => USC Trojans
|
||||
USD => San Diego Toreros
|
||||
USF => South Florida Bulls
|
||||
USU => Utah State Aggies
|
||||
UTAH => Utah Utes
|
||||
UTC => Chattanooga Mocs
|
||||
UTI => Utica College Pioneers
|
||||
UVA => Virginia Cavaliers
|
||||
VAL => Valley City State Vikings
|
||||
VAN => Vanderbilt Commodores
|
||||
VILL => Villanova Wildcats
|
||||
VIR => Virginia State Trojans
|
||||
VT => Virginia Tech Hokies
|
||||
WAB => Wabash College Little Giants
|
||||
WAKE => Wake Forest Demon Deacons
|
||||
WAS => Washington-Missouri Bears
|
||||
WASH => Washington Huskies
|
||||
WAY => Wayne State (MI) Warriors
|
||||
WES => Westminster College (MO) Blue Jays
|
||||
WHE => Wheaton College Illinois Thunder
|
||||
WIL => Wilkes University Colonels
|
||||
WIN => Wingate Bulldogs
|
||||
WIS => Wisconsin-Platteville Pioneers
|
||||
WISC => Wisconsin Badgers
|
||||
WKU => Western Kentucky Hilltoppers
|
||||
WOR => Worcester State College Lancers
|
||||
WSU => Washington State Cougars
|
||||
WVU => West Virginia Mountaineers
|
||||
YALE => Yale Bulldogs
|
||||
|
||||
NBA
|
||||
@@ -1106,6 +755,149 @@ MLB Conferences/Divisions
|
||||
OAK => Oakland Athletics
|
||||
SEA => Seattle Mariners
|
||||
TEX => Texas Rangers
|
||||
Soccer - Premier League (England)
|
||||
ARS => Arsenal
|
||||
AVL => Aston Villa
|
||||
BHA => Brighton & Hove Albion
|
||||
BOU => AFC Bournemouth
|
||||
BRE => Brentford
|
||||
BUR => Burnley
|
||||
CHE => Chelsea
|
||||
CRY => Crystal Palace
|
||||
EVE => Everton
|
||||
FUL => Fulham
|
||||
LIV => Liverpool
|
||||
LUT => Luton Town
|
||||
MCI => Manchester City
|
||||
MUN => Manchester United
|
||||
NEW => Newcastle United
|
||||
NFO => Nottingham Forest
|
||||
SHU => Sheffield United
|
||||
TOT => Tottenham Hotspur
|
||||
WHU => West Ham United
|
||||
WOL => Wolverhampton Wanderers
|
||||
|
||||
Soccer - La Liga (Spain)
|
||||
ALA => Alavés
|
||||
ATH => Athletic Bilbao
|
||||
ATM => Atlético Madrid
|
||||
BAR => Barcelona
|
||||
BET => Real Betis
|
||||
CAG => Cagliari
|
||||
CEL => Celta Vigo
|
||||
ESP => Espanyol
|
||||
GET => Getafe
|
||||
GIR => Girona
|
||||
LAZ => Lazio
|
||||
LEG => Leganés
|
||||
RAY => Rayo Vallecano
|
||||
RMA => Real Madrid
|
||||
SEV => Sevilla
|
||||
VAL => Valencia
|
||||
VLD => Valladolid
|
||||
|
||||
Soccer - Bundesliga (Germany)
|
||||
BOC => VfL Bochum
|
||||
BOL => VfL Bochum
|
||||
DOR => Borussia Dortmund
|
||||
FCA => FC Augsburg
|
||||
FCB => Bayern Munich
|
||||
FCU => FC Union Berlin
|
||||
HAC => Hannover 96
|
||||
HDH => Hertha BSC
|
||||
KOL => 1. FC Köln
|
||||
LEV => Bayer Leverkusen
|
||||
M05 => Mainz 05
|
||||
RBL => RB Leipzig
|
||||
SCF => SC Freiburg
|
||||
SGE => Eintracht Frankfurt
|
||||
STU => VfB Stuttgart
|
||||
SVW => Werder Bremen
|
||||
TSG => TSG Hoffenheim
|
||||
WOB => VfL Wolfsburg
|
||||
|
||||
Soccer - Serie A (Italy)
|
||||
ATA => Atalanta
|
||||
CAG => Cagliari
|
||||
EMP => Empoli
|
||||
FIO => Fiorentina
|
||||
INT => Inter Milan
|
||||
JUV => Juventus
|
||||
LAZ => Lazio
|
||||
MIL => AC Milan
|
||||
MON => Monza
|
||||
NAP => Napoli
|
||||
ROM => Roma
|
||||
TOR => Torino
|
||||
UDI => Udinese
|
||||
VER => Hellas Verona
|
||||
|
||||
Soccer - Ligue 1 (France)
|
||||
LIL => Lille
|
||||
LPM => Lille
|
||||
LYON => Lyon
|
||||
MAR => Marseille
|
||||
MON => Monaco
|
||||
NAN => Nantes
|
||||
NICE => Nice
|
||||
OL => Olympique Lyonnais
|
||||
OM => Olympique de Marseille
|
||||
PAR => Paris Saint-Germain
|
||||
PSG => Paris Saint-Germain
|
||||
REN => Rennes
|
||||
STR => Strasbourg
|
||||
|
||||
Soccer - Champions League
|
||||
AJA => Ajax
|
||||
ASM => AS Monaco
|
||||
ASS => AS Saint-Étienne
|
||||
BOC => VfL Bochum
|
||||
CEL => Celtic
|
||||
COM => Club Brugge
|
||||
FCA => FC Augsburg
|
||||
FCB => Bayern Munich
|
||||
FCU => FC Union Berlin
|
||||
FIO => Fiorentina
|
||||
GEN => Genoa
|
||||
HAC => Hannover 96
|
||||
IPS => Ipswich Town
|
||||
KSV => Kaiserslautern
|
||||
LEC => Lecce
|
||||
LIL => Lille
|
||||
LIV => Liverpool
|
||||
M05 => Mainz 05
|
||||
MCI => Manchester City
|
||||
MUN => Manchester United
|
||||
NAN => Nantes
|
||||
OSA => Osasuna
|
||||
RBL => RB Leipzig
|
||||
RCL => RC Lens
|
||||
RMA => Real Madrid
|
||||
SCF => SC Freiburg
|
||||
SGE => Eintracht Frankfurt
|
||||
SR => Sporting CP
|
||||
STP => St. Pauli
|
||||
SVW => Werder Bremen
|
||||
TFC => Toulouse FC
|
||||
TOT => Tottenham Hotspur
|
||||
TSG => TSG Hoffenheim
|
||||
UDI => Udinese
|
||||
VEN => Venezia
|
||||
VFB => VfB Stuttgart
|
||||
VIL => Villarreal
|
||||
|
||||
Soccer - Other Teams
|
||||
austin => Austin FC
|
||||
cf_montral => CF Montréal
|
||||
charlotte => Charlotte FC
|
||||
dortmund => Borussia Dortmund
|
||||
gladbach => Borussia Mönchengladbach
|
||||
lafc => Los Angeles FC
|
||||
leverkusen => Bayer Leverkusen
|
||||
nycfc => New York City FC
|
||||
paris_sg => Paris Saint-Germain
|
||||
st_louis => St. Louis City SC
|
||||
|
||||
MLS Conferences/Divisions
|
||||
Conferences currently unsupported
|
||||
|
||||
|
||||
BIN
assets/sports/mlb_logos/mlb.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 20 KiB |
BIN
assets/sports/nba_logos/nba.png
Normal file
|
After Width: | Height: | Size: 490 B |
BIN
assets/sports/ncaa_fbs_logos/AANDM.png
Normal file
|
After Width: | Height: | Size: 409 B |
BIN
assets/sports/ncaa_fbs_logos/AMH.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
assets/sports/ncaa_fbs_logos/ANN.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 44 KiB |
BIN
assets/sports/ncaa_fbs_logos/ASU.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
BIN
assets/sports/ncaa_fbs_logos/BOIS.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
assets/sports/ncaa_fbs_logos/BRST.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/sports/ncaa_fbs_logos/BUENA.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
BIN
assets/sports/ncaa_fbs_logos/CAR.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/sports/ncaa_fbs_logos/CLA.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
assets/sports/ncaa_fbs_logos/COLBY.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 53 KiB |
BIN
assets/sports/ncaa_fbs_logos/CP.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
assets/sports/ncaa_fbs_logos/CSU.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
assets/sports/ncaa_fbs_logos/CUR.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/sports/ncaa_fbs_logos/DEL.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
assets/sports/ncaa_fbs_logos/DUB.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/sports/ncaa_fbs_logos/ELM.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
assets/sports/ncaa_fbs_logos/FAMU.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/sports/ncaa_fbs_logos/FLA.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 56 KiB |
BIN
assets/sports/ncaa_fbs_logos/GRI.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 23 KiB |
BIN
assets/sports/ncaa_fbs_logos/GTWN.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/sports/ncaa_fbs_logos/HAW.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/sports/ncaa_fbs_logos/HOW.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
assets/sports/ncaa_fbs_logos/IDHO.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 61 KiB |
BIN
assets/sports/ncaa_fbs_logos/JXST.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/sports/ncaa_fbs_logos/LUT.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 24 KiB |
BIN
assets/sports/ncaa_fbs_logos/MESA.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
BIN
assets/sports/ncaa_fbs_logos/MIL.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/sports/ncaa_fbs_logos/MOR.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
assets/sports/ncaa_fbs_logos/NOR.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
assets/sports/ncaa_fbs_logos/RED.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
assets/sports/ncaa_fbs_logos/SAC.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 34 KiB |
BIN
assets/sports/ncaa_fbs_logos/STET.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
assets/sports/ncaa_fbs_logos/TAANDM.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
assets/sports/ncaa_fbs_logos/TAMU.png
Normal file
|
After Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 31 KiB |
BIN
assets/sports/ncaa_fbs_logos/UNT.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
assets/sports/ncaa_fbs_logos/USA.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
BIN
assets/sports/ncaa_fbs_logos/YALE.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/sports/ncaa_fbs_logos/ncaa_fb.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/sports/ncaa_fbs_logos/ncaam.png
Normal file
|
After Width: | Height: | Size: 651 B |
BIN
assets/sports/nfl_logos/nfl.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/sports/nhl_logos/nhl.png
Normal file
|
After Width: | Height: | Size: 430 B |
1
check_team_images.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
"start_time": "07:00",
|
||||
"end_time": "23:00"
|
||||
},
|
||||
"timezone": "America/Chicago",
|
||||
"timezone": "America/New_York",
|
||||
"location": {
|
||||
"city": "Dallas",
|
||||
"state": "Texas",
|
||||
"city": "Tampa",
|
||||
"state": "Florida",
|
||||
"country": "US"
|
||||
},
|
||||
"display": {
|
||||
@@ -39,6 +39,7 @@
|
||||
"daily_forecast": 30,
|
||||
"stock_news": 20,
|
||||
"odds_ticker": 60,
|
||||
"leaderboard": 60,
|
||||
"nhl_live": 30,
|
||||
"nhl_recent": 30,
|
||||
"nhl_upcoming": 30,
|
||||
@@ -81,7 +82,7 @@
|
||||
"update_interval": 1
|
||||
},
|
||||
"weather": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"update_interval": 1800,
|
||||
"units": "imperial",
|
||||
"display_format": "{temp}\u00b0F\n{condition}"
|
||||
@@ -129,11 +130,12 @@
|
||||
"duration_buffer": 0.1
|
||||
},
|
||||
"odds_ticker": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"show_favorite_teams_only": true,
|
||||
"games_per_favorite_team": 1,
|
||||
"max_games_per_league": 5,
|
||||
"show_odds_only": false,
|
||||
"fetch_odds": true,
|
||||
"sort_order": "soonest",
|
||||
"enabled_leagues": [
|
||||
"nfl",
|
||||
@@ -150,6 +152,46 @@
|
||||
"dynamic_duration": true,
|
||||
"min_duration": 30,
|
||||
"max_duration": 300,
|
||||
"duration_buffer": 0.05
|
||||
},
|
||||
"leaderboard": {
|
||||
"enabled": false,
|
||||
"enabled_sports": {
|
||||
"nfl": {
|
||||
"enabled": true,
|
||||
"top_teams": 10
|
||||
},
|
||||
"nba": {
|
||||
"enabled": false,
|
||||
"top_teams": 10
|
||||
},
|
||||
"mlb": {
|
||||
"enabled": false,
|
||||
"top_teams": 10
|
||||
},
|
||||
"ncaa_fb": {
|
||||
"enabled": true,
|
||||
"top_teams": 25,
|
||||
"show_ranking": true
|
||||
},
|
||||
"nhl": {
|
||||
"enabled": false,
|
||||
"top_teams": 10
|
||||
},
|
||||
"ncaam_basketball": {
|
||||
"enabled": false,
|
||||
"top_teams": 25
|
||||
}
|
||||
},
|
||||
"update_interval": 3600,
|
||||
"scroll_speed": 1,
|
||||
"scroll_delay": 0.01,
|
||||
"display_duration": 60,
|
||||
"loop": false,
|
||||
"request_timeout": 30,
|
||||
"dynamic_duration": true,
|
||||
"min_duration": 45,
|
||||
"max_duration": 600,
|
||||
"duration_buffer": 0.1
|
||||
},
|
||||
"calendar": {
|
||||
@@ -257,6 +299,7 @@
|
||||
],
|
||||
"logo_dir": "assets/sports/ncaa_fbs_logos",
|
||||
"show_records": true,
|
||||
"show_ranking": true,
|
||||
"display_modes": {
|
||||
"ncaa_fb_live": true,
|
||||
"ncaa_fb_recent": true ,
|
||||
@@ -366,7 +409,7 @@
|
||||
}
|
||||
},
|
||||
"text_display": {
|
||||
"enabled": false,
|
||||
"enabled": true,
|
||||
"text": "Subscribe to ChuckBuilds",
|
||||
"font_path": "assets/fonts/press-start-2p.ttf",
|
||||
"font_size": 8,
|
||||
|
||||
1
create_league_logos.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
create_ncaa_logos.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
107
debug_of_the_day.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debug script for OfTheDayManager issues
|
||||
Run this on the Raspberry Pi to diagnose the problem
|
||||
|
||||
Usage:
|
||||
1. Copy this file to your Raspberry Pi
|
||||
2. Run: python3 debug_of_the_day.py
|
||||
3. Check the output for any errors or issues
|
||||
|
||||
This script will help identify why the OfTheDayManager is not loading data files.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import date
|
||||
|
||||
def debug_of_the_day():
|
||||
print("=== OfTheDayManager Debug Script ===")
|
||||
print(f"Current working directory: {os.getcwd()}")
|
||||
print(f"Python path: {sys.path}")
|
||||
|
||||
# Check if we're in the right directory
|
||||
if not os.path.exists('config/config.json'):
|
||||
print("ERROR: config/config.json not found. Make sure you're running from the LEDMatrix root directory.")
|
||||
return
|
||||
|
||||
# Load the actual config
|
||||
try:
|
||||
with open('config/config.json', 'r') as f:
|
||||
config = json.load(f)
|
||||
print("✓ Successfully loaded config.json")
|
||||
except Exception as e:
|
||||
print(f"ERROR loading config.json: {e}")
|
||||
return
|
||||
|
||||
# Check of_the_day configuration
|
||||
of_the_day_config = config.get('of_the_day', {})
|
||||
print(f"OfTheDay enabled: {of_the_day_config.get('enabled', False)}")
|
||||
|
||||
if not of_the_day_config.get('enabled', False):
|
||||
print("OfTheDay is disabled in config!")
|
||||
return
|
||||
|
||||
categories = of_the_day_config.get('categories', {})
|
||||
print(f"Categories configured: {list(categories.keys())}")
|
||||
|
||||
# Test each category
|
||||
today = date.today()
|
||||
day_of_year = today.timetuple().tm_yday
|
||||
print(f"Today is day {day_of_year} of the year")
|
||||
|
||||
for category_name, category_config in categories.items():
|
||||
print(f"\n--- Testing category: {category_name} ---")
|
||||
print(f"Category enabled: {category_config.get('enabled', True)}")
|
||||
|
||||
if not category_config.get('enabled', True):
|
||||
print("Category is disabled, skipping...")
|
||||
continue
|
||||
|
||||
data_file = category_config.get('data_file')
|
||||
print(f"Data file: {data_file}")
|
||||
|
||||
# Test path resolution
|
||||
if not os.path.isabs(data_file):
|
||||
if data_file.startswith('of_the_day/'):
|
||||
file_path = os.path.join(os.getcwd(), data_file)
|
||||
else:
|
||||
file_path = os.path.join(os.getcwd(), 'of_the_day', data_file)
|
||||
else:
|
||||
file_path = data_file
|
||||
|
||||
file_path = os.path.abspath(file_path)
|
||||
print(f"Resolved path: {file_path}")
|
||||
print(f"File exists: {os.path.exists(file_path)}")
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
print(f"ERROR: Data file not found at {file_path}")
|
||||
continue
|
||||
|
||||
# Test JSON loading
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
print(f"✓ Successfully loaded JSON with {len(data)} items")
|
||||
|
||||
# Check for today's entry
|
||||
day_key = str(day_of_year)
|
||||
if day_key in data:
|
||||
item = data[day_key]
|
||||
print(f"✓ Found entry for day {day_of_year}: {item.get('title', 'No title')}")
|
||||
else:
|
||||
print(f"✗ No entry found for day {day_of_year}")
|
||||
# Show some nearby entries
|
||||
nearby_days = [k for k in data.keys() if k.isdigit() and abs(int(k) - day_of_year) <= 5]
|
||||
print(f"Nearby days with entries: {sorted(nearby_days)}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR loading JSON: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n=== Debug complete ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
debug_of_the_day()
|
||||
1
list_missing_teams.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
list_soccer_abbreviations.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
657
missing_team_logos.txt
Normal file
@@ -0,0 +1,657 @@
|
||||
================================================================================
|
||||
MISSING TEAM LOGOS - COMPLETE LIST
|
||||
================================================================================
|
||||
Total missing teams: 309
|
||||
|
||||
|
||||
MLB:
|
||||
---
|
||||
OAK => Oakland Athletics
|
||||
|
||||
NCAAF:
|
||||
-----
|
||||
AAMU => Alabama A&M Bulldogs
|
||||
ACU => Abilene Christian Wildcats
|
||||
ADA => Adams State Grizzlies
|
||||
ADR => Adrian Bulldogs
|
||||
AIC => American International Yellow Jackets
|
||||
ALB => Albright Lions
|
||||
ALBS => Albany State (GA) Golden Rams
|
||||
ALCN => Alcorn State Braves
|
||||
ALD => Alderson Broaddus Battlers
|
||||
ALF => Alfred Saxons
|
||||
ALL => Allegheny Gators
|
||||
ALST => Alabama State Hornets
|
||||
AMH => Amherst College Mammoths
|
||||
AND => Anderson (IN) Ravens
|
||||
ANG => Angelo State Rams
|
||||
ANN => Anna Maria College Amcats
|
||||
APSU => Austin Peay Governors
|
||||
ASH => Ashland Eagles
|
||||
ASP => Assumption Greyhounds
|
||||
ASU => Arizona State Sun Devils
|
||||
AUG => St. Augustine's Falcons
|
||||
AUR => Aurora Spartans
|
||||
AUS => Austin College 'Roos
|
||||
AVE => Averett Cougars
|
||||
AVI => Avila College Eagles
|
||||
AZU => Azusa Pacific Cougars
|
||||
BAK => Baker University Wildcats
|
||||
BAL => Baldwin Wallace Yellow Jackets
|
||||
BAT => Bates College Bobcats
|
||||
BEC => Becker College Hawks
|
||||
BEL => Beloit College Buccaneers
|
||||
BEN => Benedictine University (IL) Eagles
|
||||
BENT => Bentley Falcons
|
||||
BET => Bethel (TN) Wildcats
|
||||
BHS => Black Hills State Yellow Jackets
|
||||
BIR => Birmingham-Southern Panthers
|
||||
BKN => Bacone College Warriors
|
||||
BLA => Blackburn Beavers
|
||||
BLOM => Bloomsburg Huskies
|
||||
BLU => Bluffton Beavers
|
||||
BOW => Bowdoin Polar Bears
|
||||
BRI => British Columbia Thunderbirds
|
||||
BRWN => Brown Bears
|
||||
BST => Bemidji State Beavers
|
||||
BUCK => Bucknell Bison
|
||||
BUE => Buena Vista Beavers
|
||||
BUF => Buffalo State Bengals
|
||||
BUT => Butler Bulldogs
|
||||
CAM => Campbell Fighting Camels
|
||||
CAP => Capital University Crusaders
|
||||
CAR => Carthage College Red Men
|
||||
CARK => Central Arkansas Bears
|
||||
CAS => Castleton Spartans
|
||||
CAT => Catholic University Cardinals
|
||||
CCSU => Central Connecticut Blue Devils
|
||||
CEN => Centre College Colonels
|
||||
CHA => Chapman University Panthers
|
||||
CHI => Chicago Maroons
|
||||
CHSO => Charleston Southern Buccaneers
|
||||
CLA => Clarion Golden Eagles
|
||||
CLMB => Columbia Lions
|
||||
COE => Coe College Kohawks
|
||||
COL => Colorado School of Mines Orediggers
|
||||
COLC => Colorado College Tigers
|
||||
COLG => Colgate Raiders
|
||||
CON => Concordia-Minnesota Cobbers
|
||||
COR => Cornell College (IA) Rams
|
||||
CP => Cal Poly Mustangs
|
||||
CRO => Crown Storm
|
||||
CSU => Colorado State Rams
|
||||
CUL => Culver-Stockton Wildcats
|
||||
CUM => Cumberland College Indians
|
||||
CUR => Curry College Colonels
|
||||
DAK => Dakota Wesleyan Tigers
|
||||
DART => Dartmouth Big Green
|
||||
DAV => Davidson Wildcats
|
||||
DAY => Dayton Flyers
|
||||
DEF => Defiance Yellow Jackets
|
||||
DEL => Delta State Statesmen
|
||||
DEN => Denison Big Red
|
||||
DEP => DePauw Tigers
|
||||
DIC => Dickinson State Blue Hawks
|
||||
DRKE => Drake Bulldogs
|
||||
DSU => Delaware State Hornets
|
||||
DUB => Dubuque Spartans
|
||||
DUQ => Duquesne Dukes
|
||||
EAS => Eastern New Mexico Greyhounds
|
||||
EDI => Edinboro Fighting Scots
|
||||
EIU => Eastern Illinois Panthers
|
||||
EKU => Eastern Kentucky Colonels
|
||||
ELI => Elizabeth City State Vikings
|
||||
ELM => Elmhurst Blue Jays
|
||||
ELON => Elon Phoenix
|
||||
EMO => Emory & Henry Wasps
|
||||
EMP => Emporia State Hornets
|
||||
END => Endicott College Gulls
|
||||
EOR => Eastern Oregon Mountaineers
|
||||
ETSU => East Tennessee State Buccaneers
|
||||
EUR => Eureka College Red Devils
|
||||
EWU => Eastern Washington Eagles
|
||||
FAY => Fayetteville State Broncos
|
||||
FDU => FDU-Florham Devils
|
||||
FER => Ferrum Panthers
|
||||
FIN => Findlay Oilers
|
||||
FIT => Fitchburg State Falcons
|
||||
FLA => Florida Gators
|
||||
FOR => Fort Valley State Wildcats
|
||||
FRA => Franklin Grizzlies
|
||||
FRO => Frostburg State Bobcats
|
||||
FRST => Ferris State Bulldogs
|
||||
FTLW => Fort Lewis Skyhawks
|
||||
FUR => Furman Paladins
|
||||
GAL => Gallaudet Bison
|
||||
GAN => Gannon Golden Knights
|
||||
GEN => Geneva College Golden Tornadoes
|
||||
GEO => George Fox University Bruins
|
||||
GET => Gettysburg Bullets
|
||||
GLE => Glenville State Pioneers
|
||||
GMU => George Mason Patriots
|
||||
GRA => Grand Valley State Lakers
|
||||
GRE => Greenville Panthers
|
||||
GRI => Grinnell Pioneers
|
||||
GRO => Grove City College Wolverines
|
||||
GUI => Guilford Quakers
|
||||
GWEB => Gardner-Webb Bulldogs
|
||||
HAM => Hampden-Sydney Tigers
|
||||
HAMP => Hampton Pirates
|
||||
HAN => Hanover Panthers
|
||||
HAR => Hartwick Hawks
|
||||
HARV => Harvard Crimson
|
||||
HAS => Haskell Indian Nations Jayhawks
|
||||
HAW => Hawai'i Rainbow Warriors
|
||||
HBU => Houston Baptist Huskies
|
||||
HC => Holy Cross Crusaders
|
||||
HEI => Heidelberg Student Princes
|
||||
HEN => Hendrix College Warriors
|
||||
HIL => Hillsdale Chargers
|
||||
HIR => Hiram College Terriers
|
||||
HOB => Hobart Statesmen
|
||||
HOW => Howard Bison
|
||||
HUS => Husson Eagles
|
||||
IDHO => Idaho Vandals
|
||||
IDST => Idaho State Bengals
|
||||
ILST => Illinois State Redbirds
|
||||
ILW => Illinois Wesleyan Titans
|
||||
IND => Indianapolis
|
||||
INST => Indiana State Sycamores
|
||||
IOW => Iowa Wesleyan Tigers
|
||||
ITH => Ithaca Bombers
|
||||
JKST => Jackson State Tigers
|
||||
JOH => Johnson C Smith Golden Bulls
|
||||
JUN => Juniata Eagles
|
||||
KAL => Kalamazoo Hornets
|
||||
KAN => Kansas Wesleyan University Coyotes
|
||||
KEN => Kenyon Lords
|
||||
KIN => King's College (PA) Monarchs
|
||||
KNO => Knox College Prairie Fire
|
||||
KUT => Kutztown Golden Bears
|
||||
KYST => Kentucky State Thorobreds
|
||||
KYW => Kentucky Wesleyan Panthers
|
||||
LA => La Verne Leopards
|
||||
LAG => LaGrange College Panthers
|
||||
LAK => Lake Forest Foresters
|
||||
LAM => Lambuth Eagles
|
||||
LAN => Langston Lions
|
||||
LAW => Lawrence Vikings
|
||||
LEB => Lebanon Valley Flying Dutchmen
|
||||
LEH => Lehigh Mountain Hawks
|
||||
LEN => Lenoir-Rhyne Bears
|
||||
LEW => Lewis & Clark Pioneers
|
||||
LIM => Limestone Saints
|
||||
LIN => Linfield Wildcats
|
||||
LOC => Lock Haven Bald Eagles
|
||||
LOR => Loras College Duhawks
|
||||
LUT => Luther Norse
|
||||
LYC => Lycoming Warriors
|
||||
M-OH => Miami (OH) RedHawks
|
||||
MAC => Macalester Scots
|
||||
MAI => Maine Maritime Mariners
|
||||
MAN => Mansfield Mountaineers
|
||||
MAR => Maryville College Fighting Scots
|
||||
MAS => Mass Maritime Buccaneers
|
||||
MAY => Mayville State Comets
|
||||
MCM => McMurry War Hawks
|
||||
MCN => McNeese Cowboys
|
||||
MEN => Menlo College Oaks
|
||||
MER => Merchant Marine Mariners
|
||||
MERC => Mercyhurst Lakers
|
||||
MES => Colorado Mesa Mavericks
|
||||
MET => Methodist Monarchs
|
||||
MH => Mars Hill Mountain Lions
|
||||
MID => Midwestern State Mustangs
|
||||
MIL => Millsaps Majors
|
||||
MIN => Minot State Beavers
|
||||
MIS => Missouri Western Griffons
|
||||
MNST => Minnesota State Mavericks
|
||||
MONM => Monmouth Hawks
|
||||
MONT => Montana Grizzlies
|
||||
MOR => Morningside Chiefs
|
||||
MORE => Morehead State Eagles
|
||||
MORG => Morgan State Bears
|
||||
MOU => Mount Union Raiders
|
||||
MRST => Marist Red Foxes
|
||||
MSU => Michigan State Spartans
|
||||
MTST => Montana State Bobcats
|
||||
MTU => Michigan Tech Huskies
|
||||
MUH => Muhlenberg Mules
|
||||
MUR => Murray State Racers
|
||||
MUS => Muskingum Fighting Muskies
|
||||
MVSU => Mississippi Valley State Delta Devils
|
||||
NAU => Northern Arizona Lumberjacks
|
||||
NBY => Newberry Wolves
|
||||
NCAT => North Carolina A&T Aggies
|
||||
NCCU => North Carolina Central Eagles
|
||||
NCST => NC State Wolfpack
|
||||
NDOH => Notre Dame College Falcons
|
||||
NDSU => North Dakota State Bison
|
||||
NH => New Haven Chargers
|
||||
NICH => Nicholls Colonels
|
||||
NMH => New Mexico Highlands Cowboys
|
||||
NMI => Northern Michigan Wildcats
|
||||
NOR => Univ. of Northwestern-St. Paul Eagles
|
||||
NORF => Norfolk State Spartans
|
||||
OBE => Oberlin Yeomen
|
||||
OHI => Ohio Northern Polar Bears
|
||||
OKL => Oklahoma Baptist Bison
|
||||
OLI => Olivet College Comets
|
||||
OMA => Omaha Mavericks
|
||||
OTT => Otterbein Cardinals
|
||||
PAC => Pacific (OR) Boxers
|
||||
PENN => Pennsylvania Quakers
|
||||
PIKE => Pikeville Bears
|
||||
PRE => Presentation College Saints
|
||||
PRI => Principia College Panthers
|
||||
PRIN => Princeton Tigers
|
||||
PST => Pittsburg State Gorillas
|
||||
RED => Redlands Bulldogs
|
||||
RICH => Richmond Spiders
|
||||
RIT => Rochester Yellow Jackets
|
||||
ROB => Robert Morris (IL) Eagles
|
||||
ROS => Rose-Hulman Engineers
|
||||
SAC => Sacramento State Hornets
|
||||
SAG => Saginaw Valley Cardinals
|
||||
SDAK => South Dakota Coyotes
|
||||
SET => Seton Hill Griffins
|
||||
SIU => Southern Illinois Salukis
|
||||
SLI => Slippery Rock The Rock
|
||||
SOU => Southwestern College Moundbuilders
|
||||
SPR => Springfield College Pride
|
||||
ST => St. Scholastica Saints
|
||||
STE => Stevenson University Mustangs
|
||||
STET => Stetson Hatters
|
||||
STO => Stonehill College Skyhawks
|
||||
SUS => Susquehanna University River Hawks
|
||||
SUU => Southern Utah Thunderbirds
|
||||
TA&M => Texas A&M Aggies
|
||||
TAY => Taylor Trojans
|
||||
TIF => Tiffin University Dragons
|
||||
TRI => Trinity University (TX) Tigers
|
||||
TUF => Tufts University Jumbos
|
||||
TXST => Texas State Bobcats
|
||||
UAPB => Arkansas-Pine Bluff Golden Lions
|
||||
UCD => UC Davis Aggies
|
||||
UCONN => UConn Huskies
|
||||
ULM => UL Monroe Warhawks
|
||||
UMD => Minnesota-Duluth Bulldogs
|
||||
UMDA => UMASS Dartmouth Corsairs
|
||||
UML => UMass Lowell River Hawks
|
||||
UNA => North Alabama Lions
|
||||
UNCO => Northern Colorado Bears
|
||||
UND => North Dakota Fighting Hawks
|
||||
UNH => New Hampshire Wildcats
|
||||
UNI => University of Mary Marauders
|
||||
UNNY => Union Dutchmen
|
||||
UNT => North Texas Mean Green
|
||||
UPP => Upper Iowa Peacocks
|
||||
URI => Rhode Island Rams
|
||||
USA => South Alabama Jaguars
|
||||
USD => San Diego Toreros
|
||||
UTC => Chattanooga Mocs
|
||||
UTI => Utica College Pioneers
|
||||
VAL => Valley City State Vikings
|
||||
VILL => Villanova Wildcats
|
||||
VIR => Virginia State Trojans
|
||||
VT => Virginia Tech Hokies
|
||||
WAB => Wabash College Little Giants
|
||||
WAS => Washington-Missouri Bears
|
||||
WAY => Wayne State (MI) Warriors
|
||||
WES => Westminster College (MO) Blue Jays
|
||||
WHE => Wheaton College Illinois Thunder
|
||||
WIL => Wilkes University Colonels
|
||||
WIN => Wingate Bulldogs
|
||||
WIS => Wisconsin-Platteville Pioneers
|
||||
WOR => Worcester State College Lancers
|
||||
YALE => Yale Bulldogs
|
||||
|
||||
NHL:
|
||||
---
|
||||
ARI => Arizona Coyotes
|
||||
VGS => Vegas Golden Knights
|
||||
|
||||
SOCCER - BUNDESLIGA (GERMANY):
|
||||
-----------------------------
|
||||
DOR => Borussia Dortmund
|
||||
KOL => 1. FC Köln
|
||||
LEV => Bayer Leverkusen
|
||||
STU => VfB Stuttgart
|
||||
|
||||
SOCCER - LIGUE 1 (FRANCE):
|
||||
-------------------------
|
||||
LYON => Lyon
|
||||
MAR => Marseille
|
||||
NICE => Nice
|
||||
PSG => Paris Saint-Germain
|
||||
|
||||
SOCCER - PREMIER LEAGUE (ENGLAND):
|
||||
---------------------------------
|
||||
BUR => Burnley
|
||||
LUT => Luton Town
|
||||
SHU => Sheffield United
|
||||
|
||||
================================================================================
|
||||
SUMMARY BY SPORT:
|
||||
================================================================================
|
||||
MLB: 1 missing
|
||||
NCAAF: 295 missing
|
||||
NHL: 2 missing
|
||||
Soccer - Bundesliga (Germany): 4 missing
|
||||
Soccer - Ligue 1 (France): 4 missing
|
||||
Soccer - Premier League (England): 3 missing
|
||||
|
||||
================================================================================
|
||||
FILENAMES NEEDED:
|
||||
================================================================================
|
||||
Add these PNG files to their respective directories:
|
||||
|
||||
assets/sports/mlb_logos/OAK.png
|
||||
assets/sports/ncaa_fbs_logos/AAMU.png
|
||||
assets/sports/ncaa_fbs_logos/ACU.png
|
||||
assets/sports/ncaa_fbs_logos/ADA.png
|
||||
assets/sports/ncaa_fbs_logos/ADR.png
|
||||
assets/sports/ncaa_fbs_logos/AIC.png
|
||||
assets/sports/ncaa_fbs_logos/ALB.png
|
||||
assets/sports/ncaa_fbs_logos/ALBS.png
|
||||
assets/sports/ncaa_fbs_logos/ALCN.png
|
||||
assets/sports/ncaa_fbs_logos/ALD.png
|
||||
assets/sports/ncaa_fbs_logos/ALF.png
|
||||
assets/sports/ncaa_fbs_logos/ALL.png
|
||||
assets/sports/ncaa_fbs_logos/ALST.png
|
||||
assets/sports/ncaa_fbs_logos/AMH.png
|
||||
assets/sports/ncaa_fbs_logos/AND.png
|
||||
assets/sports/ncaa_fbs_logos/ANG.png
|
||||
assets/sports/ncaa_fbs_logos/ANN.png
|
||||
assets/sports/ncaa_fbs_logos/APSU.png
|
||||
assets/sports/ncaa_fbs_logos/ASH.png
|
||||
assets/sports/ncaa_fbs_logos/ASP.png
|
||||
assets/sports/ncaa_fbs_logos/ASU.png
|
||||
assets/sports/ncaa_fbs_logos/AUG.png
|
||||
assets/sports/ncaa_fbs_logos/AUR.png
|
||||
assets/sports/ncaa_fbs_logos/AUS.png
|
||||
assets/sports/ncaa_fbs_logos/AVE.png
|
||||
assets/sports/ncaa_fbs_logos/AVI.png
|
||||
assets/sports/ncaa_fbs_logos/AZU.png
|
||||
assets/sports/ncaa_fbs_logos/BAK.png
|
||||
assets/sports/ncaa_fbs_logos/BAL.png
|
||||
assets/sports/ncaa_fbs_logos/BAT.png
|
||||
assets/sports/ncaa_fbs_logos/BEC.png
|
||||
assets/sports/ncaa_fbs_logos/BEL.png
|
||||
assets/sports/ncaa_fbs_logos/BEN.png
|
||||
assets/sports/ncaa_fbs_logos/BENT.png
|
||||
assets/sports/ncaa_fbs_logos/BET.png
|
||||
assets/sports/ncaa_fbs_logos/BHS.png
|
||||
assets/sports/ncaa_fbs_logos/BIR.png
|
||||
assets/sports/ncaa_fbs_logos/BKN.png
|
||||
assets/sports/ncaa_fbs_logos/BLA.png
|
||||
assets/sports/ncaa_fbs_logos/BLOM.png
|
||||
assets/sports/ncaa_fbs_logos/BLU.png
|
||||
assets/sports/ncaa_fbs_logos/BOW.png
|
||||
assets/sports/ncaa_fbs_logos/BRI.png
|
||||
assets/sports/ncaa_fbs_logos/BRWN.png
|
||||
assets/sports/ncaa_fbs_logos/BST.png
|
||||
assets/sports/ncaa_fbs_logos/BUCK.png
|
||||
assets/sports/ncaa_fbs_logos/BUE.png
|
||||
assets/sports/ncaa_fbs_logos/BUF.png
|
||||
assets/sports/ncaa_fbs_logos/BUT.png
|
||||
assets/sports/ncaa_fbs_logos/CAM.png
|
||||
assets/sports/ncaa_fbs_logos/CAP.png
|
||||
assets/sports/ncaa_fbs_logos/CAR.png
|
||||
assets/sports/ncaa_fbs_logos/CARK.png
|
||||
assets/sports/ncaa_fbs_logos/CAS.png
|
||||
assets/sports/ncaa_fbs_logos/CAT.png
|
||||
assets/sports/ncaa_fbs_logos/CCSU.png
|
||||
assets/sports/ncaa_fbs_logos/CEN.png
|
||||
assets/sports/ncaa_fbs_logos/CHA.png
|
||||
assets/sports/ncaa_fbs_logos/CHI.png
|
||||
assets/sports/ncaa_fbs_logos/CHSO.png
|
||||
assets/sports/ncaa_fbs_logos/CLA.png
|
||||
assets/sports/ncaa_fbs_logos/CLMB.png
|
||||
assets/sports/ncaa_fbs_logos/COE.png
|
||||
assets/sports/ncaa_fbs_logos/COL.png
|
||||
assets/sports/ncaa_fbs_logos/COLC.png
|
||||
assets/sports/ncaa_fbs_logos/COLG.png
|
||||
assets/sports/ncaa_fbs_logos/CON.png
|
||||
assets/sports/ncaa_fbs_logos/COR.png
|
||||
assets/sports/ncaa_fbs_logos/CP.png
|
||||
assets/sports/ncaa_fbs_logos/CRO.png
|
||||
assets/sports/ncaa_fbs_logos/CSU.png
|
||||
assets/sports/ncaa_fbs_logos/CUL.png
|
||||
assets/sports/ncaa_fbs_logos/CUM.png
|
||||
assets/sports/ncaa_fbs_logos/CUR.png
|
||||
assets/sports/ncaa_fbs_logos/DAK.png
|
||||
assets/sports/ncaa_fbs_logos/DART.png
|
||||
assets/sports/ncaa_fbs_logos/DAV.png
|
||||
assets/sports/ncaa_fbs_logos/DAY.png
|
||||
assets/sports/ncaa_fbs_logos/DEF.png
|
||||
assets/sports/ncaa_fbs_logos/DEL.png
|
||||
assets/sports/ncaa_fbs_logos/DEN.png
|
||||
assets/sports/ncaa_fbs_logos/DEP.png
|
||||
assets/sports/ncaa_fbs_logos/DIC.png
|
||||
assets/sports/ncaa_fbs_logos/DRKE.png
|
||||
assets/sports/ncaa_fbs_logos/DSU.png
|
||||
assets/sports/ncaa_fbs_logos/DUB.png
|
||||
assets/sports/ncaa_fbs_logos/DUQ.png
|
||||
assets/sports/ncaa_fbs_logos/EAS.png
|
||||
assets/sports/ncaa_fbs_logos/EDI.png
|
||||
assets/sports/ncaa_fbs_logos/EIU.png
|
||||
assets/sports/ncaa_fbs_logos/EKU.png
|
||||
assets/sports/ncaa_fbs_logos/ELI.png
|
||||
assets/sports/ncaa_fbs_logos/ELM.png
|
||||
assets/sports/ncaa_fbs_logos/ELON.png
|
||||
assets/sports/ncaa_fbs_logos/EMO.png
|
||||
assets/sports/ncaa_fbs_logos/EMP.png
|
||||
assets/sports/ncaa_fbs_logos/END.png
|
||||
assets/sports/ncaa_fbs_logos/EOR.png
|
||||
assets/sports/ncaa_fbs_logos/ETSU.png
|
||||
assets/sports/ncaa_fbs_logos/EUR.png
|
||||
assets/sports/ncaa_fbs_logos/EWU.png
|
||||
assets/sports/ncaa_fbs_logos/FAY.png
|
||||
assets/sports/ncaa_fbs_logos/FDU.png
|
||||
assets/sports/ncaa_fbs_logos/FER.png
|
||||
assets/sports/ncaa_fbs_logos/FIN.png
|
||||
assets/sports/ncaa_fbs_logos/FIT.png
|
||||
assets/sports/ncaa_fbs_logos/FLA.png
|
||||
assets/sports/ncaa_fbs_logos/FOR.png
|
||||
assets/sports/ncaa_fbs_logos/FRA.png
|
||||
assets/sports/ncaa_fbs_logos/FRO.png
|
||||
assets/sports/ncaa_fbs_logos/FRST.png
|
||||
assets/sports/ncaa_fbs_logos/FTLW.png
|
||||
assets/sports/ncaa_fbs_logos/FUR.png
|
||||
assets/sports/ncaa_fbs_logos/GAL.png
|
||||
assets/sports/ncaa_fbs_logos/GAN.png
|
||||
assets/sports/ncaa_fbs_logos/GEN.png
|
||||
assets/sports/ncaa_fbs_logos/GEO.png
|
||||
assets/sports/ncaa_fbs_logos/GET.png
|
||||
assets/sports/ncaa_fbs_logos/GLE.png
|
||||
assets/sports/ncaa_fbs_logos/GMU.png
|
||||
assets/sports/ncaa_fbs_logos/GRA.png
|
||||
assets/sports/ncaa_fbs_logos/GRE.png
|
||||
assets/sports/ncaa_fbs_logos/GRI.png
|
||||
assets/sports/ncaa_fbs_logos/GRO.png
|
||||
assets/sports/ncaa_fbs_logos/GUI.png
|
||||
assets/sports/ncaa_fbs_logos/GWEB.png
|
||||
assets/sports/ncaa_fbs_logos/HAM.png
|
||||
assets/sports/ncaa_fbs_logos/HAMP.png
|
||||
assets/sports/ncaa_fbs_logos/HAN.png
|
||||
assets/sports/ncaa_fbs_logos/HAR.png
|
||||
assets/sports/ncaa_fbs_logos/HARV.png
|
||||
assets/sports/ncaa_fbs_logos/HAS.png
|
||||
assets/sports/ncaa_fbs_logos/HAW.png
|
||||
assets/sports/ncaa_fbs_logos/HBU.png
|
||||
assets/sports/ncaa_fbs_logos/HC.png
|
||||
assets/sports/ncaa_fbs_logos/HEI.png
|
||||
assets/sports/ncaa_fbs_logos/HEN.png
|
||||
assets/sports/ncaa_fbs_logos/HIL.png
|
||||
assets/sports/ncaa_fbs_logos/HIR.png
|
||||
assets/sports/ncaa_fbs_logos/HOB.png
|
||||
assets/sports/ncaa_fbs_logos/HOW.png
|
||||
assets/sports/ncaa_fbs_logos/HUS.png
|
||||
assets/sports/ncaa_fbs_logos/IDHO.png
|
||||
assets/sports/ncaa_fbs_logos/IDST.png
|
||||
assets/sports/ncaa_fbs_logos/ILST.png
|
||||
assets/sports/ncaa_fbs_logos/ILW.png
|
||||
assets/sports/ncaa_fbs_logos/IND.png
|
||||
assets/sports/ncaa_fbs_logos/INST.png
|
||||
assets/sports/ncaa_fbs_logos/IOW.png
|
||||
assets/sports/ncaa_fbs_logos/ITH.png
|
||||
assets/sports/ncaa_fbs_logos/JKST.png
|
||||
assets/sports/ncaa_fbs_logos/JOH.png
|
||||
assets/sports/ncaa_fbs_logos/JUN.png
|
||||
assets/sports/ncaa_fbs_logos/KAL.png
|
||||
assets/sports/ncaa_fbs_logos/KAN.png
|
||||
assets/sports/ncaa_fbs_logos/KEN.png
|
||||
assets/sports/ncaa_fbs_logos/KIN.png
|
||||
assets/sports/ncaa_fbs_logos/KNO.png
|
||||
assets/sports/ncaa_fbs_logos/KUT.png
|
||||
assets/sports/ncaa_fbs_logos/KYST.png
|
||||
assets/sports/ncaa_fbs_logos/KYW.png
|
||||
assets/sports/ncaa_fbs_logos/LA.png
|
||||
assets/sports/ncaa_fbs_logos/LAG.png
|
||||
assets/sports/ncaa_fbs_logos/LAK.png
|
||||
assets/sports/ncaa_fbs_logos/LAM.png
|
||||
assets/sports/ncaa_fbs_logos/LAN.png
|
||||
assets/sports/ncaa_fbs_logos/LAW.png
|
||||
assets/sports/ncaa_fbs_logos/LEB.png
|
||||
assets/sports/ncaa_fbs_logos/LEH.png
|
||||
assets/sports/ncaa_fbs_logos/LEN.png
|
||||
assets/sports/ncaa_fbs_logos/LEW.png
|
||||
assets/sports/ncaa_fbs_logos/LIM.png
|
||||
assets/sports/ncaa_fbs_logos/LIN.png
|
||||
assets/sports/ncaa_fbs_logos/LOC.png
|
||||
assets/sports/ncaa_fbs_logos/LOR.png
|
||||
assets/sports/ncaa_fbs_logos/LUT.png
|
||||
assets/sports/ncaa_fbs_logos/LYC.png
|
||||
assets/sports/ncaa_fbs_logos/M-OH.png
|
||||
assets/sports/ncaa_fbs_logos/MAC.png
|
||||
assets/sports/ncaa_fbs_logos/MAI.png
|
||||
assets/sports/ncaa_fbs_logos/MAN.png
|
||||
assets/sports/ncaa_fbs_logos/MAR.png
|
||||
assets/sports/ncaa_fbs_logos/MAS.png
|
||||
assets/sports/ncaa_fbs_logos/MAY.png
|
||||
assets/sports/ncaa_fbs_logos/MCM.png
|
||||
assets/sports/ncaa_fbs_logos/MCN.png
|
||||
assets/sports/ncaa_fbs_logos/MEN.png
|
||||
assets/sports/ncaa_fbs_logos/MER.png
|
||||
assets/sports/ncaa_fbs_logos/MERC.png
|
||||
assets/sports/ncaa_fbs_logos/MES.png
|
||||
assets/sports/ncaa_fbs_logos/MET.png
|
||||
assets/sports/ncaa_fbs_logos/MH.png
|
||||
assets/sports/ncaa_fbs_logos/MID.png
|
||||
assets/sports/ncaa_fbs_logos/MIL.png
|
||||
assets/sports/ncaa_fbs_logos/MIN.png
|
||||
assets/sports/ncaa_fbs_logos/MIS.png
|
||||
assets/sports/ncaa_fbs_logos/MNST.png
|
||||
assets/sports/ncaa_fbs_logos/MONM.png
|
||||
assets/sports/ncaa_fbs_logos/MONT.png
|
||||
assets/sports/ncaa_fbs_logos/MOR.png
|
||||
assets/sports/ncaa_fbs_logos/MORE.png
|
||||
assets/sports/ncaa_fbs_logos/MORG.png
|
||||
assets/sports/ncaa_fbs_logos/MOU.png
|
||||
assets/sports/ncaa_fbs_logos/MRST.png
|
||||
assets/sports/ncaa_fbs_logos/MSU.png
|
||||
assets/sports/ncaa_fbs_logos/MTST.png
|
||||
assets/sports/ncaa_fbs_logos/MTU.png
|
||||
assets/sports/ncaa_fbs_logos/MUH.png
|
||||
assets/sports/ncaa_fbs_logos/MUR.png
|
||||
assets/sports/ncaa_fbs_logos/MUS.png
|
||||
assets/sports/ncaa_fbs_logos/MVSU.png
|
||||
assets/sports/ncaa_fbs_logos/NAU.png
|
||||
assets/sports/ncaa_fbs_logos/NBY.png
|
||||
assets/sports/ncaa_fbs_logos/NCAT.png
|
||||
assets/sports/ncaa_fbs_logos/NCCU.png
|
||||
assets/sports/ncaa_fbs_logos/NCST.png
|
||||
assets/sports/ncaa_fbs_logos/NDOH.png
|
||||
assets/sports/ncaa_fbs_logos/NDSU.png
|
||||
assets/sports/ncaa_fbs_logos/NH.png
|
||||
assets/sports/ncaa_fbs_logos/NICH.png
|
||||
assets/sports/ncaa_fbs_logos/NMH.png
|
||||
assets/sports/ncaa_fbs_logos/NMI.png
|
||||
assets/sports/ncaa_fbs_logos/NOR.png
|
||||
assets/sports/ncaa_fbs_logos/NORF.png
|
||||
assets/sports/ncaa_fbs_logos/OBE.png
|
||||
assets/sports/ncaa_fbs_logos/OHI.png
|
||||
assets/sports/ncaa_fbs_logos/OKL.png
|
||||
assets/sports/ncaa_fbs_logos/OLI.png
|
||||
assets/sports/ncaa_fbs_logos/OMA.png
|
||||
assets/sports/ncaa_fbs_logos/OTT.png
|
||||
assets/sports/ncaa_fbs_logos/PAC.png
|
||||
assets/sports/ncaa_fbs_logos/PENN.png
|
||||
assets/sports/ncaa_fbs_logos/PIKE.png
|
||||
assets/sports/ncaa_fbs_logos/PRE.png
|
||||
assets/sports/ncaa_fbs_logos/PRI.png
|
||||
assets/sports/ncaa_fbs_logos/PRIN.png
|
||||
assets/sports/ncaa_fbs_logos/PST.png
|
||||
assets/sports/ncaa_fbs_logos/RED.png
|
||||
assets/sports/ncaa_fbs_logos/RICH.png
|
||||
assets/sports/ncaa_fbs_logos/RIT.png
|
||||
assets/sports/ncaa_fbs_logos/ROB.png
|
||||
assets/sports/ncaa_fbs_logos/ROS.png
|
||||
assets/sports/ncaa_fbs_logos/SAC.png
|
||||
assets/sports/ncaa_fbs_logos/SAG.png
|
||||
assets/sports/ncaa_fbs_logos/SDAK.png
|
||||
assets/sports/ncaa_fbs_logos/SET.png
|
||||
assets/sports/ncaa_fbs_logos/SIU.png
|
||||
assets/sports/ncaa_fbs_logos/SLI.png
|
||||
assets/sports/ncaa_fbs_logos/SOU.png
|
||||
assets/sports/ncaa_fbs_logos/SPR.png
|
||||
assets/sports/ncaa_fbs_logos/ST.png
|
||||
assets/sports/ncaa_fbs_logos/STE.png
|
||||
assets/sports/ncaa_fbs_logos/STET.png
|
||||
assets/sports/ncaa_fbs_logos/STO.png
|
||||
assets/sports/ncaa_fbs_logos/SUS.png
|
||||
assets/sports/ncaa_fbs_logos/SUU.png
|
||||
assets/sports/ncaa_fbs_logos/TA&M.png
|
||||
assets/sports/ncaa_fbs_logos/TAY.png
|
||||
assets/sports/ncaa_fbs_logos/TIF.png
|
||||
assets/sports/ncaa_fbs_logos/TRI.png
|
||||
assets/sports/ncaa_fbs_logos/TUF.png
|
||||
assets/sports/ncaa_fbs_logos/TXST.png
|
||||
assets/sports/ncaa_fbs_logos/UAPB.png
|
||||
assets/sports/ncaa_fbs_logos/UCD.png
|
||||
assets/sports/ncaa_fbs_logos/UCONN.png
|
||||
assets/sports/ncaa_fbs_logos/ULM.png
|
||||
assets/sports/ncaa_fbs_logos/UMD.png
|
||||
assets/sports/ncaa_fbs_logos/UMDA.png
|
||||
assets/sports/ncaa_fbs_logos/UML.png
|
||||
assets/sports/ncaa_fbs_logos/UNA.png
|
||||
assets/sports/ncaa_fbs_logos/UNCO.png
|
||||
assets/sports/ncaa_fbs_logos/UND.png
|
||||
assets/sports/ncaa_fbs_logos/UNH.png
|
||||
assets/sports/ncaa_fbs_logos/UNI.png
|
||||
assets/sports/ncaa_fbs_logos/UNNY.png
|
||||
assets/sports/ncaa_fbs_logos/UNT.png
|
||||
assets/sports/ncaa_fbs_logos/UPP.png
|
||||
assets/sports/ncaa_fbs_logos/URI.png
|
||||
assets/sports/ncaa_fbs_logos/USA.png
|
||||
assets/sports/ncaa_fbs_logos/USD.png
|
||||
assets/sports/ncaa_fbs_logos/UTC.png
|
||||
assets/sports/ncaa_fbs_logos/UTI.png
|
||||
assets/sports/ncaa_fbs_logos/VAL.png
|
||||
assets/sports/ncaa_fbs_logos/VILL.png
|
||||
assets/sports/ncaa_fbs_logos/VIR.png
|
||||
assets/sports/ncaa_fbs_logos/VT.png
|
||||
assets/sports/ncaa_fbs_logos/WAB.png
|
||||
assets/sports/ncaa_fbs_logos/WAS.png
|
||||
assets/sports/ncaa_fbs_logos/WAY.png
|
||||
assets/sports/ncaa_fbs_logos/WES.png
|
||||
assets/sports/ncaa_fbs_logos/WHE.png
|
||||
assets/sports/ncaa_fbs_logos/WIL.png
|
||||
assets/sports/ncaa_fbs_logos/WIN.png
|
||||
assets/sports/ncaa_fbs_logos/WIS.png
|
||||
assets/sports/ncaa_fbs_logos/WOR.png
|
||||
assets/sports/ncaa_fbs_logos/YALE.png
|
||||
assets/sports/nhl_logos/ARI.png
|
||||
assets/sports/nhl_logos/VGS.png
|
||||
assets/sports/soccer_logos/DOR.png
|
||||
assets/sports/soccer_logos/KOL.png
|
||||
assets/sports/soccer_logos/LEV.png
|
||||
assets/sports/soccer_logos/STU.png
|
||||
assets/sports/soccer_logos/LYON.png
|
||||
assets/sports/soccer_logos/MAR.png
|
||||
assets/sports/soccer_logos/NICE.png
|
||||
assets/sports/soccer_logos/PSG.png
|
||||
assets/sports/soccer_logos/BUR.png
|
||||
assets/sports/soccer_logos/LUT.png
|
||||
assets/sports/soccer_logos/SHU.png
|
||||
160
save_missing_teams.py
Normal file
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to save the missing teams list to a file for future reference.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def save_missing_teams():
|
||||
"""Save the missing teams list to a file."""
|
||||
|
||||
# Define the sports directories and their corresponding sections in the abbreviations file
|
||||
sports_dirs = {
|
||||
'mlb_logos': 'MLB',
|
||||
'nba_logos': 'NBA',
|
||||
'nfl_logos': 'NFL',
|
||||
'nhl_logos': 'NHL',
|
||||
'ncaa_fbs_logos': ['NCAAF', 'NCAA Conferences/Divisions', 'NCAA_big10', 'NCAA_big12', 'NCAA_acc', 'NCAA_sec', 'NCAA_pac12', 'NCAA_american', 'NCAA_cusa', 'NCAA_mac', 'NCAA_mwc', 'NCAA_sunbelt', 'NCAA_ind', 'NCAA_ovc', 'NCAA_col', 'NCAA_usa', 'NCAA_bigw'],
|
||||
'soccer_logos': ['Soccer - Premier League (England)', 'Soccer - La Liga (Spain)', 'Soccer - Bundesliga (Germany)', 'Soccer - Serie A (Italy)', 'Soccer - Ligue 1 (France)', 'Soccer - Champions League', 'Soccer - Other Teams'],
|
||||
'milb_logos': 'MiLB'
|
||||
}
|
||||
|
||||
# Read the abbreviations file
|
||||
abbreviations_file = Path("assets/sports/all_team_abbreviations.txt")
|
||||
if not abbreviations_file.exists():
|
||||
print("Error: all_team_abbreviations.txt not found")
|
||||
return
|
||||
|
||||
with open(abbreviations_file, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Parse teams from the abbreviations file
|
||||
teams_by_sport = {}
|
||||
current_section = None
|
||||
|
||||
for line in content.split('\n'):
|
||||
original_line = line
|
||||
line = line.strip()
|
||||
|
||||
# Check if this is a section header (not indented and no arrow)
|
||||
if line and not original_line.startswith(' ') and ' => ' not in line:
|
||||
current_section = line
|
||||
continue
|
||||
|
||||
# Check if this is a team entry (indented and has arrow)
|
||||
if original_line.startswith(' ') and ' => ' in line:
|
||||
parts = line.split(' => ')
|
||||
if len(parts) == 2:
|
||||
abbr = parts[0].strip()
|
||||
team_name = parts[1].strip()
|
||||
|
||||
if current_section not in teams_by_sport:
|
||||
teams_by_sport[current_section] = []
|
||||
teams_by_sport[current_section].append((abbr, team_name))
|
||||
|
||||
# Collect all missing teams
|
||||
all_missing_teams = []
|
||||
|
||||
for logo_dir, sections in sports_dirs.items():
|
||||
logo_path = Path(f"assets/sports/{logo_dir}")
|
||||
|
||||
if not logo_path.exists():
|
||||
print(f"⚠️ Logo directory not found: {logo_path}")
|
||||
continue
|
||||
|
||||
# Get all PNG files in the directory
|
||||
logo_files = [f.stem for f in logo_path.glob("*.png")]
|
||||
|
||||
# Check teams for this sport
|
||||
if isinstance(sections, str):
|
||||
sections = [sections]
|
||||
|
||||
for section in sections:
|
||||
if section not in teams_by_sport:
|
||||
continue
|
||||
|
||||
missing_teams = []
|
||||
|
||||
for abbr, team_name in teams_by_sport[section]:
|
||||
# Check if logo exists (case-insensitive)
|
||||
logo_found = False
|
||||
for logo_file in logo_files:
|
||||
if logo_file.lower() == abbr.lower():
|
||||
logo_found = True
|
||||
break
|
||||
|
||||
if not logo_found:
|
||||
missing_teams.append((abbr, team_name))
|
||||
|
||||
if missing_teams:
|
||||
all_missing_teams.extend([(section, abbr, team_name) for abbr, team_name in missing_teams])
|
||||
|
||||
# Sort by sport and then by team abbreviation
|
||||
all_missing_teams.sort(key=lambda x: (x[0], x[1]))
|
||||
|
||||
# Save to file
|
||||
output_file = "missing_team_logos.txt"
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
f.write("=" * 80 + "\n")
|
||||
f.write("MISSING TEAM LOGOS - COMPLETE LIST\n")
|
||||
f.write("=" * 80 + "\n")
|
||||
f.write(f"Total missing teams: {len(all_missing_teams)}\n")
|
||||
f.write("\n")
|
||||
|
||||
current_sport = None
|
||||
for section, abbr, team_name in all_missing_teams:
|
||||
if section != current_sport:
|
||||
current_sport = section
|
||||
f.write(f"\n{section.upper()}:\n")
|
||||
f.write("-" * len(section) + "\n")
|
||||
|
||||
f.write(f" {abbr:>8} => {team_name}\n")
|
||||
|
||||
f.write("\n" + "=" * 80 + "\n")
|
||||
f.write("SUMMARY BY SPORT:\n")
|
||||
f.write("=" * 80 + "\n")
|
||||
|
||||
# Count by sport
|
||||
sport_counts = {}
|
||||
for section, abbr, team_name in all_missing_teams:
|
||||
if section not in sport_counts:
|
||||
sport_counts[section] = 0
|
||||
sport_counts[section] += 1
|
||||
|
||||
for sport, count in sorted(sport_counts.items()):
|
||||
f.write(f"{sport:>30}: {count:>3} missing\n")
|
||||
|
||||
f.write("\n" + "=" * 80 + "\n")
|
||||
f.write("FILENAMES NEEDED:\n")
|
||||
f.write("=" * 80 + "\n")
|
||||
f.write("Add these PNG files to their respective directories:\n")
|
||||
f.write("\n")
|
||||
|
||||
for section, abbr, team_name in all_missing_teams:
|
||||
# Determine the directory based on the section
|
||||
if 'MLB' in section:
|
||||
dir_name = 'mlb_logos'
|
||||
elif 'NBA' in section:
|
||||
dir_name = 'nba_logos'
|
||||
elif 'NFL' in section:
|
||||
dir_name = 'nfl_logos'
|
||||
elif 'NHL' in section:
|
||||
dir_name = 'nhl_logos'
|
||||
elif 'NCAA' in section:
|
||||
dir_name = 'ncaa_fbs_logos'
|
||||
elif 'Soccer' in section:
|
||||
dir_name = 'soccer_logos'
|
||||
elif 'MiLB' in section:
|
||||
dir_name = 'milb_logos'
|
||||
else:
|
||||
dir_name = 'unknown'
|
||||
|
||||
f.write(f"assets/sports/{dir_name}/{abbr}.png\n")
|
||||
|
||||
print(f"✅ Missing teams list saved to: {output_file}")
|
||||
print(f"📊 Total missing teams: {len(all_missing_teams)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
save_missing_teams()
|
||||
@@ -581,6 +581,11 @@ class CacheManager:
|
||||
'memory_ttl': 172800,
|
||||
'force_refresh': False
|
||||
},
|
||||
'leaderboard': {
|
||||
'max_age': 604800, # 7 days (1 week) - football rankings updated weekly
|
||||
'memory_ttl': 1209600, # 14 days in memory
|
||||
'force_refresh': False
|
||||
},
|
||||
|
||||
# News and odds
|
||||
'news': {
|
||||
|
||||
@@ -42,8 +42,7 @@ class CalendarManager:
|
||||
logger.info(f"Calendar configuration: enabled={self.enabled}, update_interval={self.update_interval}, max_events={self.max_events}, calendars={self.calendars}")
|
||||
|
||||
# Get timezone from config
|
||||
self.config_manager = ConfigManager()
|
||||
timezone_str = self.config_manager.get_timezone()
|
||||
timezone_str = self.config.get('timezone', 'UTC')
|
||||
logger.info(f"Loading timezone from config: {timezone_str}")
|
||||
try:
|
||||
self.timezone = pytz.timezone(timezone_str)
|
||||
|
||||
14
src/clock.py
@@ -10,9 +10,15 @@ from src.display_manager import DisplayManager
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Clock:
|
||||
def __init__(self, display_manager: DisplayManager = None):
|
||||
self.config_manager = ConfigManager()
|
||||
self.config = self.config_manager.load_config()
|
||||
def __init__(self, display_manager: DisplayManager = None, config: Dict[str, Any] = None):
|
||||
if config is not None:
|
||||
# Use provided config
|
||||
self.config = config
|
||||
self.config_manager = None # Not needed when config is provided
|
||||
else:
|
||||
# Fallback: create ConfigManager and load config (for standalone usage)
|
||||
self.config_manager = ConfigManager()
|
||||
self.config = self.config_manager.load_config()
|
||||
# Use the provided display_manager or create a new one if none provided
|
||||
self.display_manager = display_manager or DisplayManager(self.config.get('display', {}))
|
||||
logger.info("Clock initialized with display_manager: %s", id(self.display_manager))
|
||||
@@ -31,7 +37,7 @@ class Clock:
|
||||
|
||||
def _get_timezone(self) -> pytz.timezone:
|
||||
"""Get timezone from the config file."""
|
||||
config_timezone = self.config_manager.get_timezone()
|
||||
config_timezone = self.config.get('timezone', 'UTC')
|
||||
try:
|
||||
return pytz.timezone(config_timezone)
|
||||
except pytz.exceptions.UnknownTimeZoneError:
|
||||
|
||||
@@ -20,6 +20,7 @@ from src.cache_manager import CacheManager
|
||||
from src.stock_manager import StockManager
|
||||
from src.stock_news_manager import StockNewsManager
|
||||
from src.odds_ticker_manager import OddsTickerManager
|
||||
from src.leaderboard_manager import LeaderboardManager
|
||||
from src.nhl_managers import NHLLiveManager, NHLRecentManager, NHLUpcomingManager
|
||||
from src.nba_managers import NBALiveManager, NBARecentManager, NBAUpcomingManager
|
||||
from src.mlb_manager import MLBLiveManager, MLBRecentManager, MLBUpcomingManager
|
||||
@@ -55,16 +56,17 @@ class DisplayController:
|
||||
|
||||
# Initialize display modes
|
||||
init_time = time.time()
|
||||
self.clock = Clock(self.display_manager) if self.config.get('clock', {}).get('enabled', True) else None
|
||||
self.clock = Clock(self.display_manager, self.config) if self.config.get('clock', {}).get('enabled', True) else None
|
||||
self.weather = WeatherManager(self.config, self.display_manager) if self.config.get('weather', {}).get('enabled', False) else None
|
||||
self.stocks = StockManager(self.config, self.display_manager) if self.config.get('stocks', {}).get('enabled', False) else None
|
||||
self.news = StockNewsManager(self.config, self.display_manager) if self.config.get('stock_news', {}).get('enabled', False) else None
|
||||
self.odds_ticker = OddsTickerManager(self.config, self.display_manager) if self.config.get('odds_ticker', {}).get('enabled', False) else None
|
||||
self.leaderboard = LeaderboardManager(self.config, self.display_manager) if self.config.get('leaderboard', {}).get('enabled', False) else None
|
||||
self.calendar = CalendarManager(self.display_manager, self.config) if self.config.get('calendar', {}).get('enabled', False) else None
|
||||
self.youtube = YouTubeDisplay(self.display_manager, self.config) if self.config.get('youtube', {}).get('enabled', False) else None
|
||||
self.text_display = TextDisplay(self.display_manager, self.config) if self.config.get('text_display', {}).get('enabled', False) else None
|
||||
self.of_the_day = OfTheDayManager(self.display_manager, self.config) if self.config.get('of_the_day', {}).get('enabled', False) else None
|
||||
self.news_manager = NewsManager(self.config, self.display_manager) if self.config.get('news_manager', {}).get('enabled', False) else None
|
||||
self.news_manager = NewsManager(self.config, self.display_manager, self.config_manager) if self.config.get('news_manager', {}).get('enabled', False) else None
|
||||
logger.info(f"Calendar Manager initialized: {'Object' if self.calendar else 'None'}")
|
||||
logger.info(f"Text Display initialized: {'Object' if self.text_display else 'None'}")
|
||||
logger.info(f"OfTheDay Manager initialized: {'Object' if self.of_the_day else 'None'}")
|
||||
@@ -258,6 +260,7 @@ class DisplayController:
|
||||
if self.stocks: self.available_modes.append('stocks')
|
||||
if self.news: self.available_modes.append('stock_news')
|
||||
if self.odds_ticker: self.available_modes.append('odds_ticker')
|
||||
if self.leaderboard: self.available_modes.append('leaderboard')
|
||||
if self.calendar: self.available_modes.append('calendar')
|
||||
if self.youtube: self.available_modes.append('youtube')
|
||||
if self.text_display: self.available_modes.append('text_display')
|
||||
@@ -427,11 +430,9 @@ class DisplayController:
|
||||
logger.info(f"Initial display mode: {self.current_display_mode}")
|
||||
logger.info("DisplayController initialized with display_manager: %s", id(self.display_manager))
|
||||
|
||||
# --- SCHEDULING & CONFIG REFRESH ---
|
||||
self.config_check_interval = 30
|
||||
self.last_config_check = 0
|
||||
# --- SCHEDULING ---
|
||||
self.is_display_active = True
|
||||
self._load_config() # Initial load of schedule
|
||||
self._load_schedule_config() # Load schedule config once at startup
|
||||
|
||||
def _handle_music_update(self, track_info: Dict[str, Any], significant_change: bool = False):
|
||||
"""Callback for when music track info changes."""
|
||||
@@ -516,6 +517,20 @@ class DisplayController:
|
||||
# Fall back to configured duration
|
||||
return self.display_durations.get(mode_key, 60)
|
||||
|
||||
# Handle dynamic duration for leaderboard
|
||||
if mode_key == 'leaderboard' and self.leaderboard:
|
||||
try:
|
||||
dynamic_duration = self.leaderboard.get_dynamic_duration()
|
||||
# Only log if duration has changed or we haven't logged this duration yet
|
||||
if not hasattr(self, '_last_logged_leaderboard_duration') or self._last_logged_leaderboard_duration != dynamic_duration:
|
||||
logger.info(f"Using dynamic duration for leaderboard: {dynamic_duration} seconds")
|
||||
self._last_logged_leaderboard_duration = dynamic_duration
|
||||
return dynamic_duration
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting dynamic duration for leaderboard: {e}")
|
||||
# Fall back to configured duration
|
||||
return self.display_durations.get(mode_key, 60)
|
||||
|
||||
# Simplify weather key handling
|
||||
if mode_key.startswith('weather_'):
|
||||
return self.display_durations.get(mode_key, 15)
|
||||
@@ -530,14 +545,33 @@ class DisplayController:
|
||||
|
||||
def _update_modules(self):
|
||||
"""Call update methods on active managers."""
|
||||
if self.weather: self.weather.get_weather()
|
||||
if self.stocks: self.stocks.update_stock_data()
|
||||
if self.news: self.news.update_news_data()
|
||||
if self.odds_ticker: self.odds_ticker.update()
|
||||
if self.calendar: self.calendar.update(time.time())
|
||||
if self.youtube: self.youtube.update()
|
||||
if self.text_display: self.text_display.update()
|
||||
if self.of_the_day: self.of_the_day.update(time.time())
|
||||
# Check if we're currently scrolling and defer updates if so
|
||||
if self.display_manager.is_currently_scrolling():
|
||||
logger.debug("Display is currently scrolling, deferring module updates")
|
||||
# Defer updates for modules that might cause lag during scrolling
|
||||
if self.odds_ticker:
|
||||
self.display_manager.defer_update(self.odds_ticker.update, priority=1)
|
||||
if self.stocks:
|
||||
self.display_manager.defer_update(self.stocks.update_stock_data, priority=2)
|
||||
if self.news:
|
||||
self.display_manager.defer_update(self.news.update_news_data, priority=2)
|
||||
# Continue with non-scrolling-sensitive updates
|
||||
if self.weather: self.weather.get_weather()
|
||||
if self.calendar: self.calendar.update(time.time())
|
||||
if self.youtube: self.youtube.update()
|
||||
if self.text_display: self.text_display.update()
|
||||
if self.of_the_day: self.of_the_day.update(time.time())
|
||||
else:
|
||||
# Not scrolling, perform all updates normally
|
||||
if self.weather: self.weather.get_weather()
|
||||
if self.stocks: self.stocks.update_stock_data()
|
||||
if self.news: self.news.update_news_data()
|
||||
if self.odds_ticker: self.odds_ticker.update()
|
||||
if self.calendar: self.calendar.update(time.time())
|
||||
if self.youtube: self.youtube.update()
|
||||
if self.text_display: self.text_display.update()
|
||||
if self.of_the_day: self.of_the_day.update(time.time())
|
||||
|
||||
# News manager fetches data when displayed, not during updates
|
||||
# if self.news_manager: self.news_manager.fetch_news_data()
|
||||
|
||||
@@ -829,14 +863,14 @@ class DisplayController:
|
||||
self.ncaa_fb_showing_recent = True # Reset to recent for the new team
|
||||
|
||||
# --- SCHEDULING METHODS ---
|
||||
def _load_config(self):
|
||||
"""Load configuration from the config manager and parse schedule settings."""
|
||||
self.config = self.config_manager.load_config()
|
||||
def _load_schedule_config(self):
|
||||
"""Load schedule configuration once at startup."""
|
||||
schedule_config = self.config.get('schedule', {})
|
||||
self.schedule_enabled = schedule_config.get('enabled', False)
|
||||
try:
|
||||
self.start_time = datetime.strptime(schedule_config.get('start_time', '07:00'), '%H:%M').time()
|
||||
self.end_time = datetime.strptime(schedule_config.get('end_time', '22:00'), '%H:%M').time()
|
||||
logger.info(f"Schedule loaded: enabled={self.schedule_enabled}, start={self.start_time}, end={self.end_time}")
|
||||
except (ValueError, TypeError):
|
||||
logger.warning("Invalid time format in schedule config. Using defaults.")
|
||||
self.start_time = time_obj(7, 0)
|
||||
@@ -926,12 +960,7 @@ class DisplayController:
|
||||
while True:
|
||||
current_time = time.time()
|
||||
|
||||
# Periodically check for config changes
|
||||
if current_time - self.last_config_check > self.config_check_interval:
|
||||
self._load_config()
|
||||
self.last_config_check = current_time
|
||||
|
||||
# Enforce the schedule
|
||||
# Check the schedule (no config reload needed)
|
||||
self._check_schedule()
|
||||
if not self.is_display_active:
|
||||
time.sleep(60)
|
||||
@@ -940,6 +969,9 @@ class DisplayController:
|
||||
# Update data for all modules first
|
||||
self._update_modules()
|
||||
|
||||
# Process any deferred updates that may have accumulated
|
||||
self.display_manager.process_deferred_updates()
|
||||
|
||||
# Update live modes in rotation if needed
|
||||
self._update_live_modes_in_rotation()
|
||||
|
||||
@@ -1097,6 +1129,8 @@ class DisplayController:
|
||||
manager_to_display = self.news
|
||||
elif self.current_display_mode == 'odds_ticker' and self.odds_ticker:
|
||||
manager_to_display = self.odds_ticker
|
||||
elif self.current_display_mode == 'leaderboard' and self.leaderboard:
|
||||
manager_to_display = self.leaderboard
|
||||
elif self.current_display_mode == 'calendar' and self.calendar:
|
||||
manager_to_display = self.calendar
|
||||
elif self.current_display_mode == 'youtube' and self.youtube:
|
||||
|
||||
@@ -30,6 +30,15 @@ class DisplayManager:
|
||||
self._snapshot_path = "/tmp/led_matrix_preview.png"
|
||||
self._snapshot_min_interval_sec = 0.2 # max ~5 fps
|
||||
self._last_snapshot_ts = 0.0
|
||||
|
||||
# Scrolling state tracking for graceful updates
|
||||
self._scrolling_state = {
|
||||
'is_scrolling': False,
|
||||
'last_scroll_activity': 0,
|
||||
'scroll_inactivity_threshold': 2.0, # seconds of inactivity before considering "not scrolling"
|
||||
'deferred_updates': []
|
||||
}
|
||||
|
||||
self._setup_matrix()
|
||||
logger.info("Matrix setup completed in %.3f seconds", time.time() - start_time)
|
||||
|
||||
@@ -634,6 +643,77 @@ class DisplayManager:
|
||||
|
||||
return dt.strftime(f"%b %-d{suffix}")
|
||||
|
||||
def set_scrolling_state(self, is_scrolling: bool):
|
||||
"""Set the current scrolling state. Call this when a display starts/stops scrolling."""
|
||||
current_time = time.time()
|
||||
self._scrolling_state['is_scrolling'] = is_scrolling
|
||||
if is_scrolling:
|
||||
self._scrolling_state['last_scroll_activity'] = current_time
|
||||
logger.debug(f"Scrolling state set to: {is_scrolling}")
|
||||
|
||||
def is_currently_scrolling(self) -> bool:
|
||||
"""Check if the display is currently in a scrolling state."""
|
||||
current_time = time.time()
|
||||
|
||||
# If explicitly not scrolling, return False
|
||||
if not self._scrolling_state['is_scrolling']:
|
||||
return False
|
||||
|
||||
# If we've been inactive for the threshold period, consider it not scrolling
|
||||
if current_time - self._scrolling_state['last_scroll_activity'] > self._scrolling_state['scroll_inactivity_threshold']:
|
||||
self._scrolling_state['is_scrolling'] = False
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def defer_update(self, update_func, priority: int = 0):
|
||||
"""Defer an update function to be called when not scrolling.
|
||||
|
||||
Args:
|
||||
update_func: Function to call when not scrolling
|
||||
priority: Priority level (lower numbers = higher priority)
|
||||
"""
|
||||
self._scrolling_state['deferred_updates'].append({
|
||||
'func': update_func,
|
||||
'priority': priority,
|
||||
'timestamp': time.time()
|
||||
})
|
||||
# Sort by priority (lower numbers first)
|
||||
self._scrolling_state['deferred_updates'].sort(key=lambda x: x['priority'])
|
||||
logger.debug(f"Deferred update added. Total deferred: {len(self._scrolling_state['deferred_updates'])}")
|
||||
|
||||
def process_deferred_updates(self):
|
||||
"""Process any deferred updates if not currently scrolling."""
|
||||
if self.is_currently_scrolling():
|
||||
return
|
||||
|
||||
if not self._scrolling_state['deferred_updates']:
|
||||
return
|
||||
|
||||
# Process all deferred updates
|
||||
updates_to_process = self._scrolling_state['deferred_updates'].copy()
|
||||
self._scrolling_state['deferred_updates'].clear()
|
||||
|
||||
logger.debug(f"Processing {len(updates_to_process)} deferred updates")
|
||||
|
||||
for update_info in updates_to_process:
|
||||
try:
|
||||
update_info['func']()
|
||||
logger.debug("Deferred update executed successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing deferred update: {e}")
|
||||
# Re-add failed updates for retry
|
||||
self._scrolling_state['deferred_updates'].append(update_info)
|
||||
|
||||
def get_scrolling_stats(self) -> dict:
|
||||
"""Get current scrolling statistics for debugging."""
|
||||
return {
|
||||
'is_scrolling': self._scrolling_state['is_scrolling'],
|
||||
'last_activity': self._scrolling_state['last_scroll_activity'],
|
||||
'deferred_count': len(self._scrolling_state['deferred_updates']),
|
||||
'inactivity_threshold': self._scrolling_state['scroll_inactivity_threshold']
|
||||
}
|
||||
|
||||
def _write_snapshot_if_due(self) -> None:
|
||||
"""Write the current image to a PNG snapshot file at a limited frequency."""
|
||||
try:
|
||||
|
||||
1279
src/leaderboard_manager.py
Normal file
662
src/logo_downloader.py
Normal file
@@ -0,0 +1,662 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Centralized logo downloader utility for automatically fetching team logos from ESPN API.
|
||||
This module provides functionality to download missing team logos for various sports leagues,
|
||||
with special support for FCS teams and other NCAA divisions.
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import requests
|
||||
import json
|
||||
from typing import Dict, Any, List, Optional, Tuple
|
||||
from pathlib import Path
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util.retry import Retry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class LogoDownloader:
|
||||
"""Centralized logo downloader for team logos from ESPN API."""
|
||||
|
||||
# ESPN API endpoints for different sports/leagues
|
||||
API_ENDPOINTS = {
|
||||
'nfl': 'https://site.api.espn.com/apis/site/v2/sports/football/nfl/teams',
|
||||
'nba': 'https://site.api.espn.com/apis/site/v2/sports/basketball/nba/teams',
|
||||
'mlb': 'https://site.api.espn.com/apis/site/v2/sports/baseball/mlb/teams',
|
||||
'nhl': 'https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/teams',
|
||||
'ncaa_fb': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams',
|
||||
'ncaa_fb_all': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams', # Includes FCS
|
||||
'fcs': 'https://site.api.espn.com/apis/site/v2/sports/football/college-football/teams', # FCS teams from same endpoint
|
||||
'ncaam_basketball': 'https://site.api.espn.com/apis/site/v2/sports/basketball/mens-college-basketball/teams',
|
||||
'ncaa_baseball': 'https://site.api.espn.com/apis/site/v2/sports/baseball/college-baseball/teams'
|
||||
}
|
||||
|
||||
# Directory mappings for different leagues
|
||||
LOGO_DIRECTORIES = {
|
||||
'nfl': 'assets/sports/nfl_logos',
|
||||
'nba': 'assets/sports/nba_logos',
|
||||
'mlb': 'assets/sports/mlb_logos',
|
||||
'nhl': 'assets/sports/nhl_logos',
|
||||
'ncaa_fb': 'assets/sports/ncaa_fbs_logos',
|
||||
'ncaa_fb_all': 'assets/sports/ncaa_fbs_logos', # FCS teams go in same directory
|
||||
'fcs': 'assets/sports/ncaa_fbs_logos', # FCS teams go in same directory
|
||||
'ncaam_basketball': 'assets/sports/ncaa_fbs_logos',
|
||||
'ncaa_baseball': 'assets/sports/ncaa_fbs_logos'
|
||||
}
|
||||
|
||||
def __init__(self, request_timeout: int = 30, retry_attempts: int = 3):
|
||||
"""Initialize the logo downloader with HTTP session and retry logic."""
|
||||
self.request_timeout = request_timeout
|
||||
self.retry_attempts = retry_attempts
|
||||
|
||||
# Set up session with retry logic
|
||||
self.session = requests.Session()
|
||||
retry_strategy = Retry(
|
||||
total=retry_attempts,
|
||||
backoff_factor=1,
|
||||
status_forcelist=[429, 500, 502, 503, 504],
|
||||
allowed_methods=["GET", "HEAD", "OPTIONS"]
|
||||
)
|
||||
adapter = HTTPAdapter(max_retries=retry_strategy)
|
||||
self.session.mount("https://", adapter)
|
||||
self.session.mount("http://", adapter)
|
||||
|
||||
# Set up headers
|
||||
self.headers = {
|
||||
'User-Agent': 'LEDMatrix/1.0 (https://github.com/yourusername/LEDMatrix; contact@example.com)',
|
||||
'Accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Connection': 'keep-alive'
|
||||
}
|
||||
|
||||
def normalize_abbreviation(self, abbreviation: str) -> str:
|
||||
"""Normalize team abbreviation for consistent filename usage."""
|
||||
# Handle special characters that can cause filesystem issues
|
||||
normalized = abbreviation.upper()
|
||||
# Replace problematic characters with safe alternatives
|
||||
normalized = normalized.replace('&', 'AND')
|
||||
normalized = normalized.replace('/', '_')
|
||||
normalized = normalized.replace('\\', '_')
|
||||
normalized = normalized.replace(':', '_')
|
||||
normalized = normalized.replace('*', '_')
|
||||
normalized = normalized.replace('?', '_')
|
||||
normalized = normalized.replace('"', '_')
|
||||
normalized = normalized.replace('<', '_')
|
||||
normalized = normalized.replace('>', '_')
|
||||
normalized = normalized.replace('|', '_')
|
||||
return normalized
|
||||
|
||||
def get_logo_directory(self, league: str) -> str:
|
||||
"""Get the logo directory for a given league."""
|
||||
return self.LOGO_DIRECTORIES.get(league, f'assets/sports/{league}_logos')
|
||||
|
||||
def ensure_logo_directory(self, logo_dir: str) -> bool:
|
||||
"""Ensure the logo directory exists, create if necessary."""
|
||||
try:
|
||||
os.makedirs(logo_dir, exist_ok=True)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create logo directory {logo_dir}: {e}")
|
||||
return False
|
||||
|
||||
def download_logo(self, logo_url: str, filepath: Path, team_name: str) -> bool:
|
||||
"""Download a single logo from URL and save to filepath."""
|
||||
try:
|
||||
response = self.session.get(logo_url, headers=self.headers, timeout=self.request_timeout)
|
||||
response.raise_for_status()
|
||||
|
||||
# Verify it's actually an image
|
||||
content_type = response.headers.get('content-type', '').lower()
|
||||
if not any(img_type in content_type for img_type in ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']):
|
||||
logger.warning(f"Downloaded content for {team_name} is not an image: {content_type}")
|
||||
return False
|
||||
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(response.content)
|
||||
|
||||
# Verify and convert the downloaded image to RGBA format
|
||||
try:
|
||||
with Image.open(filepath) as img:
|
||||
# Convert to RGBA to avoid PIL warnings about palette images with transparency
|
||||
if img.mode in ('P', 'LA', 'L'):
|
||||
# Convert palette or grayscale images to RGBA
|
||||
img = img.convert('RGBA')
|
||||
elif img.mode == 'RGB':
|
||||
# Convert RGB to RGBA (add alpha channel)
|
||||
img = img.convert('RGBA')
|
||||
elif img.mode != 'RGBA':
|
||||
# For any other mode, convert to RGBA
|
||||
img = img.convert('RGBA')
|
||||
|
||||
# Save the converted image
|
||||
img.save(filepath, 'PNG')
|
||||
|
||||
logger.info(f"Successfully downloaded and converted logo for {team_name} -> {filepath.name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Downloaded file for {team_name} is not a valid image or conversion failed: {e}")
|
||||
try:
|
||||
os.remove(filepath) # Remove invalid file
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Failed to download logo for {team_name}: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error downloading logo for {team_name}: {e}")
|
||||
return False
|
||||
|
||||
def fetch_teams_data(self, league: str) -> Optional[Dict]:
|
||||
"""Fetch team data from ESPN API for a specific league."""
|
||||
api_url = self.API_ENDPOINTS.get(league)
|
||||
if not api_url:
|
||||
logger.error(f"No API endpoint configured for league: {league}")
|
||||
return None
|
||||
|
||||
try:
|
||||
logger.info(f"Fetching team data for {league} from ESPN API...")
|
||||
response = self.session.get(api_url, headers=self.headers, timeout=self.request_timeout)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
logger.info(f"Successfully fetched team data for {league}")
|
||||
return data
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Error fetching team data for {league}: {e}")
|
||||
return None
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error parsing JSON response for {league}: {e}")
|
||||
return None
|
||||
|
||||
def extract_teams_from_data(self, data: Dict, league: str) -> List[Dict[str, str]]:
|
||||
"""Extract team information from ESPN API response."""
|
||||
teams = []
|
||||
|
||||
try:
|
||||
sports = data.get('sports', [])
|
||||
for sport in sports:
|
||||
leagues_data = sport.get('leagues', [])
|
||||
for league_data in leagues_data:
|
||||
teams_data = league_data.get('teams', [])
|
||||
|
||||
for team_data in teams_data:
|
||||
team_info = team_data.get('team', {})
|
||||
|
||||
abbreviation = team_info.get('abbreviation', '')
|
||||
display_name = team_info.get('displayName', 'Unknown')
|
||||
logos = team_info.get('logos', [])
|
||||
|
||||
if not abbreviation or not logos:
|
||||
continue
|
||||
|
||||
# Get the default logo (first one is usually default)
|
||||
logo_url = logos[0].get('href', '')
|
||||
if not logo_url:
|
||||
continue
|
||||
|
||||
# For NCAA football, try to determine if it's FCS or FBS
|
||||
team_category = 'FBS' # Default
|
||||
if league in ['ncaa_fb', 'ncaa_fb_all', 'fcs']:
|
||||
# Check if this is an FCS team by looking at conference or other indicators
|
||||
# ESPN API includes both FBS and FCS teams in the same endpoint
|
||||
# We'll include all teams and let the user decide which ones to use
|
||||
team_category = self._determine_ncaa_football_division(team_info, league_data)
|
||||
|
||||
teams.append({
|
||||
'abbreviation': abbreviation,
|
||||
'display_name': display_name,
|
||||
'logo_url': logo_url,
|
||||
'league': league,
|
||||
'category': team_category,
|
||||
'conference': league_data.get('name', 'Unknown')
|
||||
})
|
||||
|
||||
logger.info(f"Extracted {len(teams)} teams for {league}")
|
||||
return teams
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error extracting teams for {league}: {e}")
|
||||
return []
|
||||
|
||||
def _determine_ncaa_football_division(self, team_info: Dict, league_data: Dict) -> str:
|
||||
"""Determine if an NCAA football team is FBS or FCS based on conference and other indicators."""
|
||||
conference_name = league_data.get('name', '').lower()
|
||||
|
||||
# FBS Conferences (more comprehensive list)
|
||||
fbs_conferences = {
|
||||
'acc', 'american athletic', 'big 12', 'big ten', 'conference usa', 'c-usa',
|
||||
'mid-american', 'mac', 'mountain west', 'pac-12', 'pac-10', 'sec',
|
||||
'sun belt', 'independents', 'big east'
|
||||
}
|
||||
|
||||
# FCS Conferences (more comprehensive list)
|
||||
fcs_conferences = {
|
||||
'big sky', 'big south', 'colonial athletic', 'caa', 'ivy league',
|
||||
'meac', 'missouri valley', 'mvfc', 'northeast', 'nec',
|
||||
'ohio valley', 'ovc', 'patriot league', 'pioneer football',
|
||||
'southland', 'southern', 'southwestern athletic', 'swac',
|
||||
'western athletic', 'wac', 'ncaa division i-aa'
|
||||
}
|
||||
|
||||
# Also check for specific team indicators
|
||||
team_abbreviation = team_info.get('abbreviation', '').upper()
|
||||
|
||||
# Known FBS teams that might be misclassified
|
||||
known_fbs_teams = {
|
||||
'ASU', 'ARIZ', 'ARK', 'AUB', 'BOIS', 'CSU', 'FLA', 'HAW', 'IDHO', 'USA'
|
||||
}
|
||||
|
||||
# Check if it's a known FBS team first
|
||||
if team_abbreviation in known_fbs_teams:
|
||||
return 'FBS'
|
||||
|
||||
# Check conference names
|
||||
if any(fbs_conf in conference_name for fbs_conf in fbs_conferences):
|
||||
return 'FBS'
|
||||
elif any(fcs_conf in conference_name for fcs_conf in fcs_conferences):
|
||||
return 'FCS'
|
||||
|
||||
# If conference is just "NCAA - Football", we need to use other indicators
|
||||
if conference_name == 'ncaa - football':
|
||||
# Check team name for indicators of FCS (smaller schools, Division II/III)
|
||||
team_name = team_info.get('displayName', '').lower()
|
||||
fcs_indicators = ['college', 'university', 'state', 'tech', 'community']
|
||||
|
||||
# If it has typical FCS naming patterns and isn't a known FBS team
|
||||
if any(indicator in team_name for indicator in fcs_indicators):
|
||||
return 'FCS'
|
||||
else:
|
||||
return 'FBS'
|
||||
|
||||
# Default to FBS for unknown conferences
|
||||
return 'FBS'
|
||||
|
||||
def _get_team_name_variations(self, abbreviation: str) -> List[str]:
|
||||
"""Generate common variations of a team abbreviation for matching."""
|
||||
variations = set()
|
||||
abbr = abbreviation.upper()
|
||||
variations.add(abbr)
|
||||
|
||||
# Add normalized version
|
||||
variations.add(self.normalize_abbreviation(abbr))
|
||||
|
||||
# Common substitutions
|
||||
substitutions = {
|
||||
'&': ['AND', 'A'],
|
||||
'A&M': ['TAMU', 'TA&M', 'TEXASAM'],
|
||||
'STATE': ['ST', 'ST.'],
|
||||
'UNIVERSITY': ['U', 'UNIV'],
|
||||
'COLLEGE': ['C', 'COL'],
|
||||
'TECHNICAL': ['TECH', 'T'],
|
||||
'NORTHERN': ['NORTH', 'N'],
|
||||
'SOUTHERN': ['SOUTH', 'S'],
|
||||
'EASTERN': ['EAST', 'E'],
|
||||
'WESTERN': ['WEST', 'W']
|
||||
}
|
||||
|
||||
# Apply substitutions
|
||||
for original, replacements in substitutions.items():
|
||||
if original in abbr:
|
||||
for replacement in replacements:
|
||||
variations.add(abbr.replace(original, replacement))
|
||||
variations.add(abbr.replace(original, '')) # Remove the word entirely
|
||||
|
||||
# Add common abbreviations for Texas A&M
|
||||
if 'A&M' in abbr or 'TAMU' in abbr:
|
||||
variations.update(['TAMU', 'TA&M', 'TEXASAM', 'TEXAS_A&M', 'TEXAS_AM'])
|
||||
|
||||
return list(variations)
|
||||
|
||||
def download_missing_logos_for_league(self, league: str, force_download: bool = False) -> Tuple[int, int]:
|
||||
"""Download missing logos for a specific league."""
|
||||
logger.info(f"Starting logo download for league: {league}")
|
||||
|
||||
# Get logo directory
|
||||
logo_dir = self.get_logo_directory(league)
|
||||
if not self.ensure_logo_directory(logo_dir):
|
||||
logger.error(f"Failed to create logo directory for {league}")
|
||||
return 0, 0
|
||||
|
||||
# Fetch team data
|
||||
data = self.fetch_teams_data(league)
|
||||
if not data:
|
||||
logger.error(f"Failed to fetch team data for {league}")
|
||||
return 0, 0
|
||||
|
||||
# Extract teams
|
||||
teams = self.extract_teams_from_data(data, league)
|
||||
if not teams:
|
||||
logger.warning(f"No teams found for {league}")
|
||||
return 0, 0
|
||||
|
||||
# Download missing logos
|
||||
downloaded_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for team in teams:
|
||||
abbreviation = team['abbreviation']
|
||||
display_name = team['display_name']
|
||||
logo_url = team['logo_url']
|
||||
|
||||
# Create filename
|
||||
filename = f"{self.normalize_abbreviation(abbreviation)}.png"
|
||||
filepath = Path(logo_dir) / filename
|
||||
|
||||
# Skip if already exists and not forcing download
|
||||
if filepath.exists() and not force_download:
|
||||
logger.debug(f"Skipping {display_name}: {filename} already exists")
|
||||
continue
|
||||
|
||||
# Download logo
|
||||
if self.download_logo(logo_url, filepath, display_name):
|
||||
downloaded_count += 1
|
||||
else:
|
||||
failed_count += 1
|
||||
|
||||
# Small delay to be respectful to the API
|
||||
time.sleep(0.1)
|
||||
|
||||
logger.info(f"Logo download complete for {league}: {downloaded_count} downloaded, {failed_count} failed")
|
||||
return downloaded_count, failed_count
|
||||
|
||||
def download_all_ncaa_football_logos(self, include_fcs: bool = True, force_download: bool = False) -> Tuple[int, int]:
|
||||
"""Download all NCAA football team logos including FCS teams."""
|
||||
logger.info(f"Starting comprehensive NCAA football logo download (FCS: {include_fcs})")
|
||||
|
||||
# Use the comprehensive NCAA football endpoint
|
||||
league = 'ncaa_fb_all'
|
||||
logo_dir = self.get_logo_directory(league)
|
||||
if not self.ensure_logo_directory(logo_dir):
|
||||
logger.error(f"Failed to create logo directory for {league}")
|
||||
return 0, 0
|
||||
|
||||
# Fetch team data
|
||||
data = self.fetch_teams_data(league)
|
||||
if not data:
|
||||
logger.error(f"Failed to fetch team data for {league}")
|
||||
return 0, 0
|
||||
|
||||
# Extract teams
|
||||
teams = self.extract_teams_from_data(data, league)
|
||||
if not teams:
|
||||
logger.warning(f"No teams found for {league}")
|
||||
return 0, 0
|
||||
|
||||
# Filter teams based on FCS inclusion
|
||||
if not include_fcs:
|
||||
teams = [team for team in teams if team.get('category') == 'FBS']
|
||||
logger.info(f"Filtered to FBS teams only: {len(teams)} teams")
|
||||
|
||||
# Download missing logos
|
||||
downloaded_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for team in teams:
|
||||
abbreviation = team['abbreviation']
|
||||
display_name = team['display_name']
|
||||
logo_url = team['logo_url']
|
||||
category = team.get('category', 'Unknown')
|
||||
conference = team.get('conference', 'Unknown')
|
||||
|
||||
# Create filename
|
||||
filename = f"{self.normalize_abbreviation(abbreviation)}.png"
|
||||
filepath = Path(logo_dir) / filename
|
||||
|
||||
# Skip if already exists and not forcing download
|
||||
if filepath.exists() and not force_download:
|
||||
logger.debug(f"Skipping {display_name} ({category}, {conference}): {filename} already exists")
|
||||
continue
|
||||
|
||||
# Download logo
|
||||
if self.download_logo(logo_url, filepath, display_name):
|
||||
downloaded_count += 1
|
||||
logger.info(f"Downloaded {display_name} ({category}, {conference}) -> {filename}")
|
||||
else:
|
||||
failed_count += 1
|
||||
logger.warning(f"Failed to download {display_name} ({category}, {conference})")
|
||||
|
||||
# Small delay to be respectful to the API
|
||||
time.sleep(0.1)
|
||||
|
||||
logger.info(f"Comprehensive NCAA football logo download complete: {downloaded_count} downloaded, {failed_count} failed")
|
||||
return downloaded_count, failed_count
|
||||
|
||||
def download_missing_logo_for_team(self, team_abbreviation: str, league: str, team_name: str = None) -> bool:
|
||||
"""Download a specific team's logo if it's missing."""
|
||||
logo_dir = self.get_logo_directory(league)
|
||||
if not self.ensure_logo_directory(logo_dir):
|
||||
return False
|
||||
|
||||
filename = f"{self.normalize_abbreviation(team_abbreviation)}.png"
|
||||
filepath = Path(logo_dir) / filename
|
||||
|
||||
# Return True if logo already exists
|
||||
if filepath.exists():
|
||||
logger.debug(f"Logo already exists for {team_abbreviation}")
|
||||
return True
|
||||
|
||||
# Fetch team data to find the logo URL
|
||||
data = self.fetch_teams_data(league)
|
||||
if not data:
|
||||
return False
|
||||
|
||||
teams = self.extract_teams_from_data(data, league)
|
||||
|
||||
# Find the specific team with improved matching
|
||||
target_team = None
|
||||
normalized_search = self.normalize_abbreviation(team_abbreviation)
|
||||
|
||||
# First try exact match
|
||||
for team in teams:
|
||||
if team['abbreviation'].upper() == team_abbreviation.upper():
|
||||
target_team = team
|
||||
break
|
||||
|
||||
# If not found, try normalized match
|
||||
if not target_team:
|
||||
for team in teams:
|
||||
normalized_team_abbr = self.normalize_abbreviation(team['abbreviation'])
|
||||
if normalized_team_abbr == normalized_search:
|
||||
target_team = team
|
||||
break
|
||||
|
||||
# If still not found, try partial matching for common variations
|
||||
if not target_team:
|
||||
search_variations = self._get_team_name_variations(team_abbreviation)
|
||||
for team in teams:
|
||||
team_variations = self._get_team_name_variations(team['abbreviation'])
|
||||
if any(var in team_variations for var in search_variations):
|
||||
target_team = team
|
||||
logger.info(f"Found team {team_abbreviation} as {team['abbreviation']} ({team['display_name']})")
|
||||
break
|
||||
|
||||
if not target_team:
|
||||
logger.warning(f"Team {team_abbreviation} not found in {league} data")
|
||||
return False
|
||||
|
||||
# Download the logo
|
||||
success = self.download_logo(target_team['logo_url'], filepath, target_team['display_name'])
|
||||
if success:
|
||||
time.sleep(0.1) # Small delay
|
||||
return success
|
||||
|
||||
def download_all_missing_logos(self, leagues: List[str] = None, force_download: bool = False) -> Dict[str, Tuple[int, int]]:
|
||||
"""Download missing logos for all specified leagues."""
|
||||
if leagues is None:
|
||||
leagues = list(self.API_ENDPOINTS.keys())
|
||||
|
||||
results = {}
|
||||
total_downloaded = 0
|
||||
total_failed = 0
|
||||
|
||||
for league in leagues:
|
||||
if league not in self.API_ENDPOINTS:
|
||||
logger.warning(f"Skipping unknown league: {league}")
|
||||
continue
|
||||
|
||||
downloaded, failed = self.download_missing_logos_for_league(league, force_download)
|
||||
results[league] = (downloaded, failed)
|
||||
total_downloaded += downloaded
|
||||
total_failed += failed
|
||||
|
||||
logger.info(f"Overall logo download results: {total_downloaded} downloaded, {total_failed} failed")
|
||||
return results
|
||||
|
||||
def create_placeholder_logo(self, team_abbreviation: str, logo_dir: str, team_name: str = None) -> bool:
|
||||
"""Create a placeholder logo when real logo cannot be downloaded."""
|
||||
try:
|
||||
# Ensure the logo directory exists
|
||||
if not self.ensure_logo_directory(logo_dir):
|
||||
logger.error(f"Failed to create logo directory: {logo_dir}")
|
||||
return False
|
||||
|
||||
filename = f"{self.normalize_abbreviation(team_abbreviation)}.png"
|
||||
filepath = Path(logo_dir) / filename
|
||||
|
||||
# Check if we can write to the directory
|
||||
try:
|
||||
# Test write permissions by creating a temporary file
|
||||
test_file = filepath.parent / "test_write.tmp"
|
||||
test_file.touch()
|
||||
test_file.unlink() # Remove the test file
|
||||
except PermissionError:
|
||||
logger.error(f"Permission denied: Cannot write to directory {logo_dir}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"Directory access error for {logo_dir}: {e}")
|
||||
return False
|
||||
|
||||
# Create a simple placeholder logo
|
||||
logo = Image.new('RGBA', (64, 64), (100, 100, 100, 255)) # Gray background
|
||||
draw = ImageDraw.Draw(logo)
|
||||
|
||||
# Try to load a font, fallback to default
|
||||
try:
|
||||
font = ImageFont.truetype("assets/fonts/PressStart2P-Regular.ttf", 12)
|
||||
except:
|
||||
try:
|
||||
font = ImageFont.load_default()
|
||||
except:
|
||||
font = None
|
||||
|
||||
# Draw team abbreviation
|
||||
text = team_abbreviation
|
||||
if font:
|
||||
# Center the text
|
||||
bbox = draw.textbbox((0, 0), text, font=font)
|
||||
text_width = bbox[2] - bbox[0]
|
||||
text_height = bbox[3] - bbox[1]
|
||||
x = (64 - text_width) // 2
|
||||
y = (64 - text_height) // 2
|
||||
draw.text((x, y), text, font=font, fill=(255, 255, 255, 255))
|
||||
else:
|
||||
# Fallback without font
|
||||
draw.text((16, 24), text, fill=(255, 255, 255, 255))
|
||||
|
||||
logo.save(filepath)
|
||||
logger.info(f"Created placeholder logo for {team_abbreviation} at {filepath}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create placeholder logo for {team_abbreviation}: {e}")
|
||||
return False
|
||||
|
||||
def convert_image_to_rgba(self, filepath: Path) -> bool:
|
||||
"""Convert an image file to RGBA format to avoid PIL warnings."""
|
||||
try:
|
||||
with Image.open(filepath) as img:
|
||||
if img.mode != 'RGBA':
|
||||
# Convert to RGBA
|
||||
converted_img = img.convert('RGBA')
|
||||
converted_img.save(filepath, 'PNG')
|
||||
logger.debug(f"Converted {filepath.name} from {img.mode} to RGBA")
|
||||
return True
|
||||
else:
|
||||
logger.debug(f"{filepath.name} is already in RGBA format")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to convert {filepath.name} to RGBA: {e}")
|
||||
return False
|
||||
|
||||
def convert_all_logos_to_rgba(self, league: str) -> Tuple[int, int]:
|
||||
"""Convert all logos in a league directory to RGBA format."""
|
||||
logo_dir = Path(self.get_logo_directory(league))
|
||||
if not logo_dir.exists():
|
||||
logger.warning(f"Logo directory does not exist: {logo_dir}")
|
||||
return 0, 0
|
||||
|
||||
converted_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for logo_file in logo_dir.glob("*.png"):
|
||||
if self.convert_image_to_rgba(logo_file):
|
||||
converted_count += 1
|
||||
else:
|
||||
failed_count += 1
|
||||
|
||||
logger.info(f"Converted {converted_count} logos to RGBA format for {league}, {failed_count} failed")
|
||||
return converted_count, failed_count
|
||||
|
||||
|
||||
# Convenience function for easy integration
|
||||
def download_missing_logo(team_abbreviation: str, league: str, team_name: str = None, create_placeholder: bool = True) -> bool:
|
||||
"""
|
||||
Convenience function to download a missing team logo.
|
||||
|
||||
Args:
|
||||
team_abbreviation: Team abbreviation (e.g., 'UGA', 'BAMA', 'TA&M')
|
||||
league: League identifier (e.g., 'ncaa_fb', 'nfl')
|
||||
team_name: Optional team name for logging
|
||||
create_placeholder: Whether to create a placeholder if download fails
|
||||
|
||||
Returns:
|
||||
True if logo exists or was successfully downloaded, False otherwise
|
||||
"""
|
||||
downloader = LogoDownloader()
|
||||
|
||||
# Check if logo already exists
|
||||
logo_dir = downloader.get_logo_directory(league)
|
||||
filename = f"{downloader.normalize_abbreviation(team_abbreviation)}.png"
|
||||
filepath = Path(logo_dir) / filename
|
||||
|
||||
if filepath.exists():
|
||||
logger.debug(f"Logo already exists for {team_abbreviation} ({league})")
|
||||
return True
|
||||
|
||||
# Try to download the real logo first
|
||||
logger.info(f"Attempting to download logo for {team_abbreviation} ({team_name or 'Unknown'}) from {league}")
|
||||
success = downloader.download_missing_logo_for_team(team_abbreviation, league, team_name)
|
||||
|
||||
if not success and create_placeholder:
|
||||
logger.info(f"Creating placeholder logo for {team_abbreviation} ({team_name or 'Unknown'})")
|
||||
# Create placeholder as fallback
|
||||
success = downloader.create_placeholder_logo(team_abbreviation, logo_dir, team_name)
|
||||
|
||||
if success:
|
||||
logger.info(f"Successfully handled logo for {team_abbreviation} ({team_name or 'Unknown'})")
|
||||
else:
|
||||
logger.warning(f"Failed to download or create logo for {team_abbreviation} ({team_name or 'Unknown'})")
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def download_all_logos_for_league(league: str, force_download: bool = False) -> Tuple[int, int]:
|
||||
"""
|
||||
Convenience function to download all missing logos for a league.
|
||||
|
||||
Args:
|
||||
league: League identifier (e.g., 'ncaa_fb', 'nfl')
|
||||
force_download: Whether to re-download existing logos
|
||||
|
||||
Returns:
|
||||
Tuple of (downloaded_count, failed_count)
|
||||
"""
|
||||
downloader = LogoDownloader()
|
||||
return downloader.download_missing_logos_for_league(league, force_download)
|
||||
@@ -29,12 +29,14 @@ class BaseMLBManager:
|
||||
def __init__(self, config: Dict[str, Any], display_manager, cache_manager: CacheManager):
|
||||
self.config = config
|
||||
self.display_manager = display_manager
|
||||
# Store reference to config instead of creating new ConfigManager
|
||||
self.config_manager = None # Not used in this class
|
||||
self.mlb_config = config.get('mlb', {})
|
||||
self.show_odds = self.mlb_config.get("show_odds", False)
|
||||
self.favorite_teams = self.mlb_config.get('favorite_teams', [])
|
||||
self.show_records = self.mlb_config.get('show_records', False)
|
||||
self.cache_manager = cache_manager
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config_manager)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
||||
# Logo handling
|
||||
|
||||
@@ -6,7 +6,7 @@ import json
|
||||
import os
|
||||
from io import BytesIO
|
||||
import requests
|
||||
from typing import Union
|
||||
from typing import Union, Dict, Any, Optional
|
||||
from PIL import Image, ImageEnhance
|
||||
import queue # Added import
|
||||
|
||||
@@ -15,6 +15,14 @@ from .spotify_client import SpotifyClient
|
||||
from .ytm_client import YTMClient
|
||||
# Removed: import config
|
||||
|
||||
# Import the API counter function from web interface
|
||||
try:
|
||||
from web_interface_v2 import increment_api_counter
|
||||
except ImportError:
|
||||
# Fallback if web interface is not available
|
||||
def increment_api_counter(kind: str, count: int = 1):
|
||||
pass
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -313,6 +321,10 @@ class MusicManager:
|
||||
try:
|
||||
response = requests.get(url, timeout=5) # 5-second timeout for image download
|
||||
response.raise_for_status() # Raise an exception for bad status codes
|
||||
|
||||
# Increment API counter for music data
|
||||
increment_api_counter('music', 1)
|
||||
|
||||
img_data = BytesIO(response.content)
|
||||
img = Image.open(img_data)
|
||||
|
||||
|
||||
@@ -43,10 +43,11 @@ class BaseNBAManager:
|
||||
|
||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||
self.display_manager = display_manager
|
||||
self.config_manager = ConfigManager()
|
||||
# Store reference to config instead of creating new ConfigManager
|
||||
self.config_manager = None # Not used in this class
|
||||
self.config = config
|
||||
self.cache_manager = cache_manager
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
||||
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.nba_config = config.get("nba_scoreboard", {})
|
||||
self.is_enabled = self.nba_config.get("enabled", False)
|
||||
@@ -63,13 +64,9 @@ class BaseNBAManager:
|
||||
# Set logging level to INFO to reduce noise
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
||||
# Get display dimensions from config
|
||||
display_config = config.get("display", {})
|
||||
hardware_config = display_config.get("hardware", {})
|
||||
cols = hardware_config.get("cols", 64)
|
||||
chain = hardware_config.get("chain_length", 1)
|
||||
self.display_width = int(cols * chain)
|
||||
self.display_height = hardware_config.get("rows", 32)
|
||||
# Get display dimensions from matrix
|
||||
self.display_width = self.display_manager.matrix.width
|
||||
self.display_height = self.display_manager.matrix.height
|
||||
|
||||
# Cache for loaded logos
|
||||
self._logo_cache = {}
|
||||
@@ -79,7 +76,8 @@ class BaseNBAManager:
|
||||
|
||||
def _get_timezone(self):
|
||||
try:
|
||||
return pytz.timezone(self.config_manager.get_timezone())
|
||||
timezone_str = self.config.get('timezone', 'UTC')
|
||||
return pytz.timezone(timezone_str)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.utc
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@ class BaseNCAABaseballManager:
|
||||
def __init__(self, config: Dict[str, Any], display_manager, cache_manager: CacheManager):
|
||||
self.config = config
|
||||
self.display_manager = display_manager
|
||||
# Store reference to config instead of creating new ConfigManager
|
||||
self.config_manager = None # Not used in this class
|
||||
self.ncaa_baseball_config = config.get('ncaa_baseball_scoreboard', {})
|
||||
self.show_odds = self.ncaa_baseball_config.get('show_odds', False)
|
||||
self.show_records = self.ncaa_baseball_config.get('show_records', False)
|
||||
self.favorite_teams = self.ncaa_baseball_config.get('favorite_teams', [])
|
||||
self.cache_manager = cache_manager
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config_manager)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.logger.setLevel(logging.DEBUG) # Set logger level to DEBUG
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ from src.display_manager import DisplayManager
|
||||
from src.cache_manager import CacheManager # Keep CacheManager import
|
||||
from src.config_manager import ConfigManager
|
||||
from src.odds_manager import OddsManager
|
||||
from src.logo_downloader import download_missing_logo
|
||||
import pytz
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util.retry import Retry
|
||||
@@ -53,6 +54,7 @@ class BaseNCAAFBManager: # Renamed class
|
||||
self.logo_dir = self.ncaa_fb_config.get("logo_dir", "assets/sports/ncaa_fbs_logos") # Changed logo dir
|
||||
self.update_interval = self.ncaa_fb_config.get("update_interval_seconds", 60)
|
||||
self.show_records = self.ncaa_fb_config.get('show_records', False)
|
||||
self.show_ranking = self.ncaa_fb_config.get('show_ranking', False)
|
||||
self.season_cache_duration = self.ncaa_fb_config.get("season_cache_duration_seconds", 86400) # 24 hours default
|
||||
# Number of games to show (instead of time-based windows)
|
||||
self.recent_games_to_show = self.ncaa_fb_config.get("recent_games_to_show", 5) # Show last 5 games
|
||||
@@ -91,22 +93,66 @@ class BaseNCAAFBManager: # Renamed class
|
||||
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
||||
display_config = config.get("display", {})
|
||||
hardware_config = display_config.get("hardware", {})
|
||||
cols = hardware_config.get("cols", 64)
|
||||
chain = hardware_config.get("chain_length", 1)
|
||||
self.display_width = int(cols * chain)
|
||||
self.display_height = hardware_config.get("rows", 32)
|
||||
self.display_width = self.display_manager.matrix.width
|
||||
self.display_height = self.display_manager.matrix.height
|
||||
|
||||
self._logo_cache = {}
|
||||
|
||||
# Initialize team rankings cache
|
||||
self._team_rankings_cache = {}
|
||||
self._rankings_cache_timestamp = 0
|
||||
self._rankings_cache_duration = 3600 # Cache rankings for 1 hour
|
||||
|
||||
self.logger.info(f"Initialized NCAAFB manager with display dimensions: {self.display_width}x{self.display_height}")
|
||||
self.logger.info(f"Logo directory: {self.logo_dir}")
|
||||
self.logger.info(f"Display modes - Recent: {self.recent_enabled}, Upcoming: {self.upcoming_enabled}, Live: {self.live_enabled}")
|
||||
|
||||
def _fetch_team_rankings(self) -> Dict[str, int]:
|
||||
"""Fetch current team rankings from ESPN API."""
|
||||
current_time = time.time()
|
||||
|
||||
# Check if we have cached rankings that are still valid
|
||||
if (self._team_rankings_cache and
|
||||
current_time - self._rankings_cache_timestamp < self._rankings_cache_duration):
|
||||
return self._team_rankings_cache
|
||||
|
||||
try:
|
||||
rankings_url = "https://site.api.espn.com/apis/site/v2/sports/football/college-football/rankings"
|
||||
response = self.session.get(rankings_url, headers=self.headers, timeout=30)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
rankings = {}
|
||||
rankings_data = data.get('rankings', [])
|
||||
|
||||
if rankings_data:
|
||||
# Use the first ranking (usually AP Top 25)
|
||||
first_ranking = rankings_data[0]
|
||||
teams = first_ranking.get('ranks', [])
|
||||
|
||||
for team_data in teams:
|
||||
team_info = team_data.get('team', {})
|
||||
team_abbr = team_info.get('abbreviation', '')
|
||||
current_rank = team_data.get('current', 0)
|
||||
|
||||
if team_abbr and current_rank > 0:
|
||||
rankings[team_abbr] = current_rank
|
||||
|
||||
# Cache the results
|
||||
self._team_rankings_cache = rankings
|
||||
self._rankings_cache_timestamp = current_time
|
||||
|
||||
self.logger.debug(f"Fetched rankings for {len(rankings)} teams")
|
||||
return rankings
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error fetching team rankings: {e}")
|
||||
return {}
|
||||
|
||||
def _get_timezone(self):
|
||||
try:
|
||||
return pytz.timezone(self.config_manager.get_timezone())
|
||||
timezone_str = self.config.get('timezone', 'UTC')
|
||||
return pytz.timezone(timezone_str)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.utc
|
||||
|
||||
@@ -313,39 +359,8 @@ class BaseNCAAFBManager: # Renamed class
|
||||
draw.text((x + dx, y + dy), text, font=font, fill=outline_color)
|
||||
draw.text((x, y), text, font=font, fill=fill)
|
||||
|
||||
def _download_team_logo(self, team_id):
|
||||
# Base API URL with placeholder for team ID
|
||||
API_URL_TEMPLATE = "http://site.api.espn.com/apis/site/v2/sports/football/college-football/teams/{}"
|
||||
|
||||
url = API_URL_TEMPLATE.format(team_id)
|
||||
response = requests.get(url)
|
||||
|
||||
if response.status_code != 200:
|
||||
print(f"Failed to fetch data for team ID {team_id}. Status code: {response.status_code}")
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
team = data.get("team", {})
|
||||
logos = team.get("logos", [])
|
||||
|
||||
if logos:
|
||||
logo_url = logos[0].get("href")
|
||||
if logo_url:
|
||||
try:
|
||||
img_data = requests.get(logo_url).content
|
||||
file_path = f"{self.logo_dir}/{team_id}.png"
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(img_data)
|
||||
print(f"Saved logo for {team_id} as {file_path}")
|
||||
except Exception as e:
|
||||
print(f"Error downloading logo for {team_id}: {e}")
|
||||
else:
|
||||
print(f"No logo URL found for team ID {team_id}")
|
||||
else:
|
||||
print(f"No logos found for team ID {team_id}")
|
||||
|
||||
def _load_and_resize_logo(self, team_abbrev: str) -> Optional[Image.Image]:
|
||||
"""Load and resize a team logo, with caching."""
|
||||
def _load_and_resize_logo(self, team_abbrev: str, team_name: str = None) -> Optional[Image.Image]:
|
||||
"""Load and resize a team logo, with caching and automatic download if missing."""
|
||||
if team_abbrev in self._logo_cache:
|
||||
return self._logo_cache[team_abbrev]
|
||||
|
||||
@@ -353,20 +368,22 @@ class BaseNCAAFBManager: # Renamed class
|
||||
self.logger.debug(f"Logo path: {logo_path}")
|
||||
|
||||
try:
|
||||
# Try to download team logo
|
||||
# Try to download missing logo first
|
||||
if not os.path.exists(logo_path):
|
||||
self.logger.warning(f"Logo not found for {team_abbrev} at {logo_path}. Attempting to download.")
|
||||
self._download_team_logo(team_abbrev)
|
||||
|
||||
# Check to make sure logo was able to be downloaded and saved. If not, create a placeholder.
|
||||
if not os.path.exists(logo_path):
|
||||
self.logger.warning(f"Error occured donwloading logo for {team_abbrev} at {logo_path}. Creating placeholder.")
|
||||
os.makedirs(os.path.dirname(logo_path), exist_ok=True)
|
||||
logo = Image.new('RGBA', (32, 32), (200, 200, 200, 255)) # Gray placeholder
|
||||
draw = ImageDraw.Draw(logo)
|
||||
draw.text((2, 10), team_abbrev, fill=(0, 0, 0, 255))
|
||||
logo.save(logo_path)
|
||||
self.logger.info(f"Created placeholder logo at {logo_path}")
|
||||
self.logger.info(f"Logo not found for {team_abbrev} at {logo_path}. Attempting to download.")
|
||||
|
||||
# Try to download the logo from ESPN API
|
||||
success = download_missing_logo(team_abbrev, 'ncaa_fb', team_name)
|
||||
|
||||
if not success:
|
||||
# Create placeholder if download fails
|
||||
self.logger.warning(f"Failed to download logo for {team_abbrev}. Creating placeholder.")
|
||||
os.makedirs(os.path.dirname(logo_path), exist_ok=True)
|
||||
logo = Image.new('RGBA', (32, 32), (200, 200, 200, 255)) # Gray placeholder
|
||||
draw = ImageDraw.Draw(logo)
|
||||
draw.text((2, 10), team_abbrev, fill=(0, 0, 0, 255))
|
||||
logo.save(logo_path)
|
||||
self.logger.info(f"Created placeholder logo at {logo_path}")
|
||||
|
||||
logo = Image.open(logo_path)
|
||||
if logo.mode != 'RGBA':
|
||||
@@ -775,8 +792,8 @@ class NCAAFBLiveManager(BaseNCAAFBManager): # Renamed class
|
||||
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
||||
draw_overlay = ImageDraw.Draw(overlay) # Draw text elements on overlay first
|
||||
|
||||
home_logo = self._load_and_resize_logo(game["home_abbr"])
|
||||
away_logo = self._load_and_resize_logo(game["away_abbr"])
|
||||
home_logo = self._load_and_resize_logo(game["home_abbr"], game.get("home_team_name"))
|
||||
away_logo = self._load_and_resize_logo(game["away_abbr"], game.get("away_team_name"))
|
||||
|
||||
if not home_logo or not away_logo:
|
||||
self.logger.error(f"[NCAAFB] Failed to load logos for live game: {game.get('id')}") # Changed log prefix
|
||||
@@ -1002,8 +1019,8 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class
|
||||
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
||||
draw_overlay = ImageDraw.Draw(overlay)
|
||||
|
||||
home_logo = self._load_and_resize_logo(game["home_abbr"])
|
||||
away_logo = self._load_and_resize_logo(game["away_abbr"])
|
||||
home_logo = self._load_and_resize_logo(game["home_abbr"], game.get("home_team_name"))
|
||||
away_logo = self._load_and_resize_logo(game["away_abbr"], game.get("away_team_name"))
|
||||
|
||||
if not home_logo or not away_logo:
|
||||
self.logger.error(f"[NCAAFB Recent] Failed to load logos for game: {game.get('id')}") # Changed log prefix
|
||||
@@ -1046,29 +1063,72 @@ class NCAAFBRecentManager(BaseNCAAFBManager): # Renamed class
|
||||
if 'odds' in game and game['odds']:
|
||||
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height)
|
||||
|
||||
# Draw records if enabled
|
||||
if self.show_records:
|
||||
# Draw records or rankings if enabled
|
||||
if self.show_records or self.show_ranking:
|
||||
try:
|
||||
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
|
||||
except IOError:
|
||||
record_font = ImageFont.load_default()
|
||||
|
||||
away_record = game.get('away_record', '')
|
||||
home_record = game.get('home_record', '')
|
||||
# Get team abbreviations
|
||||
away_abbr = game.get('away_abbr', '')
|
||||
home_abbr = game.get('home_abbr', '')
|
||||
|
||||
record_bbox = draw_overlay.textbbox((0,0), "0-0", font=record_font)
|
||||
record_height = record_bbox[3] - record_bbox[1]
|
||||
record_y = self.display_height - record_height
|
||||
|
||||
if away_record:
|
||||
away_record_x = 0
|
||||
self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font)
|
||||
# Display away team info
|
||||
if away_abbr:
|
||||
if self.show_ranking:
|
||||
# Show ranking if available
|
||||
rankings = self._fetch_team_rankings()
|
||||
away_rank = rankings.get(away_abbr, 0)
|
||||
if away_rank > 0:
|
||||
away_text = f"#{away_rank}"
|
||||
elif self.show_records:
|
||||
# Only show record if show_records is enabled
|
||||
away_text = game.get('away_record', '')
|
||||
else:
|
||||
# Show nothing if show_records is false and team is unranked
|
||||
away_text = ''
|
||||
else:
|
||||
# Show record only if show_records is enabled
|
||||
if self.show_records:
|
||||
away_text = game.get('away_record', '')
|
||||
else:
|
||||
away_text = ''
|
||||
|
||||
if away_text:
|
||||
away_record_x = 0
|
||||
self._draw_text_with_outline(draw_overlay, away_text, (away_record_x, record_y), record_font)
|
||||
|
||||
if home_record:
|
||||
home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font)
|
||||
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
||||
home_record_x = self.display_width - home_record_width
|
||||
self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font)
|
||||
# Display home team info
|
||||
if home_abbr:
|
||||
if self.show_ranking:
|
||||
# Show ranking if available
|
||||
rankings = self._fetch_team_rankings()
|
||||
home_rank = rankings.get(home_abbr, 0)
|
||||
if home_rank > 0:
|
||||
home_text = f"#{home_rank}"
|
||||
elif self.show_records:
|
||||
# Only show record if show_records is enabled
|
||||
home_text = game.get('home_record', '')
|
||||
else:
|
||||
# Show nothing if show_records is false and team is unranked
|
||||
home_text = ''
|
||||
else:
|
||||
# Show record only if show_records is enabled
|
||||
if self.show_records:
|
||||
home_text = game.get('home_record', '')
|
||||
else:
|
||||
home_text = ''
|
||||
|
||||
if home_text:
|
||||
home_record_bbox = draw_overlay.textbbox((0,0), home_text, font=record_font)
|
||||
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
||||
home_record_x = self.display_width - home_record_width
|
||||
self._draw_text_with_outline(draw_overlay, home_text, (home_record_x, record_y), record_font)
|
||||
|
||||
# Composite and display
|
||||
main_img = Image.alpha_composite(main_img, overlay)
|
||||
@@ -1234,8 +1294,8 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class
|
||||
overlay = Image.new('RGBA', (self.display_width, self.display_height), (0, 0, 0, 0))
|
||||
draw_overlay = ImageDraw.Draw(overlay)
|
||||
|
||||
home_logo = self._load_and_resize_logo(game["home_abbr"])
|
||||
away_logo = self._load_and_resize_logo(game["away_abbr"])
|
||||
home_logo = self._load_and_resize_logo(game["home_abbr"], game.get("home_team_name"))
|
||||
away_logo = self._load_and_resize_logo(game["away_abbr"], game.get("away_team_name"))
|
||||
|
||||
if not home_logo or not away_logo:
|
||||
self.logger.error(f"[NCAAFB Upcoming] Failed to load logos for game: {game.get('id')}") # Changed log prefix
|
||||
@@ -1284,29 +1344,72 @@ class NCAAFBUpcomingManager(BaseNCAAFBManager): # Renamed class
|
||||
if 'odds' in game and game['odds']:
|
||||
self._draw_dynamic_odds(draw_overlay, game['odds'], self.display_width, self.display_height)
|
||||
|
||||
# Draw records if enabled
|
||||
if self.show_records:
|
||||
# Draw records or rankings if enabled
|
||||
if self.show_records or self.show_ranking:
|
||||
try:
|
||||
record_font = ImageFont.truetype("assets/fonts/4x6-font.ttf", 6)
|
||||
except IOError:
|
||||
record_font = ImageFont.load_default()
|
||||
|
||||
away_record = game.get('away_record', '')
|
||||
home_record = game.get('home_record', '')
|
||||
# Get team abbreviations
|
||||
away_abbr = game.get('away_abbr', '')
|
||||
home_abbr = game.get('home_abbr', '')
|
||||
|
||||
record_bbox = draw_overlay.textbbox((0,0), "0-0", font=record_font)
|
||||
record_height = record_bbox[3] - record_bbox[1]
|
||||
record_y = self.display_height - record_height
|
||||
|
||||
if away_record:
|
||||
away_record_x = 0
|
||||
self._draw_text_with_outline(draw_overlay, away_record, (away_record_x, record_y), record_font)
|
||||
# Display away team info
|
||||
if away_abbr:
|
||||
if self.show_ranking:
|
||||
# Show ranking if available
|
||||
rankings = self._fetch_team_rankings()
|
||||
away_rank = rankings.get(away_abbr, 0)
|
||||
if away_rank > 0:
|
||||
away_text = f"#{away_rank}"
|
||||
elif self.show_records:
|
||||
# Only show record if show_records is enabled
|
||||
away_text = game.get('away_record', '')
|
||||
else:
|
||||
# Show nothing if show_records is false and team is unranked
|
||||
away_text = ''
|
||||
else:
|
||||
# Show record only if show_records is enabled
|
||||
if self.show_records:
|
||||
away_text = game.get('away_record', '')
|
||||
else:
|
||||
away_text = ''
|
||||
|
||||
if away_text:
|
||||
away_record_x = 0
|
||||
self._draw_text_with_outline(draw_overlay, away_text, (away_record_x, record_y), record_font)
|
||||
|
||||
if home_record:
|
||||
home_record_bbox = draw_overlay.textbbox((0,0), home_record, font=record_font)
|
||||
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
||||
home_record_x = self.display_width - home_record_width
|
||||
self._draw_text_with_outline(draw_overlay, home_record, (home_record_x, record_y), record_font)
|
||||
# Display home team info
|
||||
if home_abbr:
|
||||
if self.show_ranking:
|
||||
# Show ranking if available
|
||||
rankings = self._fetch_team_rankings()
|
||||
home_rank = rankings.get(home_abbr, 0)
|
||||
if home_rank > 0:
|
||||
home_text = f"#{home_rank}"
|
||||
elif self.show_records:
|
||||
# Only show record if show_records is enabled
|
||||
home_text = game.get('home_record', '')
|
||||
else:
|
||||
# Show nothing if show_records is false and team is unranked
|
||||
home_text = ''
|
||||
else:
|
||||
# Show record only if show_records is enabled
|
||||
if self.show_records:
|
||||
home_text = game.get('home_record', '')
|
||||
else:
|
||||
home_text = ''
|
||||
|
||||
if home_text:
|
||||
home_record_bbox = draw_overlay.textbbox((0,0), home_text, font=record_font)
|
||||
home_record_width = home_record_bbox[2] - home_record_bbox[0]
|
||||
home_record_x = self.display_width - home_record_width
|
||||
self._draw_text_with_outline(draw_overlay, home_text, (home_record_x, record_y), record_font)
|
||||
|
||||
# Composite and display
|
||||
main_img = Image.alpha_composite(main_img, overlay)
|
||||
|
||||
@@ -13,6 +13,14 @@ from src.config_manager import ConfigManager
|
||||
from src.odds_manager import OddsManager
|
||||
import pytz
|
||||
|
||||
# Import the API counter function from web interface
|
||||
try:
|
||||
from web_interface_v2 import increment_api_counter
|
||||
except ImportError:
|
||||
# Fallback if web interface is not available
|
||||
def increment_api_counter(kind: str, count: int = 1):
|
||||
pass
|
||||
|
||||
# Constants
|
||||
ESPN_NCAAMB_SCOREBOARD_URL = "https://site.api.espn.com/apis/site/v2/sports/basketball/mens-college-basketball/scoreboard"
|
||||
|
||||
@@ -35,10 +43,11 @@ class BaseNCAAMBasketballManager:
|
||||
|
||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||
self.display_manager = display_manager
|
||||
self.config_manager = ConfigManager()
|
||||
# Store reference to config instead of creating new ConfigManager
|
||||
self.config_manager = None # Not used in this class
|
||||
self.config = config
|
||||
self.cache_manager = cache_manager
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
||||
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.ncaam_basketball_config = config.get("ncaam_basketball_scoreboard", {})
|
||||
self.is_enabled = self.ncaam_basketball_config.get("enabled", False)
|
||||
@@ -55,13 +64,9 @@ class BaseNCAAMBasketballManager:
|
||||
# Set logging level to INFO to reduce noise
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
||||
# Get display dimensions from config
|
||||
display_config = config.get("display", {})
|
||||
hardware_config = display_config.get("hardware", {})
|
||||
cols = hardware_config.get("cols", 64)
|
||||
chain = hardware_config.get("chain_length", 1)
|
||||
self.display_width = int(cols * chain)
|
||||
self.display_height = hardware_config.get("rows", 32)
|
||||
# Get display dimensions from matrix
|
||||
self.display_width = self.display_manager.matrix.width
|
||||
self.display_height = self.display_manager.matrix.height
|
||||
|
||||
# Cache for loaded logos
|
||||
self._logo_cache = {}
|
||||
@@ -99,7 +104,8 @@ class BaseNCAAMBasketballManager:
|
||||
|
||||
def _get_timezone(self):
|
||||
try:
|
||||
return pytz.timezone(self.config_manager.get_timezone())
|
||||
timezone_str = self.config.get('timezone', 'UTC')
|
||||
return pytz.timezone(timezone_str)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.utc
|
||||
|
||||
@@ -328,6 +334,9 @@ class BaseNCAAMBasketballManager:
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# Increment API counter for sports data
|
||||
increment_api_counter('sports', 1)
|
||||
|
||||
if use_cache:
|
||||
self.cache_manager.set(cache_key, data)
|
||||
|
||||
|
||||
@@ -29,9 +29,10 @@ logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class NewsManager:
|
||||
def __init__(self, config: Dict[str, Any], display_manager):
|
||||
def __init__(self, config: Dict[str, Any], display_manager, config_manager=None):
|
||||
self.config = config
|
||||
self.config_manager = ConfigManager()
|
||||
# Use provided config_manager or create a new one if none provided
|
||||
self.config_manager = config_manager or ConfigManager()
|
||||
self.display_manager = display_manager
|
||||
self.news_config = config.get('news_manager', {})
|
||||
self.last_update = time.time() # Initialize to current time
|
||||
|
||||
@@ -37,10 +37,11 @@ class BaseNFLManager: # Renamed class
|
||||
|
||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||
self.display_manager = display_manager
|
||||
self.config_manager = ConfigManager()
|
||||
# Store reference to config instead of creating new ConfigManager
|
||||
self.config_manager = None # Not used in this class
|
||||
self.config = config
|
||||
self.cache_manager = cache_manager
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
||||
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.nfl_config = config.get("nfl_scoreboard", {}) # Changed config key
|
||||
self.is_enabled = self.nfl_config.get("enabled", False)
|
||||
@@ -62,12 +63,8 @@ class BaseNFLManager: # Renamed class
|
||||
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
||||
display_config = config.get("display", {})
|
||||
hardware_config = display_config.get("hardware", {})
|
||||
cols = hardware_config.get("cols", 64)
|
||||
chain = hardware_config.get("chain_length", 1)
|
||||
self.display_width = int(cols * chain)
|
||||
self.display_height = hardware_config.get("rows", 32)
|
||||
self.display_width = self.display_manager.matrix.width
|
||||
self.display_height = self.display_manager.matrix.height
|
||||
|
||||
self._logo_cache = {}
|
||||
|
||||
@@ -87,7 +84,8 @@ class BaseNFLManager: # Renamed class
|
||||
|
||||
def _get_timezone(self):
|
||||
try:
|
||||
return pytz.timezone(self.config_manager.get_timezone())
|
||||
timezone_str = self.config.get('timezone', 'UTC')
|
||||
return pytz.timezone(timezone_str)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.utc
|
||||
|
||||
|
||||
@@ -42,10 +42,11 @@ class BaseNHLManager:
|
||||
|
||||
def __init__(self, config: Dict[str, Any], display_manager: DisplayManager, cache_manager: CacheManager):
|
||||
self.display_manager = display_manager
|
||||
self.config_manager = ConfigManager()
|
||||
# Store reference to config instead of creating new ConfigManager
|
||||
self.config_manager = None # Not used in this class
|
||||
self.config = config
|
||||
self.cache_manager = cache_manager
|
||||
self.odds_manager = OddsManager(self.cache_manager, self.config)
|
||||
self.odds_manager = OddsManager(self.cache_manager, None)
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.nhl_config = config.get("nhl_scoreboard", {})
|
||||
self.is_enabled = self.nhl_config.get("enabled", False)
|
||||
@@ -63,13 +64,9 @@ class BaseNHLManager:
|
||||
# Set logging level to DEBUG to see all messages
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Get display dimensions from config
|
||||
display_config = config.get("display", {})
|
||||
hardware_config = display_config.get("hardware", {})
|
||||
cols = hardware_config.get("cols", 64)
|
||||
chain = hardware_config.get("chain_length", 1)
|
||||
self.display_width = int(cols * chain)
|
||||
self.display_height = hardware_config.get("rows", 32)
|
||||
# Get display dimensions from matrix
|
||||
self.display_width = self.display_manager.matrix.width
|
||||
self.display_height = self.display_manager.matrix.height
|
||||
|
||||
# Cache for loaded logos
|
||||
self._logo_cache = {}
|
||||
@@ -107,7 +104,8 @@ class BaseNHLManager:
|
||||
|
||||
def _get_timezone(self):
|
||||
try:
|
||||
return pytz.timezone(self.config_manager.get_timezone())
|
||||
timezone_str = self.config.get('timezone', 'UTC')
|
||||
return pytz.timezone(timezone_str)
|
||||
except pytz.UnknownTimeZoneError:
|
||||
return pytz.utc
|
||||
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import requests
|
||||
import time
|
||||
import logging
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from src.cache_manager import CacheManager
|
||||
from src.config_manager import ConfigManager
|
||||
from typing import Optional, List, Dict, Any
|
||||
import pytz
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
# Import the API counter function from web interface
|
||||
try:
|
||||
from web_interface_v2 import increment_api_counter
|
||||
except ImportError:
|
||||
# Fallback if web interface is not available
|
||||
def increment_api_counter(kind: str, count: int = 1):
|
||||
pass
|
||||
|
||||
class OddsManager:
|
||||
def __init__(self, cache_manager: CacheManager, config_manager: ConfigManager):
|
||||
def __init__(self, cache_manager: CacheManager, config_manager=None):
|
||||
self.cache_manager = cache_manager
|
||||
self.config_manager = config_manager
|
||||
self.logger = logging.getLogger(__name__)
|
||||
@@ -31,6 +40,9 @@ class OddsManager:
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
raw_data = response.json()
|
||||
|
||||
# Increment API counter for odds data
|
||||
increment_api_counter('odds', 1)
|
||||
self.logger.debug(f"Received raw odds data from ESPN: {json.dumps(raw_data, indent=2)}")
|
||||
|
||||
odds_data = self._extract_espn_data(raw_data)
|
||||
|
||||