Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local apiPath = "https://pinestore.cc/api/"
- if not fs.exists("installed") then
- fs.makeDir("installed")
- end
- local width, height = term.getSize()
- local renderWindow = window.create(term.current(), 1, 1, width, height)
- local oldTerm = term.redirect(renderWindow)
- local maxWidth = 70
- local function updateTermSize()
- width, height = oldTerm.getSize()
- local newW = math.min(width, maxWidth)
- renderWindow.reposition(1 + math.floor((width - newW)*0.5 + 0.5), 1, newW, height)
- oldTerm.setBackgroundColor(colors.black)
- oldTerm.clear()
- end
- updateTermSize()
- local online = true
- local categories = {
- "fun",
- "tools",
- "turtle",
- "audio",
- "other",
- }
- local selectedCategory = 1
- local selectedProject = 1
- local projectActionsOpened = false
- local projectActionSelected = 1
- local searchOpened = false
- local searchQuery = ""
- local searchResultsOpened = false
- local function getAPI(path)
- local res = http.get(apiPath .. path)
- if not res then
- online = false
- return
- end
- local data = res.readAll()
- res.close()
- return textutils.unserialiseJSON(data)
- end
- function postAPI(path, body)
- local res = http.post(apiPath .. path, textutils.serialiseJSON(body), {["Content-Type"] = "application/json"})
- if not res then
- return
- end
- local data = res.readAll()
- res.close()
- return textutils.unserialiseJSON(data)
- end
- local installedInfo = {
- projects = {},
- }
- if fs.exists("installed.json") then
- local h = fs.open("installed.json", "r")
- installedInfo = textutils.unserialiseJSON(h.readAll())
- h.close()
- end
- local function saveInstalled()
- local encoded = textutils.serialiseJSON(installedInfo)
- local h = fs.open("installed.json", "w")
- h.write(encoded)
- h.close()
- end
- local searchResults = {}
- local projectsData = getAPI("projects")
- local projects = projectsData.projects
- if not projects then
- local ps = {}
- for id, project in pairs(installedInfo.projects) do
- ps[#ps+1] = project
- end
- projects = ps
- else
- for i = 1, #projects do
- local project = projects[i]
- if installedInfo.projects[tostring(project.id)] then
- installedInfo.projects[tostring(project.id)].downloads = project.downloads
- end
- end
- saveInstalled()
- end
- for i = #projects, 1, -1 do
- local project = projects[i]
- if not project.install_command or not project.target_file then
- table.remove(projects, i)
- end
- end
- local categoryProjects = {}
- for i = 1, #projects do
- local project = projects[i]
- if not project.category then project.category = "Other" end
- if not categoryProjects[project.category] then
- categoryProjects[project.category] = {}
- end
- categoryProjects[project.category][#categoryProjects[project.category]+1] = project
- end
- for category, projectList in pairs(categoryProjects) do
- table.sort(projectList, function(a, b)
- return b.downloads < a.downloads
- end)
- end
- local function installProject(project)
- -- redirect term to old one
- term.redirect(oldTerm)
- -- override fs methods
- local projectPath = "installed/" .. project.id .. "/"
- fs.makeDir(projectPath)
- local oldFSOpen = fs.open
- local oldFSMakeDir = fs.makeDir
- local oldFSExists = fs.exists
- function fs.open(path, mode)
- -- print("open " .. path)
- -- sleep(0.5)
- if path:sub(1, 12) == "rom/programs" then
- return oldFSOpen(path, mode)
- end
- return oldFSOpen(projectPath .. path, mode)
- end
- function fs.makeDir(path)
- return oldFSMakeDir(projectPath .. path)
- end
- function fs.exists(path)
- if path:sub(1, 12) == "rom/programs" then
- return oldFSExists(path)
- end
- return oldFSExists(projectPath .. path)
- end
- -- actually run the install command
- local success, res = xpcall(shell.run, debug.traceback, project.install_command)
- -- return old fs methods
- fs.open = oldFSOpen
- fs.makeDir = oldFSMakeDir
- fs.exists = oldFSExists
- -- use render window again
- oldTerm = term.redirect(renderWindow)
- updateTermSize()
- if success then
- -- set project info to installed
- installedInfo.projects[tostring(project.id)] = project
- saveInstalled()
- postAPI("newdownload", {projectId = project.id})
- else
- error(res)
- end
- end
- local function startProject(project)
- -- override fs methods
- local projectPath = "installed/" .. project.id .. "/"
- local oldFSOpen = fs.open
- local oldFSMakeDir = fs.makeDir
- local oldFSExists = fs.exists
- local oldFSList = fs.list
- function fs.open(path, mode)
- if path:sub(1, 12) == "rom/programs" then
- return oldFSOpen(path, mode)
- end
- return oldFSOpen(projectPath .. path, mode)
- end
- function fs.makeDir(path)
- return oldFSMakeDir(projectPath .. path)
- end
- function fs.exists(path)
- return oldFSExists(projectPath .. path)
- end
- function fs.list(path)
- return oldFSList(projectPath .. path)
- end
- term.redirect(oldTerm)
- local success, res = xpcall(function()
- local success = shell.run(project.target_file)
- if not success then
- sleep(1)
- term.setTextColor(colors.white)
- print("\nPress any key to continue...")
- os.pullEvent("key")
- end
- end, debug.traceback)
- -- return old fs methods
- fs.open = oldFSOpen
- fs.makeDir = oldFSMakeDir
- fs.exists = oldFSExists
- fs.list = oldFSList
- if not success then
- if res:sub(1, 10) ~= "Terminated" then
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.red)
- term.clear()
- term.setCursorPos(1, 1)
- print(res)
- term.setTextColor(colors.white)
- sleep(1)
- print("\nPress any key to continue...")
- os.pullEvent("key")
- end
- end
- term.redirect(renderWindow)
- updateTermSize()
- end
- local function runSearch()
- local searchData = getAPI("search?q=" .. textutils.urlEncode(searchQuery))
- searchResults = searchData.projects
- searchOpened = false
- searchResultsOpened = true
- selectedProject = 1
- searchQuery = ""
- end
- local function drawCategories()
- term.setCursorPos(1, 2)
- for nr = 1, #categories do
- if nr == selectedCategory then
- term.setTextColor(colors.lime)
- else
- term.setTextColor(colors.green)
- end
- local str = "[" .. nr .. " " .. categories[nr] .. "] "
- local x, y = term.getCursorPos()
- if x + #str - 2 > width then
- print("")
- end
- term.write(str)
- end
- end
- local function drawProjects(projects)
- print("")
- local startX, startY = term.getCursorPos()
- local width, height = term.getSize()
- local linesAvailable = height - startY - 1
- local startI = math.max(1, selectedProject - linesAvailable + 1)
- for i = startI, math.min(startI + linesAvailable, #projects) do
- if i == selectedProject then
- term.setTextColor(colors.yellow)
- term.write("> ")
- else
- term.write(" ")
- end
- local project = projects[i]
- if installedInfo.projects[tostring(project.id)] then
- term.setTextColor(colors.lightBlue)
- else
- term.setTextColor(colors.white)
- end
- term.write(project.name)
- term.setTextColor(colors.lightGray)
- term.write(" by " .. project.owner_name)
- local showDownloads = true
- local downText = " downloads"
- if project.downloads == 1 then
- downText = " download "
- end
- if width <= 25 then
- showDownloads = false
- elseif width <= 29 then
- downText = ""
- elseif width <= 42 then
- downText = " dls"
- if project.downloads == 1 then
- downText = " dl "
- end
- end
- if showDownloads then
- local downloadsText = project.downloads .. downText
- local x, y = term.getCursorPos()
- term.setCursorPos(width - #downloadsText + 1, y)
- print(downloadsText)
- else
- print("")
- end
- end
- end
- local function drawProjectOptions()
- print("")
- local projects = categoryProjects[categories[selectedCategory]] or {}
- if searchResultsOpened then
- projects = searchResults
- end
- local project = projects[selectedProject]
- local installed = installedInfo.projects[tostring(project.id)]
- for i = 1, installed and 3 or 2 do
- if i == projectActionSelected then
- term.setTextColor(colors.yellow)
- term.write("> ")
- else
- term.write(" ")
- end
- term.setTextColor(colors.white)
- if installed then
- if i == 1 then
- if project.version > installed.version then
- print("Update and run")
- else
- print("Run")
- end
- elseif i == 2 then
- print("Uninstall")
- elseif i == 3 then
- print("Back")
- end
- else
- if i == 1 then
- print("Install")
- elseif i == 2 then
- print("Back")
- end
- end
- end
- print("")
- term.setTextColor(colors.white)
- term.write(project.name)
- term.setTextColor(colors.lightGray)
- print(" by " .. project.owner_name)
- term.setTextColor(colors.gray)
- print("v" .. project.version .. " " .. os.date("%B %d, %Y", math.max(project.date_added, project.date_updated) / 1000))
- print("")
- term.setTextColor(colors.lime)
- local stoppedScroll = false
- local scrollOld = term.scroll
- function term.scroll()
- stoppedScroll = true
- end
- print(project.description)
- term.scroll = scrollOld
- if stoppedScroll then
- local width, height = term.getSize()
- term.setCursorPos(1, height)
- term.clearLine()
- end
- end
- local function selectProjectAction()
- local projects = categoryProjects[categories[selectedCategory]] or {}
- if searchResultsOpened then
- projects = searchResults
- end
- local project = projects[selectedProject]
- local installed = installedInfo.projects[tostring(project.id)]
- local i = projectActionSelected
- if installed then
- if i == 1 then
- if project.version > installed.version then
- installProject(project) -- run install again to update
- end
- startProject(project)
- projectActionsOpened = false
- elseif i == 2 then
- fs.delete("installed/" .. project.id)
- installedInfo.projects[tostring(project.id)] = nil
- saveInstalled()
- projectActionsOpened = false
- elseif i == 3 then
- projectActionsOpened = false
- end
- else
- if i == 1 then
- installProject(project)
- elseif i == 2 then
- projectActionsOpened = false
- end
- end
- end
- local function drawSearch()
- term.setTextColor(colors.lightGray)
- print("\n\nSearch query:")
- term.setTextColor(colors.white)
- local lineCount = math.ceil(#searchQuery / width)
- for i = 1, lineCount do
- term.write(searchQuery:sub(1 + width*(i-1), width + width*(i-1)))
- if i < lineCount then
- local x, y = term.getCursorPos()
- term.setCursorPos(1, y+1)
- end
- end
- term.setCursorBlink(true)
- end
- local function drawSearchResults()
- term.setTextColor(colors.white)
- print("\n\nSearch results:")
- drawProjects(searchResults)
- end
- local function drawMain()
- term.setBackgroundColor(colors.black)
- term.clear()
- term.setTextColor(colors.yellow)
- term.setCursorPos(1, 1)
- local width, height = term.getSize()
- if width > 37 then
- term.write("PineStore Console v1.0")
- elseif width > 31 then
- term.write("PineStore Console")
- elseif width > 25 then
- term.write("PineStore C.")
- else
- term.write("PStore C.")
- end
- if not online then
- term.setTextColor(colors.orange)
- term.write(" offline mode")
- end
- term.setCursorBlink(false)
- if searchOpened then
- local searchText = "[TAB close]"
- term.setCursorPos(width - #searchText + 1, 1)
- term.write(searchText)
- drawSearch()
- elseif projectActionsOpened then
- drawProjectOptions()
- elseif searchResultsOpened then
- drawSearchResults()
- else
- if online then
- local searchText = "[TAB search]"
- term.setCursorPos(width - #searchText + 1, 1)
- term.write(searchText)
- end
- drawCategories()
- local projects = categoryProjects[categories[selectedCategory]] or {}
- drawProjects(projects)
- end
- renderWindow.setVisible(true)
- renderWindow.setVisible(false)
- end
- function runStore()
- while true do
- drawMain()
- -- if searchOpened then
- -- local searchText = "[TAB close]"
- -- term.setCursorPos(width - #searchText, 1)
- -- term.write(searchText)
- -- drawSearch()
- -- elseif searchResultsOpened then
- -- drawSearchResults()
- -- elseif projectActionsOpened then
- -- drawProjectOptions()
- -- else
- local event, key, x, y = os.pullEvent()
- if event == "key" then
- if searchOpened then
- if key == keys.tab then
- searchOpened = false
- elseif key == keys.enter then
- runSearch()
- elseif key == keys.backspace then
- searchQuery = searchQuery:sub(1, -2)
- end
- elseif projectActionsOpened then
- if key == keys.up or key == keys.w then
- projectActionSelected = math.max(1, projectActionSelected - 1)
- elseif key == keys.down or key == keys.s then
- local projects = categoryProjects[categories[selectedCategory]] or {}
- if searchResultsOpened then
- projects = searchResults
- end
- local project = projects[selectedProject]
- local installed = installedInfo.projects[tostring(project.id)]
- projectActionSelected = math.max(1, math.min(installed and 3 or 2, projectActionSelected + 1))
- elseif key == keys.backspace or key == keys.grave then
- projectActionsOpened = false
- elseif key == keys.enter or key == keys.space then
- selectProjectAction()
- end
- elseif searchResultsOpened then
- if key == keys.up or key == keys.w then
- selectedProject = math.max(1, selectedProject - 1)
- elseif key == keys.down or key == keys.s then
- selectedProject = math.max(1, math.min(#searchResults, selectedProject + 1))
- elseif key == keys.enter or key == keys.space then
- if #searchResults > 0 then
- projectActionsOpened = true
- projectActionSelected = 1
- end
- elseif key == keys.backspace or key == keys.grave or key == keys.tab then
- searchResultsOpened = false
- end
- else
- if key >= keys.one and key <= keys.nine then
- selectedCategory = math.max(1, math.min(#categories, key - keys.one + 1))
- elseif key == keys.left or key == keys.a then
- selectedCategory = math.max(1, selectedCategory - 1)
- elseif key == keys.right or key == keys.d then
- selectedCategory = math.max(1, math.min(#categories, selectedCategory + 1))
- elseif key == keys.up or key == keys.w then
- selectedProject = math.max(1, selectedProject - 1)
- elseif key == keys.down or key == keys.s then
- local projects = categoryProjects[categories[selectedCategory]] or {}
- selectedProject = math.max(1, math.min(#projects, selectedProject + 1))
- elseif key == keys.enter or key == keys.space then
- projectActionsOpened = true
- projectActionSelected = 1
- elseif key == keys.tab and online then
- searchOpened = true
- end
- end
- elseif event == "char" then
- if key ~= "`" then
- searchQuery = searchQuery .. key
- end
- elseif event == "term_resize" then
- updateTermSize()
- end
- end
- end
- local success, res = xpcall(runStore, debug.traceback)
- term.redirect(oldTerm)
- if not success then
- if type(res) == "string" and res:sub(1, 10) == "Terminated" then
- return
- end
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.red)
- term.clear()
- term.setCursorPos(1, 1)
- print(res)
- term.setTextColor(colors.white)
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement