Compare commits
No commits in common. "master" and "python_final" have entirely different histories.
master
...
python_fin
|
@ -1,6 +1,4 @@
|
||||||
*.elf
|
__pycache__/
|
||||||
*.exe
|
.vscode/
|
||||||
*.sum
|
*.egg-info/
|
||||||
live/
|
build/
|
||||||
*.list
|
|
||||||
*.zip
|
|
||||||
|
|
28
Makefile
28
Makefile
|
@ -1,28 +0,0 @@
|
||||||
EXEs=banana.elf banana.exe
|
|
||||||
|
|
||||||
all: tidy clean ${EXEs}
|
|
||||||
|
|
||||||
tidy:
|
|
||||||
go mod tidy
|
|
||||||
|
|
||||||
banana.elf:
|
|
||||||
go build -o banana.elf banana.go
|
|
||||||
|
|
||||||
banana.exe:
|
|
||||||
GOOS=windows go build -o banana.exe banana.go
|
|
||||||
|
|
||||||
clean:
|
|
||||||
go clean
|
|
||||||
-rm ${EXEs}
|
|
||||||
|
|
||||||
run:
|
|
||||||
go run banana.go -i live/addons.list -o live/AddOns/
|
|
||||||
|
|
||||||
install:
|
|
||||||
GOBIN=~/.local/bin/ go install banana.go
|
|
||||||
|
|
||||||
install-steamos: banana.elf
|
|
||||||
cp banana.elf /usr/bin/banana
|
|
||||||
cp banana.timer banana.service /etc/systemd/system/
|
|
||||||
systemctl enable banana.timer
|
|
||||||
systemctl enable banana.service
|
|
111
README.mdown
111
README.mdown
|
@ -4,7 +4,7 @@ Elder Scrolls Online addon manager and a Tamriel Trade Centre price table update
|
||||||
|
|
||||||
[MIT License](LICENSE)
|
[MIT License](LICENSE)
|
||||||
|
|
||||||
It is recommended that you back up your ESO live profile before using `banana` in case you want to revert back.
|
It is recommended that you back up your ESO live profile before using `eso-banana` in case you want to revert back.
|
||||||
|
|
||||||
On Windows, press `Windows Key + e` to open a file explorer and enter the following path in the address bar:
|
On Windows, press `Windows Key + e` to open a file explorer and enter the following path in the address bar:
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ Make a copy of the `live` folder and rename it to something meaningful like `old
|
||||||
Follow the installation instructions for your `platform`:
|
Follow the installation instructions for your `platform`:
|
||||||
|
|
||||||
- [Windows](#windows-installation)
|
- [Windows](#windows-installation)
|
||||||
- [SteamOS](#steamos-installation)
|
- [Linux](#linux-installation)
|
||||||
|
|
||||||
On first run, the `addons.list` file will be created in your ESO live directory.
|
On first run, the `addons.list` file will be created in your ESO live directory.
|
||||||
It will look similar to the following:
|
It will look similar to the following:
|
||||||
|
@ -41,18 +41,27 @@ Make sure to include the `www.` indicator that ESOUI requires for some stupid re
|
||||||
|
|
||||||
# Windows Installation
|
# Windows Installation
|
||||||
|
|
||||||
0. Download the latest `banana.exe` release for [Windows](https://git.joyo.dev/eso/banana/releases/).
|
Install the latest Python 3 using the installer provided by [python.org](https://www.python.org/downloads/windows/).
|
||||||
|
When the python installer asks, [add python to PATH](https://docs.python.org/3/using/windows.html#installation-steps).
|
||||||
|
|
||||||
1. Add the executable to the [user's path](https://www.wikihow.com/Change-the-PATH-Environment-Variable-on-Windows) or replace "banana.exe" with the full path to where the executable is stored.
|
Open [Windows Terminal](https://www.microsoft.com/en-US/p/windows-terminal/9n0dx20hk701) or any windows command prompt and navigate to this project.
|
||||||
|
Use the Python package manager PIP to install `eso-banana`.
|
||||||
2. Run the executable from the commandline `banana.exe` for a default AddOn update or with `banana.exe -h` for usage.
|
|
||||||
|
|
||||||
# Windows Optional
|
|
||||||
|
|
||||||
To schedule a periodic background run of `banana`, open Powershell as a user and enter the following commands.
|
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$Command = (Get-Command "banana.exe").Source
|
cd .\banana\
|
||||||
|
pip install .
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the project is installed to python's packages you may invoke the addon script with the following command:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
eso-banana-script.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
To schedule a periodic background run of `eso-banana`, open Powershell as a user and enter the following commands.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$Command = (Get-Command "eso-banana-script.exe").Source
|
||||||
$Action = New-ScheduledTaskAction -Execute $Command -Argument "--log"
|
$Action = New-ScheduledTaskAction -Execute $Command -Argument "--log"
|
||||||
$Trigger = New-ScheduledTaskTrigger -Daily -At 11am
|
$Trigger = New-ScheduledTaskTrigger -Daily -At 11am
|
||||||
$Settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable -StartWhenAvailable -RunOnlyIfIdle
|
$Settings = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable -StartWhenAvailable -RunOnlyIfIdle
|
||||||
|
@ -67,7 +76,7 @@ TaskPath TaskName State
|
||||||
\ eso-banana Ready
|
\ eso-banana Ready
|
||||||
```
|
```
|
||||||
|
|
||||||
## Windows Unschedule
|
# Windows Unschedule
|
||||||
|
|
||||||
Only run the following command if you wish to remove scheduled run of `eso-banana`.
|
Only run the following command if you wish to remove scheduled run of `eso-banana`.
|
||||||
|
|
||||||
|
@ -79,7 +88,7 @@ Unregister-ScheduledTask eso-banana
|
||||||
|
|
||||||
Press enter to confirm removal.
|
Press enter to confirm removal.
|
||||||
|
|
||||||
# SteamOS Installation
|
# Steam Deck Installation
|
||||||
|
|
||||||
The following instructions are for arch linux system installed with the Steam Deck.
|
The following instructions are for arch linux system installed with the Steam Deck.
|
||||||
|
|
||||||
|
@ -87,57 +96,103 @@ The following instructions are for arch linux system installed with the Steam De
|
||||||
|
|
||||||
> From the STEAM menu, select Power, then Switch to Desktop
|
> From the STEAM menu, select Power, then Switch to Desktop
|
||||||
|
|
||||||
1. Download the latest `banana.elf` release for [SteamOS](https://git.joyo.dev/eso/banana/releases/).
|
1. Once you've become familiar with desktop mode, open a terminal and enter the following commands to install python.
|
||||||
|
|
||||||
2. Set executable with `chmod +x banana.elf` and add the executable to `~/.local/bin/` or any directory in the user's $PATH environment.
|
```
|
||||||
|
sudo btrfs property set -ts / ro false
|
||||||
|
sudo pacman-key --init
|
||||||
|
sudo pacman-key --populate archlinux
|
||||||
|
sudo pacman -S python-pip
|
||||||
|
```
|
||||||
|
|
||||||
- optionally replace "banana.elf" with the full path to where the executable is stored.
|
2. Run the following commands to download this project and install the scheduled service.
|
||||||
|
|
||||||
2. Run the executable from the commandline `banana.elf` for a default AddOn update or with `banana.elf -h` for usage.
|
```
|
||||||
|
wget https://joyo.dev/eso/banana/archive/master.tar.gz -O banana.tgz
|
||||||
|
tar xvf banana.tgz
|
||||||
|
cd ./banana/
|
||||||
|
pip install .
|
||||||
|
cp banana.timer banana.service /etc/systemd/system/
|
||||||
|
sudo systemctl enable banana.timer
|
||||||
|
sudo systemctl enable banana.service
|
||||||
|
```
|
||||||
|
|
||||||
Note: banana will create and `addons.list` file which can be edited to include additional adddon URLs at the following path:
|
3. Now that `eso-banana-script` has been installed, run it once to create the `addons.list` file.
|
||||||
|
If the mentioned command is missing your may need to restart the TTY.
|
||||||
|
|
||||||
`$EDITOR "~/.steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/addons.list"`
|
4. The created file can be edited with vim to include additional adddon URLs at the following path.
|
||||||
|
|
||||||
5. To return to [Gaming Mode](https://help.steampowered.com/en/faqs/view/671A-4453-E8D2-323C):
|
`vim "~/.steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/addons.list"`
|
||||||
|
|
||||||
|
It will look similar to the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://www.esoui.com/downloads/info7-LibAddonMenu.html
|
||||||
|
https://www.esoui.com/downloads/info1245-TamrielTradeCentre.html
|
||||||
|
https://www.esoui.com/downloads/info1146-LibCustomMenu.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the ESOUI url for each additional addon you wish to keep updated.
|
||||||
|
Make sure to include the `www.` indicator that ESOUI requires for some stupid reason.
|
||||||
|
|
||||||
|
5. Run `eso-banana-script` once more to download and install the additional addons.
|
||||||
|
|
||||||
|
6. To return to [Gaming Mode](https://help.steampowered.com/en/faqs/view/671A-4453-E8D2-323C):
|
||||||
|
|
||||||
> ...there's a shortcut link to 'Return to Gaming Mode'.
|
> ...there's a shortcut link to 'Return to Gaming Mode'.
|
||||||
> Select that to get back to standard Steam Deck UI.
|
> Select that to get back to standard Steam Deck UI.
|
||||||
> You can also go through the system menus to Log Off to get back.
|
> You can also go through the system menus to Log Off to get back.
|
||||||
|
|
||||||
# SteamOS Optional
|
# Steam Deck Unschedule
|
||||||
|
|
||||||
|
Only run the following command if you wish to remove scheduled run of `eso-banana`.
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl list-timers --all
|
||||||
|
sudo systemctl disable banana.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
# Linux Installation
|
||||||
|
|
||||||
|
The following instructions are for any linux system using systemd.
|
||||||
|
Using your distros package management system, install python3-pip.
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install python3-pip
|
||||||
|
```
|
||||||
|
|
||||||
Because systemd is cancer you will need to edit the `banana.service` file to include your user's home path.
|
Because systemd is cancer you will need to edit the `banana.service` file to include your user's home path.
|
||||||
If `banana` is not installed to `~/.local/bin/banana.elf`, open a new terminal and use the results from `which banana.elf`.
|
In the unlikely event that the script is not installed to `~/.local/bin/eso-banana-script`, open a new terminal and use the results from `which eso-banana-script`.
|
||||||
|
|
||||||
```
|
```
|
||||||
[Unit]
|
[Unit]
|
||||||
Description="Elder Scrolls Online addon manager and a Tamriel Trade Centre price table updater."
|
Description=Run banana daily and on boot.
|
||||||
Wants=banana.timer
|
Wants=banana.timer
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/home/<user_name_here>/.local/bin/banana.elf
|
ExecStart=/home/user_name_here/.local/bin/eso-banana-script
|
||||||
User=deck
|
User=deck
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, install the systemd timer and service to run `banana` everyday.
|
Finally, install `eso-banana` using pip and install the systemd timer and service to run `eso-banana` everyday.
|
||||||
|
|
||||||
```
|
```
|
||||||
cd ./banana/
|
cd ./banana/
|
||||||
|
pip install .
|
||||||
cp banana.timer banana.service /etc/systemd/system/
|
cp banana.timer banana.service /etc/systemd/system/
|
||||||
sudo systemctl enable banana.timer
|
sudo systemctl enable banana.timer
|
||||||
sudo systemctl enable banana.service
|
sudo systemctl enable banana.service
|
||||||
```
|
```
|
||||||
|
|
||||||
## SteamOS Unschedule
|
# Linux Unschedule
|
||||||
|
|
||||||
Only run the following command if you wish to remove scheduled run of `banana`.
|
Only run the following command if you wish to remove scheduled run of `eso-banana`.
|
||||||
|
|
||||||
```
|
```
|
||||||
systemctl list-timers --all
|
systemctl list-timers --all
|
||||||
sudo systemctl disable banana.timer
|
sudo systemctl disable banana.timer
|
||||||
```
|
```
|
367
banana.go
367
banana.go
|
@ -1,367 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/alexflint/go-arg"
|
|
||||||
|
|
||||||
"archive/zip"
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var args struct {
|
|
||||||
Addon_list_path string `arg:"-i,--addon-list" help:"path to addon list file"`
|
|
||||||
Out_dir string `arg:"-o,--live-dir" help:"path to eso live directory"`
|
|
||||||
Ttc bool `arg:"-t,--ttc" help:"only update tamriel trade centre db"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
arg.MustParse(&args)
|
|
||||||
eso_live_path := eso_live_path_get()
|
|
||||||
if args.Addon_list_path == "" {
|
|
||||||
args.Addon_list_path = filepath.Join(eso_live_path, "addons.list")
|
|
||||||
}
|
|
||||||
if args.Out_dir == "" {
|
|
||||||
args.Out_dir = filepath.Join(eso_live_path, "AddOns")
|
|
||||||
}
|
|
||||||
fmt.Println("args", args)
|
|
||||||
|
|
||||||
_, error := os.Stat(args.Addon_list_path)
|
|
||||||
if errors.Is(error, os.ErrNotExist) {
|
|
||||||
error = addon_list_create(args.Addon_list_path)
|
|
||||||
if error != nil {
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addon_urls, error := addon_list_read(args.Addon_list_path)
|
|
||||||
if error != nil {
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
error = ttc_update(filepath.Join(args.Out_dir, "TamrielTradeCentre"))
|
|
||||||
if error != nil {
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Updated,", TCC_PRICE_TABLE_URI)
|
|
||||||
|
|
||||||
if args.Ttc {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
addon_paths, error := os.ReadDir(args.Out_dir)
|
|
||||||
if error != nil {
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
var eso_live_addon_names []string
|
|
||||||
for _, path := range addon_paths {
|
|
||||||
if path.IsDir() {
|
|
||||||
eso_live_addon_names = append(eso_live_addon_names, path.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var eso_ui_list []EsoAddon
|
|
||||||
for _, url := range addon_urls {
|
|
||||||
eso_ui, error := eso_ui_stat_init(url)
|
|
||||||
if error != nil {
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
eso_ui_list = append(eso_ui_list, eso_ui)
|
|
||||||
}
|
|
||||||
|
|
||||||
var eso_live_list []EsoAddon
|
|
||||||
for _, eso_live_name := range eso_live_addon_names {
|
|
||||||
eso_live, error := eso_live_stat_init(eso_live_name)
|
|
||||||
if error != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
matching := ""
|
|
||||||
|
|
||||||
for _, eso_ui := range eso_ui_list {
|
|
||||||
if strings.Contains(eso_live_name, eso_ui.name) {
|
|
||||||
matching = eso_live_name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if matching == "" {
|
|
||||||
fmt.Println("Removed,", eso_live.path)
|
|
||||||
os.RemoveAll(eso_live.path)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
eso_live_list = append(eso_live_list, eso_live)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, eso_live := range eso_live_list {
|
|
||||||
fmt.Printf("Live, %s, %s\n", eso_live.name, eso_live.version)
|
|
||||||
}
|
|
||||||
for _, eso_ui := range eso_ui_list {
|
|
||||||
fmt.Printf("Updated, %s, %s\n", eso_ui.name, eso_ui.version)
|
|
||||||
error = eso_ui_get_unzip(eso_ui.path, args.Out_dir)
|
|
||||||
if error != nil {
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
CONFIG_TEMPLATE = `https://www.esoui.com/downloads/info7-LibAddonMenu.html
|
|
||||||
https://www.esoui.com/downloads/info1245-TamrielTradeCentre.html
|
|
||||||
https://www.esoui.com/downloads/info1146-LibCustomMenu.html
|
|
||||||
`
|
|
||||||
ESO_LIVE_PATH_WINDOWS = `Documents\Elder Scrolls Online\live`
|
|
||||||
ESO_LIVE_PATH_STEAMOS = ".steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/"
|
|
||||||
TCC_PRICE_TABLE_URI = "https://us.tamrieltradecentre.com/download/PriceTable"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ESOUI_NAME = regexp.MustCompile(`(?:https://www.esoui.com/downloads/info[0-9]+\-)([A-Za-z]+)(?:\.html)`)
|
|
||||||
ESOUI_VERSION = regexp.MustCompile(`(?:<div\s+id="version">Version:\s+)(.*)(?:</div>)`)
|
|
||||||
ESOUI_DOWNLOAD = regexp.MustCompile(`https://cdn.esoui.com/downloads/file[^"]*`)
|
|
||||||
LIVE_VERSION = regexp.MustCompile(`(?:##\s+Version:\s+)(.*)(?:\n)`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func eso_live_path_get() string {
|
|
||||||
home_path, error := os.UserHomeDir()
|
|
||||||
if error != nil {
|
|
||||||
panic(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
return filepath.Join(home_path, ESO_LIVE_PATH_WINDOWS)
|
|
||||||
} else {
|
|
||||||
return filepath.Join(home_path, ESO_LIVE_PATH_STEAMOS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addon_list_create(addon_list_path string) error {
|
|
||||||
file_open, error := os.Create(addon_list_path)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
defer file_open.Close()
|
|
||||||
|
|
||||||
_, error = file_open.Write([]byte(CONFIG_TEMPLATE))
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addon_list_read(addon_list_path string) ([]string, error) {
|
|
||||||
file_open, error := os.OpenFile(addon_list_path, os.O_RDONLY, 0644)
|
|
||||||
defer file_open.Close()
|
|
||||||
if error != nil {
|
|
||||||
return nil, error
|
|
||||||
}
|
|
||||||
|
|
||||||
file_scanner := bufio.NewScanner(file_open)
|
|
||||||
lines := []string{}
|
|
||||||
|
|
||||||
for file_scanner.Scan() {
|
|
||||||
line := file_scanner.Text()
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case line == "":
|
|
||||||
continue
|
|
||||||
case strings.HasPrefix(line, "#"):
|
|
||||||
continue
|
|
||||||
case strings.HasPrefix(line, "//"):
|
|
||||||
continue
|
|
||||||
case strings.HasPrefix(line, "-"):
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = append(lines, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type EsoAddon struct {
|
|
||||||
name string
|
|
||||||
version string
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func eso_ui_stat_init(addon_url string) (EsoAddon, error) {
|
|
||||||
addon_resp, error := http.Get(addon_url)
|
|
||||||
if error != nil {
|
|
||||||
return EsoAddon{}, error
|
|
||||||
}
|
|
||||||
defer addon_resp.Body.Close()
|
|
||||||
|
|
||||||
if addon_resp.StatusCode == http.StatusNotFound {
|
|
||||||
return EsoAddon{}, errors.New(http.StatusText(addon_resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
addon_body, error := io.ReadAll(addon_resp.Body)
|
|
||||||
if error != nil {
|
|
||||||
return EsoAddon{}, error
|
|
||||||
}
|
|
||||||
|
|
||||||
download_page_url := strings.Replace(addon_url, "info", "download", -1)
|
|
||||||
download_resp, error := http.Get(download_page_url)
|
|
||||||
if error != nil {
|
|
||||||
return EsoAddon{}, error
|
|
||||||
}
|
|
||||||
defer download_resp.Body.Close()
|
|
||||||
|
|
||||||
if download_resp.StatusCode == http.StatusNotFound {
|
|
||||||
return EsoAddon{}, errors.New(http.StatusText(download_resp.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
download_body, error := io.ReadAll(download_resp.Body)
|
|
||||||
if error != nil {
|
|
||||||
return EsoAddon{}, error
|
|
||||||
}
|
|
||||||
|
|
||||||
var name string
|
|
||||||
names := ESOUI_NAME.FindStringSubmatch(addon_url)
|
|
||||||
if len(names) > 1 {
|
|
||||||
name = names[1]
|
|
||||||
} else {
|
|
||||||
name = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var version string
|
|
||||||
versions := ESOUI_VERSION.FindStringSubmatch(string(addon_body))
|
|
||||||
if len(versions) > 1 {
|
|
||||||
version = versions[1]
|
|
||||||
} else {
|
|
||||||
version = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
path := string(ESOUI_DOWNLOAD.Find(download_body))
|
|
||||||
if path == "" {
|
|
||||||
return EsoAddon{}, errors.New("Download URI missing " + addon_url)
|
|
||||||
}
|
|
||||||
|
|
||||||
return EsoAddon{name, version, path}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func eso_live_stat_init(eso_live_name string) (EsoAddon, error) {
|
|
||||||
path := filepath.Join(args.Out_dir, eso_live_name)
|
|
||||||
|
|
||||||
content, error := os.ReadFile(filepath.Join(path, eso_live_name+".txt"))
|
|
||||||
if error != nil {
|
|
||||||
return EsoAddon{}, error
|
|
||||||
}
|
|
||||||
|
|
||||||
var version string
|
|
||||||
versions := LIVE_VERSION.FindStringSubmatch(string(content))
|
|
||||||
if len(versions) > 1 {
|
|
||||||
version = versions[1]
|
|
||||||
} else {
|
|
||||||
version = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return EsoAddon{eso_live_name, version, path}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func eso_ui_get_unzip(esoui_url string, out_dir string) error {
|
|
||||||
response, error := http.Get(esoui_url)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
if response.StatusCode == http.StatusNotFound {
|
|
||||||
return errors.New(http.StatusText(response.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
body, error := io.ReadAll(response.Body)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bytes.NewReader(body)
|
|
||||||
zip_reader, error := zip.NewReader(reader, int64(len(body)))
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, zipped_file := range zip_reader.File {
|
|
||||||
if zipped_file.Mode().IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
zipped_file_open, error := zipped_file.Open()
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
name := filepath.Join(out_dir, zipped_file.Name)
|
|
||||||
os.MkdirAll(filepath.Dir(name), os.ModePerm)
|
|
||||||
|
|
||||||
create, error := os.Create(name)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
defer create.Close()
|
|
||||||
|
|
||||||
create.ReadFrom(zipped_file_open)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ttc_update(out_path string) error {
|
|
||||||
response, error := http.Get(TCC_PRICE_TABLE_URI)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
|
|
||||||
if response.StatusCode == http.StatusNotFound {
|
|
||||||
return errors.New(http.StatusText(response.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
body, error := io.ReadAll(response.Body)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bytes.NewReader(body)
|
|
||||||
zip_reader, error := zip.NewReader(reader, int64(len(body)))
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, zipped_file := range zip_reader.File {
|
|
||||||
if zipped_file.Mode().IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
zipped_file_open, error := zipped_file.Open()
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
name := filepath.Join(out_path, zipped_file.Name)
|
|
||||||
os.MkdirAll(filepath.Dir(name), os.ModePerm)
|
|
||||||
|
|
||||||
create, error := os.Create(name)
|
|
||||||
if error != nil {
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
defer create.Close()
|
|
||||||
|
|
||||||
create.ReadFrom(zipped_file_open)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,388 @@
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from io import BytesIO
|
||||||
|
from packaging import version
|
||||||
|
from pathlib import Path
|
||||||
|
from pathlib import Path
|
||||||
|
from platform import system
|
||||||
|
from shutil import rmtree, copytree
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
from typing import Tuple
|
||||||
|
from zipfile import ZipFile
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
|
||||||
|
config_template = """https://www.esoui.com/downloads/info7-LibAddonMenu.html
|
||||||
|
https://www.esoui.com/downloads/info1245-TamrielTradeCentre.html
|
||||||
|
https://www.esoui.com/downloads/info1146-LibCustomMenu.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def config_new(path: Path):
|
||||||
|
path.touch(exist_ok=True)
|
||||||
|
|
||||||
|
with path.open("w") as file_open:
|
||||||
|
file_open.write(config_template)
|
||||||
|
|
||||||
|
def live_to_esoui(*, path: Path, esoui_uris: list):
|
||||||
|
live_name, live_version, live_path = parsing_live(path)
|
||||||
|
|
||||||
|
if not live_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
esoui_name, esoui_version, esoui_uri = None, None, None
|
||||||
|
|
||||||
|
for _name, _version, _uri in esoui_uris:
|
||||||
|
if _name in live_name:
|
||||||
|
esoui_name, esoui_version, esoui_uri = _name, _version, _uri
|
||||||
|
break
|
||||||
|
|
||||||
|
if live_name in _name:
|
||||||
|
esoui_name, esoui_version, esoui_uri = _name, _version, _uri
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
if not esoui_name:
|
||||||
|
rmtree(live_path)
|
||||||
|
logging.info(f"{live_name} addon removed from: {live_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if esoui_version == live_version:
|
||||||
|
logging.info(f"{live_name} is already up to date.")
|
||||||
|
return
|
||||||
|
|
||||||
|
response = requests.get(esoui_uri)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
temp_dir = TemporaryDirectory()
|
||||||
|
temp_path = Path(temp_dir.name)
|
||||||
|
|
||||||
|
zip_file = ZipFile(BytesIO(response.content))
|
||||||
|
zip_file.extractall(temp_path)
|
||||||
|
|
||||||
|
rmtree(live_path)
|
||||||
|
|
||||||
|
for each in temp_path.iterdir():
|
||||||
|
copytree(each, live_path)
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
f"{live_name} updated from {live_version} to {esoui_version} at {live_path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def esoui_to_live(*, esoui_uris: list, live_path: Path):
|
||||||
|
for addon_name, addon_version, esoui_dowload_uri in esoui_uris:
|
||||||
|
match = None
|
||||||
|
|
||||||
|
for each in live_path.iterdir():
|
||||||
|
if addon_name in each.name:
|
||||||
|
match = each
|
||||||
|
break
|
||||||
|
|
||||||
|
if each.name in addon_name:
|
||||||
|
match = each
|
||||||
|
break
|
||||||
|
|
||||||
|
if match:
|
||||||
|
logging.debug(f"{addon_name} already installed.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
response = requests.get(esoui_dowload_uri)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
temp_dir = TemporaryDirectory()
|
||||||
|
temp_path = Path(temp_dir.name)
|
||||||
|
|
||||||
|
zip_file = ZipFile(BytesIO(response.content))
|
||||||
|
zip_file.extractall(temp_path)
|
||||||
|
|
||||||
|
for each in temp_path.iterdir():
|
||||||
|
live_dest = live_path.joinpath(each.name)
|
||||||
|
|
||||||
|
if live_dest.exists():
|
||||||
|
continue
|
||||||
|
|
||||||
|
copytree(each, live_dest)
|
||||||
|
|
||||||
|
logging.info(f"{addon_name} installed {addon_version} at {live_dest}")
|
||||||
|
|
||||||
|
|
||||||
|
esoui_prefix = re.compile("https://www.esoui.com/downloads/info[0-9]+\-")
|
||||||
|
esoui_version_html = re.compile('<div\s+id="version">Version:\s+[^<]+')
|
||||||
|
esoui_version_split = re.compile('<div\s+id="version">Version:\s+')
|
||||||
|
esoui_download = re.compile('https://cdn.esoui.com/downloads/file[^"]*')
|
||||||
|
live_version = re.compile("##\s+Version:\s+.*")
|
||||||
|
live_version_split = re.compile("##\s+Version:\s+")
|
||||||
|
|
||||||
|
|
||||||
|
def esoui_parse(url: str) -> Tuple[str, version.Version, str]:
|
||||||
|
addon_name = esoui_prefix.split(url)[1]
|
||||||
|
addon_name = addon_name.split(".html")[0]
|
||||||
|
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
version_line = esoui_version_html.search(response.text).group(0)
|
||||||
|
_version = esoui_version_split.split(version_line)[1]
|
||||||
|
try:
|
||||||
|
_version = version.parse(_version)
|
||||||
|
except version.InvalidVersion:
|
||||||
|
_version = version.parse("1")
|
||||||
|
|
||||||
|
esoui_page_url = url.replace("info", "download").replace(".html", "")
|
||||||
|
|
||||||
|
response = requests.get(esoui_page_url)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
esoui_dowload_uri = esoui_download.search(response.text).group(0)
|
||||||
|
response = requests.head(esoui_dowload_uri)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
return addon_name, _version, esoui_dowload_uri
|
||||||
|
|
||||||
|
|
||||||
|
def parsing_live(path: Path):
|
||||||
|
if not path.is_dir():
|
||||||
|
logging.error(f"unexpected file object {path}, ignoring")
|
||||||
|
return
|
||||||
|
|
||||||
|
meta_file = path.joinpath(f"{path.stem}.txt")
|
||||||
|
|
||||||
|
if not meta_file.exists():
|
||||||
|
for meta_file in path.glob("*.txt"):
|
||||||
|
if not meta_file.stem in path.stem:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
with meta_file.open("r") as file_open:
|
||||||
|
meta_data = file_open.read()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
with meta_file.open("r", encoding="latin-1") as file_open:
|
||||||
|
meta_data = file_open.read()
|
||||||
|
|
||||||
|
addon_name = meta_file.stem
|
||||||
|
result = live_version.search(meta_data)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
_version = result.group(0)
|
||||||
|
_version = live_version_split.split(_version)[1]
|
||||||
|
try:
|
||||||
|
_version = version.parse(_version)
|
||||||
|
except version.InvalidVersion:
|
||||||
|
_version = version.parse("0")
|
||||||
|
else:
|
||||||
|
_version = version.parse("0")
|
||||||
|
|
||||||
|
return addon_name, _version, path
|
||||||
|
|
||||||
|
def eso_live_path_get():
|
||||||
|
if system() == "Windows":
|
||||||
|
eso_live_path = Path.home().joinpath(
|
||||||
|
"Documents\Elder Scrolls Online\live"
|
||||||
|
)
|
||||||
|
if eso_live_path.exists:
|
||||||
|
return eso_live_path
|
||||||
|
|
||||||
|
else:
|
||||||
|
eso_live_path = Path.home().joinpath(
|
||||||
|
".steam/steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live/"
|
||||||
|
)
|
||||||
|
|
||||||
|
if eso_live_path.exists():
|
||||||
|
return eso_live_path
|
||||||
|
|
||||||
|
eso_live_path = Path.home().joinpath(
|
||||||
|
".var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/compatdata/306130/pfx/drive_c/users/steamuser/Documents/Elder Scrolls Online/live"
|
||||||
|
)
|
||||||
|
|
||||||
|
if eso_live_path.exists():
|
||||||
|
return eso_live_path
|
||||||
|
|
||||||
|
raise Exception("Unable to find `steamuser/Documents/Elder Scrolls Online/live`, specify the full path and contact maintainer to see if they'll add it.")
|
||||||
|
|
||||||
|
def unlisted_remove():
|
||||||
|
parser = ArgumentParser(
|
||||||
|
description="Visit https://www.esoui.com/ to search for addons and their dependencies URLs. Edit addons.yaml in the ESO live path and add the URL for each addon for installation. "
|
||||||
|
)
|
||||||
|
parser.add_argument("-v", "--verbose", action="count", help="verbose logging")
|
||||||
|
parser.add_argument("-l", "--log", action="store_true")
|
||||||
|
parser.add_argument("-p", "--eso_live_path")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.eso_live_path:
|
||||||
|
args.eso_live_path = Path(args.eso_live_path)
|
||||||
|
else:
|
||||||
|
args.eso_live_path = eso_live_path_get()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
level = logging.DEBUG
|
||||||
|
format = "%(asctime)s %(filename)s:%(lineno)d %(message)s"
|
||||||
|
else:
|
||||||
|
level = logging.INFO
|
||||||
|
format = "%(asctime)s %(message)s"
|
||||||
|
|
||||||
|
if args.log:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=level,
|
||||||
|
format=format,
|
||||||
|
filename=args.eso_live_path.joinpath("banana.log"),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=level,
|
||||||
|
format=format,
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.info(args)
|
||||||
|
|
||||||
|
|
||||||
|
config_path = Path(args.eso_live_path).joinpath("addons.list")
|
||||||
|
|
||||||
|
if not config_path.exists():
|
||||||
|
config_new(config_path)
|
||||||
|
logging.info(f'addons list created at "{config_path}"')
|
||||||
|
|
||||||
|
with config_path.open("r") as file_open:
|
||||||
|
config_current = [line.rstrip('\n') for line in file_open]
|
||||||
|
|
||||||
|
config_current = filter(None, config_current)
|
||||||
|
live_path = args.eso_live_path.joinpath("AddOns")
|
||||||
|
live_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
esoui_uris = list()
|
||||||
|
|
||||||
|
for url in config_current:
|
||||||
|
esoui = esoui_parse(url)
|
||||||
|
if esoui:
|
||||||
|
esoui_uris.append(esoui)
|
||||||
|
|
||||||
|
for child in live_path.iterdir():
|
||||||
|
live_to_esoui(path=child, esoui_uris=esoui_uris)
|
||||||
|
|
||||||
|
esoui_to_live(esoui_uris=esoui_uris, live_path=live_path)
|
||||||
|
ttc_update(live_path=live_path)
|
||||||
|
|
||||||
|
def periodical_script():
|
||||||
|
parser = ArgumentParser(
|
||||||
|
description="Visit https://www.esoui.com/ to search for addons and their dependencies URLs. Edit addons.yaml in the ESO live path and add the URL for each addon for installation. "
|
||||||
|
)
|
||||||
|
parser.add_argument("-v", "--verbose", action="count", help="verbose logging")
|
||||||
|
parser.add_argument("-l", "--log", action="store_true")
|
||||||
|
parser.add_argument("-p", "--eso_live_path")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.eso_live_path:
|
||||||
|
args.eso_live_path = Path(args.eso_live_path)
|
||||||
|
else:
|
||||||
|
args.eso_live_path = eso_live_path_get()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
level = logging.DEBUG
|
||||||
|
format = "%(asctime)s %(filename)s:%(lineno)d %(message)s"
|
||||||
|
else:
|
||||||
|
level = logging.INFO
|
||||||
|
format = "%(asctime)s %(message)s"
|
||||||
|
|
||||||
|
if args.log:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=level,
|
||||||
|
format=format,
|
||||||
|
filename=args.eso_live_path.joinpath("banana.log"),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=level,
|
||||||
|
format=format,
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.info(args)
|
||||||
|
|
||||||
|
|
||||||
|
config_path = Path(args.eso_live_path).joinpath("addons.list")
|
||||||
|
|
||||||
|
if not config_path.exists():
|
||||||
|
config_new(config_path)
|
||||||
|
logging.info(f'addons list created at "{config_path}"')
|
||||||
|
|
||||||
|
with config_path.open("r") as file_open:
|
||||||
|
config_current = [line.rstrip('\n') for line in file_open]
|
||||||
|
|
||||||
|
config_current = filter(None, config_current)
|
||||||
|
live_path = args.eso_live_path.joinpath("AddOns")
|
||||||
|
live_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
esoui_uris = list()
|
||||||
|
|
||||||
|
for url in config_current:
|
||||||
|
esoui = esoui_parse(url)
|
||||||
|
if esoui:
|
||||||
|
esoui_uris.append(esoui)
|
||||||
|
|
||||||
|
esoui_to_live(esoui_uris=esoui_uris, live_path=live_path)
|
||||||
|
ttc_update(live_path=live_path)
|
||||||
|
|
||||||
|
|
||||||
|
def ttc():
|
||||||
|
parser = ArgumentParser(description="Tamriel Trade Centre price table updater.")
|
||||||
|
parser.add_argument("-v", "--verbose", action="count", help="verbose logging")
|
||||||
|
parser.add_argument("-l", "--log", action="store_true")
|
||||||
|
parser.add_argument("-p", "--eso_live_path")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.eso_live_path:
|
||||||
|
args.eso_live_path = Path(args.eso_live_path)
|
||||||
|
else:
|
||||||
|
args.eso_live_path = eso_live_path_get()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
level = logging.DEBUG
|
||||||
|
format = "%(asctime)s %(filename)s:%(lineno)d %(message)s"
|
||||||
|
else:
|
||||||
|
level = logging.INFO
|
||||||
|
format = "%(asctime)s %(message)s"
|
||||||
|
|
||||||
|
if args.log:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=level,
|
||||||
|
format=format,
|
||||||
|
filename=args.eso_live_path.joinpath("banana.log"),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(
|
||||||
|
level=level,
|
||||||
|
format=format,
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.info(args)
|
||||||
|
|
||||||
|
live_path = Path(args.eso_live_path).joinpath("AddOns")
|
||||||
|
|
||||||
|
if not live_path.is_dir():
|
||||||
|
logging.error(f"eso_live_path_invalid_dir {live_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
ttc_update(live_path=live_path)
|
||||||
|
|
||||||
|
|
||||||
|
price_table_uri = "https://us.tamrieltradecentre.com/download/PriceTable"
|
||||||
|
price_table_name = "TamrielTradeCentre"
|
||||||
|
|
||||||
|
|
||||||
|
def ttc_update(live_path: Path):
|
||||||
|
response = requests.get(price_table_uri)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
temp_dir = TemporaryDirectory()
|
||||||
|
temp_path = Path(temp_dir.name)
|
||||||
|
|
||||||
|
zip_file = ZipFile(BytesIO(response.content))
|
||||||
|
zip_file.extractall(temp_path)
|
||||||
|
|
||||||
|
live_tamriel_trade_centre = live_path.joinpath("TamrielTradeCentre")
|
||||||
|
copytree(temp_path.absolute(), live_tamriel_trade_centre.absolute(), dirs_exist_ok=True)
|
||||||
|
|
||||||
|
logging.info(
|
||||||
|
f"tamriel trade centre price table updated: {live_tamriel_trade_centre}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
periodical_script()
|
|
@ -1,10 +1,10 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description="Elder Scrolls Online addon manager and a Tamriel Trade Centre price table updater."
|
Description=Run banana daily and on boot.
|
||||||
Wants=banana.timer
|
Wants=banana.timer
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
ExecStart=/usr/bin/banana
|
ExecStart=/home/deck/.local/bin/eso-banana-script
|
||||||
User=deck
|
User=deck
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -1,7 +0,0 @@
|
||||||
module banana
|
|
||||||
|
|
||||||
go 1.22.3
|
|
||||||
|
|
||||||
require github.com/alexflint/go-arg v1.5.0
|
|
||||||
|
|
||||||
require github.com/alexflint/go-scalar v1.2.0 // indirect
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="eso-banana",
|
||||||
|
version="0.0.2",
|
||||||
|
scripts=["banana.py"],
|
||||||
|
entry_points={
|
||||||
|
"console_scripts": [
|
||||||
|
"eso-banana-script = banana:periodical_script",
|
||||||
|
"eso-ttc = banana:ttc",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
install_requires=["packaging", "requests"],
|
||||||
|
python_requires=">3",
|
||||||
|
)
|
Loading…
Reference in New Issue