2019-11-02 17:27:54 -05:00
|
|
|
import os
|
|
|
|
import time
|
|
|
|
import datetime
|
|
|
|
from pandac.PandaModules import Filename, DSearchPath, TextNode
|
|
|
|
from pandac.PandaModules import HTTPClient, Ramfile, DocumentSpec
|
|
|
|
from direct.showbase import DirectObject
|
|
|
|
from direct.gui.DirectGui import DirectFrame, DGG
|
|
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
|
|
from direct.task.Task import Task
|
|
|
|
from direct.showbase import AppRunnerGlobal
|
|
|
|
from toontown.shtiker import IssueFrame
|
|
|
|
from toontown.shtiker import IssueFrameV2
|
|
|
|
from toontown.toonbase import TTLocalizer
|
|
|
|
|
|
|
|
class DirectNewsFrame(DirectObject.DirectObject):
|
|
|
|
TaskName = 'HtmlViewUpdateTask'
|
|
|
|
TaskChainName = 'RedownladTaskChain'
|
|
|
|
RedownloadTaskName = 'RedownloadNewsTask'
|
|
|
|
NewsBaseDir = config.GetString('news-base-dir', '/httpNews')
|
|
|
|
NewsStageDir = config.GetString('news-stage-dir', 'news')
|
|
|
|
FrameDimensions = (-1.30666637421,
|
|
|
|
1.30666637421,
|
|
|
|
-0.751666665077,
|
|
|
|
0.751666665077)
|
|
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DirectNewsFrame')
|
|
|
|
NewsIndexFilename = config.GetString('news-index-filename', 'http_news_index.txt')
|
|
|
|
NewsOverHttp = config.GetBool('news-over-http', True)
|
|
|
|
CacheIndexFilename = 'cache_index.txt'
|
|
|
|
SectionIdents = ['hom',
|
|
|
|
'new',
|
|
|
|
'evt',
|
|
|
|
'tot',
|
|
|
|
'att',
|
|
|
|
'tnr']
|
|
|
|
|
|
|
|
def __init__(self, parent = aspect2d):
|
|
|
|
DirectObject.DirectObject.__init__(self)
|
|
|
|
self.accept('newsSnapshot', self.doSnapshot)
|
|
|
|
self.active = False
|
|
|
|
self.parent = parent
|
|
|
|
self.issues = []
|
|
|
|
self.accept('newsChangeWeek', self.changeWeek)
|
|
|
|
self.curIssueIndex = 0
|
|
|
|
self.strFilenames = None
|
|
|
|
self.redownloadingNews = False
|
|
|
|
self.startRedownload = datetime.datetime.now()
|
|
|
|
self.endRedownload = datetime.datetime.now()
|
|
|
|
self.load()
|
|
|
|
self.percentDownloaded = 0.0
|
|
|
|
self.numIssuesExpected = 0
|
|
|
|
self.needsParseNews = True
|
|
|
|
self.newsIndexEntries = []
|
|
|
|
if self.NewsOverHttp:
|
|
|
|
self.redownloadNews()
|
|
|
|
self.accept('newIssueOut', self.handleNewIssueOut)
|
|
|
|
self.accept('clientCleanup', self.handleClientCleanup)
|
|
|
|
return
|
|
|
|
|
|
|
|
def parseNewsContent(self):
|
|
|
|
if not self.needsParseNews:
|
|
|
|
return
|
|
|
|
self.needsParseNews = False
|
|
|
|
result = False
|
|
|
|
newsDir = self.findNewsDir()
|
|
|
|
if newsDir:
|
|
|
|
allHomeFiles = self.getAllHomeFilenames(newsDir)
|
|
|
|
self.notify.debug('len allHomeFiles = %s' % len(allHomeFiles))
|
|
|
|
self.numIssuesExpected = len(allHomeFiles)
|
|
|
|
if allHomeFiles:
|
|
|
|
for myIssueIndex, oneHomeFile in enumerate(allHomeFiles):
|
|
|
|
if type(oneHomeFile) == type(''):
|
|
|
|
justFilename = oneHomeFile
|
|
|
|
else:
|
|
|
|
justFilename = oneHomeFile.getFilename().getBasename()
|
|
|
|
self.notify.debug('parseNewContent %s' % justFilename)
|
|
|
|
parts = justFilename.split('_')
|
|
|
|
dateStr = parts[3]
|
|
|
|
majorVer, minorVer = self.calcIssueVersion(dateStr)
|
|
|
|
if majorVer == 1:
|
|
|
|
oneIssue = IssueFrame.IssueFrame(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames)
|
|
|
|
elif majorVer == 2:
|
|
|
|
oneIssue = IssueFrameV2.IssueFrameV2(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries)
|
|
|
|
else:
|
|
|
|
self.notify.warning('Dont know how to handle version %s, asuming v2' % majorVer)
|
|
|
|
oneIssue = IssueFrameV2.IssueFrameV2(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries)
|
|
|
|
oneIssue.hide()
|
|
|
|
self.issues.append(oneIssue)
|
|
|
|
|
|
|
|
if self.issues:
|
|
|
|
self.issues[-1].show()
|
|
|
|
self.curIssueIndex = len(self.issues) - 1
|
|
|
|
result = True
|
|
|
|
if hasattr(base.cr, 'inGameNewsMgr') and base.cr.inGameNewsMgr:
|
|
|
|
self.createdTime = base.cr.inGameNewsMgr.getLatestIssue()
|
|
|
|
self.notify.debug('setting created time to latest issue %s' % self.createdTime)
|
|
|
|
else:
|
|
|
|
self.createdTime = base.cr.toontownTimeManager.getCurServerDateTime()
|
|
|
|
self.notify.debug('setting created time cur server time %s' % self.createdTime)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def getAllHomeFilenames(self, newsDir):
|
|
|
|
self.notify.debug('getAllHomeFilenames')
|
|
|
|
newsDirAsFile = vfs.getFile(Filename(newsDir))
|
|
|
|
fileList = newsDirAsFile.scanDirectory()
|
|
|
|
fileNames = fileList.getFiles()
|
2019-11-17 15:57:18 -06:00
|
|
|
self.notify.debug('filenames=%s' % str(fileNames))
|
2019-11-02 17:27:54 -05:00
|
|
|
homeFileNames = set([])
|
|
|
|
for name in fileNames:
|
|
|
|
self.notify.debug('processing %s' % name)
|
|
|
|
baseName = name.getFilename().getBasename()
|
|
|
|
self.notify.debug('baseName=%s' % baseName)
|
|
|
|
if 'hom1.' in baseName:
|
|
|
|
homeFileNames.add(name)
|
|
|
|
else:
|
|
|
|
self.notify.debug('hom1. not in baseName')
|
|
|
|
|
|
|
|
if not homeFileNames:
|
|
|
|
self.notify.warning('couldnt find hom1. in %s' % fileNames)
|
|
|
|
self.setErrorMessage(TTLocalizer.NewsPageNoIssues)
|
|
|
|
return []
|
|
|
|
|
|
|
|
def fileCmp(fileA, fileB):
|
|
|
|
return fileA.getFilename().compareTo(fileB.getFilename())
|
|
|
|
|
|
|
|
homeFileNames = list(homeFileNames)
|
|
|
|
homeFileNames.sort(cmp=fileCmp)
|
|
|
|
self.notify.debug('returned homeFileNames=%s' % homeFileNames)
|
|
|
|
return homeFileNames
|
|
|
|
|
|
|
|
def findNewsDir(self):
|
|
|
|
if self.NewsOverHttp:
|
|
|
|
return self.NewsStageDir
|
|
|
|
searchPath = DSearchPath()
|
|
|
|
if AppRunnerGlobal.appRunner:
|
|
|
|
searchPath.appendDirectory(Filename.expandFrom('$TT_3_5_ROOT/phase_3.5/models/news'))
|
|
|
|
else:
|
|
|
|
basePath = os.path.expandvars('$TTMODELS') or './ttmodels'
|
|
|
|
searchPath.appendDirectory(Filename.fromOsSpecific(basePath + '/built/' + self.NewsBaseDir))
|
|
|
|
searchPath.appendDirectory(Filename(self.NewsBaseDir))
|
|
|
|
pfile = Filename(self.NewsIndexFilename)
|
|
|
|
found = vfs.resolveFilename(pfile, searchPath)
|
|
|
|
if not found:
|
|
|
|
self.notify.warning('findNewsDir - no path: %s' % self.NewsIndexFilename)
|
|
|
|
self.setErrorMessage(TTLocalizer.NewsPageErrorDownloadingFile % self.NewsIndexFilename)
|
|
|
|
return None
|
|
|
|
self.notify.debug('found index file %s' % pfile)
|
|
|
|
realDir = pfile.getDirname()
|
|
|
|
return realDir
|
|
|
|
|
|
|
|
def load(self):
|
|
|
|
self.loadBackground()
|
|
|
|
|
|
|
|
def loadBackground(self):
|
|
|
|
upsellBackground = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_newsStatusBackground')
|
|
|
|
imageScaleX = self.FrameDimensions[1] - self.FrameDimensions[0]
|
|
|
|
imageScaleY = self.FrameDimensions[3] - self.FrameDimensions[2]
|
|
|
|
self.backFrame = DirectFrame(parent=self.parent, image=upsellBackground, image_scale=(imageScaleX, 1, imageScaleY), frameColor=(1, 1, 1, 0), frameSize=self.FrameDimensions, pos=(0, 0, 0), relief=DGG.FLAT, text=TTLocalizer.NewsPageDownloadingNews1, text_scale=0.06, text_pos=(0, -0.4))
|
|
|
|
|
|
|
|
def addDownloadingTextTask(self):
|
|
|
|
self.removeDownloadingTextTask()
|
|
|
|
task = taskMgr.doMethodLater(1, self.loadingTextTask, 'DirectNewsFrameDownloadingTextTask')
|
|
|
|
task.startTime = globalClock.getFrameTime()
|
|
|
|
self.loadingTextTask(task)
|
|
|
|
|
|
|
|
def removeDownloadingTextTask(self):
|
|
|
|
taskMgr.remove('DirectNewsFrameDownloadingTextTask')
|
|
|
|
|
|
|
|
def loadMainPage(self):
|
|
|
|
self.mainFrame = DirectFrame(parent=self.backFrame, frameSize=self.FrameDimensions, frameColor=(1, 0, 0, 1))
|
|
|
|
|
|
|
|
def activate(self):
|
|
|
|
if hasattr(self, 'createdTime') and self.createdTime < base.cr.inGameNewsMgr.getLatestIssue() and self.NewsOverHttp and not self.redownloadingNews:
|
|
|
|
self.redownloadNews()
|
|
|
|
else:
|
|
|
|
self.addDownloadingTextTask()
|
|
|
|
if self.needsParseNews and not self.redownloadingNews:
|
|
|
|
self.parseNewsContent()
|
|
|
|
self.active = True
|
|
|
|
|
|
|
|
def deactivate(self):
|
|
|
|
self.removeDownloadingTextTask()
|
|
|
|
self.active = False
|
|
|
|
|
|
|
|
def unload(self):
|
|
|
|
self.removeDownloadingTextTask()
|
|
|
|
result = taskMgr.remove(self.RedownloadTaskName)
|
|
|
|
self.ignore('newsSnapshot')
|
|
|
|
self.ignore('newsChangeWeek')
|
|
|
|
self.ignore('newIssueOut')
|
|
|
|
self.ignore('clientCleanup')
|
|
|
|
|
|
|
|
def handleClientCleanup(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def doSnapshot(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def changeWeek(self, issueIndex):
|
|
|
|
if 0 <= issueIndex and issueIndex < len(self.issues):
|
|
|
|
self.issues[self.curIssueIndex].hide()
|
|
|
|
self.issues[issueIndex].show()
|
|
|
|
self.curIssueIndex = issueIndex
|
|
|
|
|
|
|
|
def loadingTextTask(self, task):
|
|
|
|
timeIndex = int(globalClock.getFrameTime() - task.startTime) % 3
|
|
|
|
timeStrs = (TTLocalizer.NewsPageDownloadingNews0, TTLocalizer.NewsPageDownloadingNews1, TTLocalizer.NewsPageDownloadingNews2)
|
|
|
|
textToDisplay = timeStrs[timeIndex] % int(self.percentDownloaded * 100)
|
|
|
|
if self.backFrame['text'] != textToDisplay:
|
|
|
|
if TTLocalizer.NewsPageDownloadingNewsSubstr in self.backFrame['text']:
|
|
|
|
self.backFrame['text'] = textToDisplay
|
|
|
|
return task.again
|
|
|
|
|
|
|
|
def setErrorMessage(self, errText):
|
|
|
|
self.backFrame['text'] = errText
|
|
|
|
|
|
|
|
def redownloadNews(self):
|
|
|
|
if self.redownloadingNews:
|
|
|
|
self.notify.warning('averting potential crash redownloadNews called twice, just returning')
|
|
|
|
return
|
|
|
|
self.percentDownloaded = 0.0
|
|
|
|
self.notify.info('starting redownloadNews')
|
|
|
|
self.startRedownload = datetime.datetime.now()
|
|
|
|
self.redownloadingNews = True
|
|
|
|
self.addDownloadingTextTask()
|
|
|
|
for issue in self.issues:
|
|
|
|
issue.destroy()
|
|
|
|
|
|
|
|
self.issues = []
|
|
|
|
self.curIssueIndex = 0
|
|
|
|
self.strFilenames = None
|
|
|
|
self.needsParseNews = True
|
|
|
|
self.newsUrl = self.getInGameNewsUrl()
|
|
|
|
self.newsDir = Filename(self.findNewsDir())
|
|
|
|
Filename(self.newsDir + '/.').makeDir()
|
|
|
|
http = HTTPClient.getGlobalPtr()
|
|
|
|
self.url = self.newsUrl + self.NewsIndexFilename
|
|
|
|
self.ch = http.makeChannel(True)
|
|
|
|
self.ch.beginGetDocument(self.url)
|
|
|
|
self.rf = Ramfile()
|
|
|
|
self.ch.downloadToRam(self.rf)
|
|
|
|
taskMgr.remove(self.RedownloadTaskName)
|
|
|
|
taskMgr.add(self.downloadIndexTask, self.RedownloadTaskName)
|
|
|
|
return
|
|
|
|
|
|
|
|
def downloadIndexTask(self, task):
|
|
|
|
if self.ch.run():
|
|
|
|
return task.cont
|
|
|
|
if not self.ch.isValid():
|
|
|
|
self.notify.warning('Unable to download %s' % self.url)
|
|
|
|
self.redownloadingNews = False
|
|
|
|
return task.done
|
|
|
|
self.newsFiles = []
|
|
|
|
filename = self.rf.readline()
|
|
|
|
while filename:
|
|
|
|
filename = filename.strip()
|
|
|
|
if filename:
|
|
|
|
self.newsFiles.append(filename)
|
|
|
|
filename = self.rf.readline()
|
|
|
|
|
|
|
|
del self.rf
|
|
|
|
self.newsFiles.sort()
|
|
|
|
self.newsIndexEntries = list(self.newsFiles)
|
|
|
|
self.notify.info('Server lists %s news files' % len(self.newsFiles))
|
|
|
|
self.notify.debug('self.newsIndexEntries=%s' % self.newsIndexEntries)
|
|
|
|
self.readNewsCache()
|
|
|
|
for basename in os.listdir(self.newsDir.toOsSpecific()):
|
|
|
|
if basename != self.CacheIndexFilename and basename not in self.newsCache:
|
|
|
|
junk = Filename(self.newsDir, basename)
|
|
|
|
self.notify.info('Removing %s' % junk)
|
|
|
|
junk.unlink()
|
|
|
|
|
|
|
|
self.nextNewsFile = 0
|
|
|
|
return self.downloadNextFile(task)
|
|
|
|
|
|
|
|
def downloadNextFile(self, task):
|
|
|
|
while self.nextNewsFile < len(self.newsFiles) and 'aaver' in self.newsFiles[self.nextNewsFile]:
|
|
|
|
self.nextNewsFile += 1
|
|
|
|
|
|
|
|
if self.nextNewsFile >= len(self.newsFiles):
|
|
|
|
self.notify.info('Done downloading news.')
|
|
|
|
self.percentDownloaded = 1
|
|
|
|
del self.newsFiles
|
|
|
|
del self.nextNewsFile
|
|
|
|
del self.newsUrl
|
|
|
|
del self.newsDir
|
|
|
|
del self.ch
|
|
|
|
del self.url
|
|
|
|
if hasattr(self, 'filename'):
|
|
|
|
del self.filename
|
|
|
|
self.redownloadingNews = False
|
|
|
|
if self.active:
|
|
|
|
self.parseNewsContent()
|
|
|
|
return task.done
|
|
|
|
self.percentDownloaded = float(self.nextNewsFile) / float(len(self.newsFiles))
|
|
|
|
self.filename = self.newsFiles[self.nextNewsFile]
|
|
|
|
self.nextNewsFile += 1
|
|
|
|
self.url = self.newsUrl + self.filename
|
|
|
|
localFilename = Filename(self.newsDir, self.filename)
|
|
|
|
self.notify.info('testing for %s' % localFilename.getFullpath())
|
|
|
|
doc = DocumentSpec(self.url)
|
|
|
|
if self.filename in self.newsCache:
|
|
|
|
size, date = self.newsCache[self.filename]
|
|
|
|
if date and localFilename.exists() and (size == 0 or localFilename.getFileSize() == size):
|
|
|
|
doc.setDate(date)
|
|
|
|
doc.setRequestMode(doc.RMNewer)
|
|
|
|
self.ch.beginGetDocument(doc)
|
|
|
|
self.ch.downloadToFile(localFilename)
|
|
|
|
taskMgr.remove(self.RedownloadTaskName)
|
|
|
|
taskMgr.add(self.downloadCurrentFileTask, self.RedownloadTaskName)
|
|
|
|
|
|
|
|
def downloadCurrentFileTask(self, task):
|
|
|
|
if self.ch.run():
|
|
|
|
return task.cont
|
|
|
|
if self.ch.getStatusCode() == 304:
|
|
|
|
self.notify.info('already cached: %s' % self.filename)
|
|
|
|
return self.downloadNextFile(task)
|
|
|
|
localFilename = Filename(self.newsDir, self.filename)
|
|
|
|
if not self.ch.isValid():
|
|
|
|
self.notify.warning('Unable to download %s' % self.url)
|
|
|
|
localFilename.unlink()
|
|
|
|
if self.filename in self.newsCache:
|
|
|
|
del self.newsCache[self.filename]
|
|
|
|
self.saveNewsCache()
|
|
|
|
return self.downloadNextFile(task)
|
|
|
|
self.notify.info('downloaded %s' % self.filename)
|
|
|
|
size = self.ch.getFileSize()
|
|
|
|
doc = self.ch.getDocumentSpec()
|
|
|
|
date = ''
|
|
|
|
if doc.hasDate():
|
|
|
|
date = doc.getDate().getString()
|
|
|
|
self.newsCache[self.filename] = (size, date)
|
|
|
|
self.saveNewsCache()
|
|
|
|
return self.downloadNextFile(task)
|
|
|
|
|
|
|
|
def readNewsCache(self):
|
|
|
|
cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename)
|
|
|
|
self.newsCache = {}
|
|
|
|
if cacheIndexFilename.isRegularFile():
|
|
|
|
file = open(cacheIndexFilename.toOsSpecific(), 'r')
|
|
|
|
for line in file.readlines():
|
|
|
|
line = line.strip()
|
|
|
|
keywords = line.split('\t')
|
|
|
|
if len(keywords) == 3:
|
|
|
|
filename, size, date = keywords
|
|
|
|
if filename in self.newsFiles:
|
|
|
|
try:
|
|
|
|
size = int(size)
|
|
|
|
except ValueError:
|
|
|
|
size = 0
|
|
|
|
|
|
|
|
self.newsCache[filename] = (size, date)
|
|
|
|
|
|
|
|
def saveNewsCache(self):
|
|
|
|
cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename)
|
|
|
|
try:
|
|
|
|
file = open(cacheIndexFilename.toOsSpecific(), 'w')
|
|
|
|
except IOError, e:
|
|
|
|
self.notify.warning('error opening news cache file %s: %s' % (cacheIndexFilename, str(e)))
|
|
|
|
return
|
|
|
|
|
|
|
|
for filename, (size, date) in self.newsCache.items():
|
|
|
|
print >> file, '%s\t%s\t%s' % (filename, size, date)
|
|
|
|
|
|
|
|
def handleNewIssueOut(self):
|
|
|
|
if hasattr(self, 'createdTime') and base.cr.inGameNewsMgr.getLatestIssue() < self.createdTime:
|
|
|
|
self.createdTime = base.cr.inGameNewsMgr.getLatestIssue()
|
|
|
|
elif self.NewsOverHttp and not self.redownloadingNews:
|
|
|
|
if not self.active:
|
|
|
|
self.redownloadNews()
|
|
|
|
|
|
|
|
def getInGameNewsUrl(self):
|
|
|
|
result = base.config.GetString('fallback-news-url', 'http://cdn.toontown.disney.go.com/toontown/en/gamenews/')
|
|
|
|
override = base.config.GetString('in-game-news-url', '')
|
|
|
|
if override:
|
|
|
|
self.notify.info('got an override url, using %s for in game news' % override)
|
|
|
|
result = override
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
launcherUrl = base.launcher.getValue('GAME_IN_GAME_NEWS_URL', '')
|
|
|
|
if launcherUrl:
|
|
|
|
result = launcherUrl
|
|
|
|
self.notify.info('got GAME_IN_GAME_NEWS_URL from launcher using %s' % result)
|
|
|
|
else:
|
|
|
|
self.notify.info('blank GAME_IN_GAME_NEWS_URL from launcher, using %s' % result)
|
|
|
|
except:
|
|
|
|
self.notify.warning('got exception getting GAME_IN_GAME_NEWS_URL from launcher, using %s' % result)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
def calcIssueVersion(self, dateStr):
|
|
|
|
majorVer = 1
|
|
|
|
minorVer = 0
|
|
|
|
for entry in self.newsIndexEntries:
|
|
|
|
if 'aaver' in entry and dateStr in entry:
|
|
|
|
parts = entry.split('_')
|
|
|
|
if len(parts) > 5:
|
|
|
|
try:
|
|
|
|
majorVer = int(parts[5])
|
|
|
|
except:
|
|
|
|
self.notify.warning('could not int %s' % parts[5])
|
|
|
|
|
|
|
|
else:
|
|
|
|
self.notify.warning('expected more than 5 parts in %s' % entry)
|
|
|
|
if len(parts) > 6:
|
|
|
|
try:
|
|
|
|
minorVer = int(parts[6])
|
|
|
|
except:
|
|
|
|
self.notify.warning('could not int %s' % parts[6])
|
|
|
|
|
|
|
|
else:
|
|
|
|
self.notify.warning('expected more than 6 parts in %s' % entry)
|
|
|
|
break
|
|
|
|
|
|
|
|
return (majorVer, minorVer)
|