diff --git a/drivers/189Cloud/auth.go b/drivers/189Cloud/auth.go index a495ffe..0ecc0fe 100644 --- a/drivers/189Cloud/auth.go +++ b/drivers/189Cloud/auth.go @@ -110,12 +110,18 @@ func (d *Driver) refreshSession(ctx context.Context, accessToken string) error { } return domain.Errorf(domain.CodeAuthExpired, "%s", msg) } - if out.SessionKey == "" || out.SessionSecret == "" { - return domain.Errorf(domain.CodeAuthExpired, "刷新会话成功但缺少 sessionKey/sessionSecret") + if d.isFamily() { + if out.FamilySessionKey == "" || out.FamilySessionSecret == "" { + return domain.Errorf(domain.CodeAuthExpired, "刷新会话成功但缺少家庭云会话信息") + } + } else if out.SessionKey == "" || out.SessionSecret == "" { + return domain.Errorf(domain.CodeAuthExpired, "刷新会话成功但缺少个人云会话信息") } d.mu.Lock() d.sessionKey = out.SessionKey d.sessionSecret = out.SessionSecret + d.familyKey = out.FamilySessionKey + d.familySecret = out.FamilySessionSecret d.loginName = out.LoginName if out.RefreshToken != "" { d.refreshToken = out.RefreshToken diff --git a/drivers/189Cloud/config.go b/drivers/189Cloud/config.go index 82816e5..da1c1f8 100644 --- a/drivers/189Cloud/config.go +++ b/drivers/189Cloud/config.go @@ -5,9 +5,11 @@ import "litepan/pkg/jsonvalue" type flexString = jsonvalue.FlexibleString type Addition struct { - RefreshToken string `json:"refresh_token" label:"Token" type:"password" form:"required,full"` + AccessToken string `json:"access_token" label:"访问令牌 access_token" type:"password" form:"pair=auth"` + RefreshToken string `json:"refresh_token" label:"刷新令牌 refresh_token" type:"password" form:"required,pair=auth"` + SpaceType string `json:"space_type" label:"云空间" type:"select" options:"personal:个人云,family:家庭云" default:"personal" form:"pair=space"` DeleteMode string `json:"delete_mode" label:"删除模式" type:"select" options:"trash:移动到回收站,delete:永久删除" default:"trash" form:"pair=opts1"` DownloadMode string `json:"download_mode" label:"下载模式" type:"select" options:"redirect:302重定向,proxy:本机代理" default:"redirect" form:"pair=opts1"` - RootFolderID string `json:"root_folder_id" label:"根目录ID(默认 -11)" default:"-11" form:"pair=opts2"` + RootFolderID string `json:"root_folder_id" label:"根目录ID(默认空间根目录)" default:"-11" form:"pair=space" default_by:"space_type" defaults:"personal=-11,family=/"` CacheTTL flexString `json:"cache_ttl" label:"缓存时间(分钟)" type:"number" default:"30" form:"pair=opts2"` } diff --git a/drivers/189Cloud/driver.go b/drivers/189Cloud/driver.go index 6079109..0d55654 100644 --- a/drivers/189Cloud/driver.go +++ b/drivers/189Cloud/driver.go @@ -25,14 +25,19 @@ type Driver struct { refreshToken string sessionKey string sessionSecret string + familyKey string + familySecret string + familyID string loginName string + itemCache map[string]domain.FileItem + itemOrder []string } var config = driver.Config{ Name: "189_cloud", DisplayName: "天翼云盘", - Description: "天翼云盘 PC 接口接入,支持扫码登录和文件管理", - CardTags: []string{"个人云", "扫码登录", "支持302", "支持秒传"}, + Description: "天翼云盘 PC 接口接入,支持个人云、家庭云和文件管理", + CardTags: []string{"个人云", "家庭云", "扫码登录", "支持302", "支持秒传"}, SortOrder: 7, AuthLabel: "扫码登录", CardColor: "#FEC52C", @@ -77,16 +82,36 @@ func (d *Driver) Init(ctx context.Context) error { }) } d.mu.Lock() + if d.accessToken == "" { + d.accessToken = strings.TrimSpace(d.add.AccessToken) + } if d.refreshToken == "" { d.refreshToken = strings.TrimSpace(d.add.RefreshToken) } + access := d.accessToken empty := d.refreshToken == "" d.mu.Unlock() if empty { return domain.Errorf(domain.CodeValidation, "请先扫码登录获取天翼云盘授权") } if !d.hasSession() { - if _, err := d.doRefresh(ctx); err != nil { + if access != "" { + if err := d.refreshSession(ctx, access); err != nil { + if !isSessionExpired(err) { + return err + } + if _, err := d.doRefresh(ctx); err != nil { + return err + } + } + } else { + if _, err := d.doRefresh(ctx); err != nil { + return err + } + } + } + if d.isFamily() { + if err := d.ensureFamilyID(ctx); err != nil { return err } } @@ -123,21 +148,34 @@ func (d *Driver) ExplainConnectionError(technical string, saving bool) string { } func (d *Driver) ListFiles(ctx context.Context, parentID string) ([]domain.FileItem, error) { - parent := d.normalizeParent(parentID) + parent := d.apiParentID(parentID) + endpoint := apiURL + "/listFiles.action" + baseParams := map[string]string{ + "folderId": parent, + "fileType": "0", + "mediaAttr": "0", + "iconOption": "5", + } + if d.isFamily() { + endpoint = apiURL + "/family/file/listFiles.action" + baseParams["familyId"] = d.currentFamilyID() + baseParams["orderBy"] = "1" + baseParams["descending"] = "false" + } else { + baseParams["recursive"] = "0" + baseParams["orderBy"] = "filename" + baseParams["descending"] = "false" + } var out []domain.FileItem for page := 1; page < 10000; page++ { var resp listResp - err := d.apiRequest(ctx, http.MethodGet, apiURL+"/listFiles.action", map[string]string{ - "folderId": parent, - "fileType": "0", - "mediaAttr": "0", - "iconOption": "5", - "pageNum": itoa(page), - "pageSize": itoa(listPageSize), - "recursive": "0", - "orderBy": "filename", - "descending": "false", - }, &resp) + params := make(map[string]string, len(baseParams)+2) + for key, value := range baseParams { + params[key] = value + } + params["pageNum"] = itoa(page) + params["pageSize"] = itoa(listPageSize) + err := d.apiRequest(ctx, http.MethodGet, endpoint, params, &resp) if err != nil { return nil, err } @@ -158,12 +196,13 @@ func (d *Driver) ListFiles(ctx context.Context, parentID string) ([]domain.FileI break } } + d.rememberItems(out) return out, nil } func (d *Driver) GetFileInfo(ctx context.Context, fileID string) (*domain.FileItem, error) { id := strings.TrimSpace(fileID) - if id == "" || id == "/" || id == d.rootID() { + if id == "" || id == "/" || id == d.rootID() || (d.isFamily() && id == "-11") { return &domain.FileItem{ ID: d.rootID(), Name: "根目录", @@ -171,6 +210,12 @@ func (d *Driver) GetFileInfo(ctx context.Context, fileID string) (*domain.FileIt IDKind: domain.IDStable, }, nil } + if d.isFamily() { + if item, ok := d.cachedItem(id); ok { + return &item, nil + } + return d.findFamilyItem(ctx, id) + } raw, err := d.fetchFileInfo(ctx, id) if err != nil { diff --git a/drivers/189Cloud/family.go b/drivers/189Cloud/family.go new file mode 100644 index 0000000..119a318 --- /dev/null +++ b/drivers/189Cloud/family.go @@ -0,0 +1,135 @@ +package cloud189 + +import ( + "context" + "net/http" + "strings" + + "litepan/internal/domain" +) + +const familyItemCacheLimit = 4096 + +func (d *Driver) isFamily() bool { + return strings.EqualFold(strings.TrimSpace(d.add.SpaceType), "family") +} + +func (d *Driver) currentFamilyID() string { + d.mu.Lock() + defer d.mu.Unlock() + return d.familyID +} + +func (d *Driver) uploadSpaceKey() string { + if d.isFamily() { + return "family:" + d.currentFamilyID() + } + return "personal" +} + +func (d *Driver) ensureFamilyID(ctx context.Context) error { + if d.currentFamilyID() != "" { + return nil + } + var resp familyListResp + if err := d.apiRequest(ctx, http.MethodGet, apiURL+"/family/manage/getFamilyList.action", nil, &resp); err != nil { + return err + } + if len(resp.FamilyInfoResp) == 0 { + return domain.Errorf(domain.CodeValidation, "当前天翼账号没有可用的家庭云") + } + + d.mu.Lock() + loginName := d.loginName + d.mu.Unlock() + selected := "" + for _, info := range resp.FamilyInfoResp { + remark := strings.TrimSpace(info.RemarkName) + if remark != "" && strings.Contains(loginName, remark) { + selected = info.FamilyID.String() + break + } + } + if selected == "" { + for _, info := range resp.FamilyInfoResp { + if info.UseFlag != 0 { + selected = info.FamilyID.String() + break + } + } + } + if selected == "" { + selected = resp.FamilyInfoResp[0].FamilyID.String() + } + if selected == "" { + return domain.Errorf(domain.CodeDriverError, "天翼云盘未返回家庭云ID") + } + d.mu.Lock() + d.familyID = selected + d.mu.Unlock() + return nil +} + +func (d *Driver) rememberItems(items []domain.FileItem) { + d.mu.Lock() + defer d.mu.Unlock() + if d.itemCache == nil { + d.itemCache = make(map[string]domain.FileItem) + } + for _, item := range items { + if item.ID == "" { + continue + } + if _, exists := d.itemCache[item.ID]; !exists { + d.itemOrder = append(d.itemOrder, item.ID) + } + d.itemCache[item.ID] = item + } + for len(d.itemOrder) > familyItemCacheLimit { + id := d.itemOrder[0] + d.itemOrder = d.itemOrder[1:] + delete(d.itemCache, id) + } +} + +func (d *Driver) cachedItem(id string) (domain.FileItem, bool) { + d.mu.Lock() + defer d.mu.Unlock() + item, ok := d.itemCache[id] + return item, ok +} + +func (d *Driver) forgetItems(ids []string) { + d.mu.Lock() + defer d.mu.Unlock() + for _, id := range ids { + delete(d.itemCache, id) + } +} + +func (d *Driver) findFamilyItem(ctx context.Context, fileID string) (*domain.FileItem, error) { + queue := []string{d.rootID()} + visited := map[string]struct{}{} + for len(queue) > 0 { + parent := queue[0] + queue = queue[1:] + if _, ok := visited[parent]; ok { + continue + } + visited[parent] = struct{}{} + items, err := d.ListFiles(ctx, parent) + if err != nil { + return nil, err + } + for _, item := range items { + if item.ID == fileID { + found := item + return &found, nil + } + if item.IsDir { + queue = append(queue, item.ID) + } + } + } + return nil, domain.Errf(domain.CodeNotFound) +} diff --git a/drivers/189Cloud/models.go b/drivers/189Cloud/models.go index 309edbc..219deb1 100644 --- a/drivers/189Cloud/models.go +++ b/drivers/189Cloud/models.go @@ -20,12 +20,25 @@ type oauthRefreshResp struct { } type sessionResp struct { - ResCode json.RawMessage `json:"res_code"` - ResMessage string `json:"res_message"` - SessionKey string `json:"sessionKey"` - SessionSecret string `json:"sessionSecret"` - LoginName string `json:"loginName"` - RefreshToken string `json:"refreshToken"` + ResCode json.RawMessage `json:"res_code"` + ResMessage string `json:"res_message"` + AccessToken string `json:"accessToken"` + SessionKey string `json:"sessionKey"` + SessionSecret string `json:"sessionSecret"` + FamilySessionKey string `json:"familySessionKey"` + FamilySessionSecret string `json:"familySessionSecret"` + LoginName string `json:"loginName"` + RefreshToken string `json:"refreshToken"` +} + +type familyListResp struct { + FamilyInfoResp []familyInfo `json:"familyInfoResp"` +} + +type familyInfo struct { + FamilyID flexString `json:"familyId"` + RemarkName string `json:"remarkName"` + UseFlag int `json:"useFlag"` } type listResp struct { diff --git a/drivers/189Cloud/ops.go b/drivers/189Cloud/ops.go index 989ef0b..e9e2685 100644 --- a/drivers/189Cloud/ops.go +++ b/drivers/189Cloud/ops.go @@ -25,12 +25,21 @@ func (d *Driver) ResolveDownload(ctx context.Context, req driver.DownloadRequest if fileID == "" { return nil, domain.Errorf(domain.CodeValidation, "file_id 不能为空") } - var out map[string]any - if err := d.apiRequest(ctx, http.MethodGet, apiURL+"/getFileDownloadUrl.action", map[string]string{ + endpoint := apiURL + "/getFileDownloadUrl.action" + params := map[string]string{ "fileId": fileID, "dt": "3", "flag": "1", - }, &out); err != nil { + } + if d.isFamily() { + endpoint = apiURL + "/family/file/getFileDownloadUrl.action" + params = map[string]string{ + "fileId": fileID, + "familyId": d.currentFamilyID(), + } + } + var out map[string]any + if err := d.apiRequest(ctx, http.MethodGet, endpoint, params, &out); err != nil { return nil, err } downloadURL := firstString(anyString(out["fileDownloadUrl"]), anyString(out["downloadUrl"]), anyString(out["url"])) @@ -41,9 +50,13 @@ func (d *Driver) ResolveDownload(ctx context.Context, req driver.DownloadRequest downloadURL = regexp.MustCompile(`(?i)^http://`).ReplaceAllString(downloadURL, "https://") fileName := "" size := int64(0) - if raw, err := d.fetchFileInfo(ctx, fileID); err == nil && raw != nil { - fileName = strings.TrimSpace(raw.Name) - size = raw.size() + if d.isFamily() { + if item, err := d.GetFileInfo(ctx, fileID); err == nil && item != nil { + fileName = item.Name + size = item.Size + } + } else if raw, err := d.fetchFileInfo(ctx, fileID); err == nil && raw != nil { + fileName, size = strings.TrimSpace(raw.Name), raw.size() } headers := http.Header{} headers.Set("User-Agent", resolveUA(req.UA)) @@ -82,12 +95,22 @@ func (d *Driver) CreateFolder(ctx context.Context, parentID, name string) (*doma if err := uploadutil.ValidateFileName(folderName); err != nil { return nil, err } - var entry fileEntry - if err := d.apiRequest(ctx, http.MethodPost, apiURL+"/createFolder.action", map[string]string{ - "parentFolderId": d.normalizeParent(parentID), + endpoint := apiURL + "/createFolder.action" + params := map[string]string{ + "parentFolderId": d.apiParentID(parentID), "folderName": folderName, "relativePath": "", - }, &entry); err != nil { + } + if d.isFamily() { + endpoint = apiURL + "/family/file/createFolder.action" + params = map[string]string{ + "familyId": d.currentFamilyID(), + "parentId": d.apiParentID(parentID), + "folderName": folderName, + } + } + var entry fileEntry + if err := d.apiRequest(ctx, http.MethodPost, endpoint, params, &entry); err != nil { return nil, err } entry.isDir = true @@ -98,6 +121,7 @@ func (d *Driver) CreateFolder(ctx context.Context, parentID, name string) (*doma if item.Name == "" { item.Name = folderName } + d.rememberItems([]domain.FileItem{item}) return &item, nil } @@ -120,16 +144,30 @@ func (d *Driver) RenameFile(ctx context.Context, fileID, newName string) error { if err != nil { return err } - if item.IsDir { - return d.apiRequest(ctx, http.MethodPost, apiURL+"/renameFolder.action", map[string]string{ - "folderId": id, - "destFolderName": name, - }, nil) + method := http.MethodPost + prefix := apiURL + base := map[string]string{} + if d.isFamily() { + method = http.MethodGet + prefix = apiURL + "/family/file" + base["familyId"] = d.currentFamilyID() } - return d.apiRequest(ctx, http.MethodPost, apiURL+"/renameFile.action", map[string]string{ - "fileId": id, - "destFileName": name, - }, nil) + if item.IsDir { + base["folderId"] = id + base["destFolderName"] = name + if err := d.apiRequest(ctx, method, prefix+"/renameFolder.action", base, nil); err != nil { + return err + } + } else { + base["fileId"] = id + base["destFileName"] = name + if err := d.apiRequest(ctx, method, prefix+"/renameFile.action", base, nil); err != nil { + return err + } + } + item.Name = name + d.rememberItems([]domain.FileItem{*item}) + return nil } func (d *Driver) DeleteFiles(ctx context.Context, fileIDs []string) error { @@ -159,8 +197,10 @@ func (d *Driver) DeleteFiles(ctx context.Context, fileIDs []string) error { if err := d.waitBatchTask(ctx, "CLEAR_RECYCLE", clearID, time.Second, 40*time.Second); err != nil { return err } + d.forgetItems(ids) return nil } + d.forgetItems(ids) return nil } @@ -184,7 +224,7 @@ func (d *Driver) transferFiles(ctx context.Context, taskType string, fileIDs []s if err != nil { return err } - taskID, err := d.createBatchTask(ctx, taskType, taskInfos, d.normalizeParent(targetParentID), nil) + taskID, err := d.createBatchTask(ctx, taskType, taskInfos, d.apiParentID(targetParentID), nil) if err != nil { return err } @@ -250,6 +290,9 @@ func (d *Driver) createBatchTask(ctx context.Context, taskType string, taskInfos form.Set("type", taskType) form.Set("taskInfos", string(raw)) form.Set("targetFolderId", targetFolderID) + if d.isFamily() { + form.Set("familyId", d.currentFamilyID()) + } for k, v := range extra { form.Set(k, v) } @@ -271,7 +314,7 @@ func (d *Driver) waitBatchTask(ctx context.Context, taskType, taskID string, int form.Set("type", taskType) form.Set("taskId", taskID) var resp map[string]any - if err := d.formRequest(ctx, http.MethodPost, apiURL+"/batch/checkBatchTask.action", form, &resp); err != nil { + if err := d.formRequestFor(ctx, http.MethodPost, apiURL+"/batch/checkBatchTask.action", form, &resp, false); err != nil { return err } status := anyInt(resp["taskStatus"]) diff --git a/drivers/189Cloud/qrlogin.go b/drivers/189Cloud/qrlogin.go index 31dd26a..d194595 100644 --- a/drivers/189Cloud/qrlogin.go +++ b/drivers/189Cloud/qrlogin.go @@ -228,6 +228,7 @@ func finalizeQRLogin(ctx context.Context, client *http.Client, redirectURL strin return &driver.QRPollResult{ Status: driver.QRSuccess, Credentials: domain.AuthCredentials{ + AccessToken: out.AccessToken, RefreshToken: out.RefreshToken, TokenExpires: time.Now().Add(7 * 24 * time.Hour), }, diff --git a/drivers/189Cloud/transport.go b/drivers/189Cloud/transport.go index 44a2e2e..ac730ed 100644 --- a/drivers/189Cloud/transport.go +++ b/drivers/189Cloud/transport.go @@ -47,29 +47,66 @@ const ( ) func (d *Driver) rootID() string { - if id := strings.TrimSpace(d.add.RootFolderID); id != "" && id != "/" && id != "0" { + id := strings.TrimSpace(d.add.RootFolderID) + if !d.isRootAlias(id) { return id } + if d.isFamily() { + return "/" + } return "-11" } func (d *Driver) normalizeParent(parentID string) string { p := strings.TrimSpace(parentID) - if p == "" || p == "/" || p == "0" || p == "root" { + if d.isRootAlias(p) { return d.rootID() } return p } +func (d *Driver) isRootAlias(id string) bool { + switch strings.ToLower(strings.TrimSpace(id)) { + case "", "/", "0", "root": + return true + case "-11", "home": + return d.isFamily() + default: + return false + } +} + +func (d *Driver) apiParentID(parentID string) string { + parent := d.normalizeParent(parentID) + if d.isFamily() && d.isRootAlias(parent) { + return "" + } + return parent +} + func (d *Driver) hasSession() bool { + return d.hasSessionFor(d.isFamily()) +} + +func (d *Driver) hasSessionFor(family bool) bool { d.mu.Lock() defer d.mu.Unlock() + if family { + return d.familyKey != "" && d.familySecret != "" + } return d.sessionKey != "" && d.sessionSecret != "" } func (d *Driver) currentSession() (string, string) { + return d.currentSessionFor(d.isFamily()) +} + +func (d *Driver) currentSessionFor(family bool) (string, string) { d.mu.Lock() defer d.mu.Unlock() + if family { + return d.familyKey, d.familySecret + } return d.sessionKey, d.sessionSecret } @@ -115,16 +152,20 @@ func (d *Driver) apiRequest(ctx context.Context, method, rawURL string, params m } func (d *Driver) formRequest(ctx context.Context, method, rawURL string, form url.Values, out any) error { - if !d.hasSession() { + return d.formRequestFor(ctx, method, rawURL, form, out, d.isFamily()) +} + +func (d *Driver) formRequestFor(ctx context.Context, method, rawURL string, form url.Values, out any, family bool) error { + if !d.hasSessionFor(family) { if _, err := d.doRefresh(ctx); err != nil { return err } } - if err := d.signedForm(ctx, method, rawURL, form, out); isSessionExpired(err) { + if err := d.signedFormFor(ctx, method, rawURL, form, out, family); isSessionExpired(err) { if _, rerr := d.doRefresh(ctx); rerr != nil { return rerr } - return d.signedForm(ctx, method, rawURL, form, out) + return d.signedFormFor(ctx, method, rawURL, form, out, family) } else if err != nil { return err } @@ -134,9 +175,7 @@ func (d *Driver) formRequest(ctx context.Context, method, rawURL string, form ur func (d *Driver) signedJSON(ctx context.Context, method, rawURL string, params map[string]string, out any) error { query := clientSuffix() for k, v := range params { - if strings.TrimSpace(v) != "" { - query.Set(k, v) - } + query.Set(k, v) } headers, err := d.signatureHeaders(method, rawURL, "") if err != nil { @@ -146,8 +185,12 @@ func (d *Driver) signedJSON(ctx context.Context, method, rawURL string, params m } func (d *Driver) signedForm(ctx context.Context, method, rawURL string, form url.Values, out any) error { + return d.signedFormFor(ctx, method, rawURL, form, out, d.isFamily()) +} + +func (d *Driver) signedFormFor(ctx context.Context, method, rawURL string, form url.Values, out any, family bool) error { query := clientSuffix() - headers, err := d.signatureHeaders(method, rawURL, "") + headers, err := d.signatureHeadersFor(method, rawURL, "", family) if err != nil { return err } @@ -168,6 +211,9 @@ func (d *Driver) rawJSON(ctx context.Context, method, rawURL string, query url.V if err != nil { return domain.Wrap(domain.CodeDriverError, err) } + if is189AuthExpiredPayload(data) { + return domain.Errorf(domain.CodeAuthExpired, "天翼云盘认证会话已失效") + } if resp.StatusCode != http.StatusOK { return domain.Errorf(domain.CodeDriverError, "天翼云盘 API HTTP %d: %s", resp.StatusCode, httpx.Truncate(data, 300)) } @@ -193,6 +239,9 @@ func (d *Driver) rawForm(ctx context.Context, method, rawURL string, query url.V if err != nil { return domain.Wrap(domain.CodeDriverError, err) } + if is189AuthExpiredPayload(data) { + return domain.Errorf(domain.CodeAuthExpired, "天翼云盘认证会话已失效") + } if resp.StatusCode != http.StatusOK { return domain.Errorf(domain.CodeDriverError, "天翼云盘 API HTTP %d: %s", resp.StatusCode, httpx.Truncate(data, 300)) } @@ -200,7 +249,7 @@ func (d *Driver) rawForm(ctx context.Context, method, rawURL string, query url.V } func parse189Response(data []byte, out any) error { - if bytes.Contains(data, []byte("InvalidSessionKey")) || bytes.Contains(data, []byte("userSessionBO is null")) { + if is189AuthExpiredPayload(data) { return domain.Errorf(domain.CodeAuthExpired, "天翼云盘会话已失效") } trimmed := bytes.TrimSpace(data) @@ -255,6 +304,14 @@ func parse189XMLResponse(data []byte, out any) error { return nil } +func is189AuthExpiredPayload(data []byte) bool { + lower := bytes.ToLower(data) + return bytes.Contains(lower, []byte("invalidsessionkey")) || + bytes.Contains(lower, []byte("usersessionbo is null")) || + bytes.Contains(lower, []byte("userinvalidopentoken")) || + bytes.Contains(lower, []byte("unifyaccountinfo is null")) +} + func responseMessage(env map[string]json.RawMessage, keys ...string) string { for _, key := range keys { var s string @@ -288,11 +345,19 @@ func isSessionExpired(err error) bool { return true } msg := err.Error() - return strings.Contains(msg, "InvalidSessionKey") || strings.Contains(msg, "userSessionBO is null") + lower := strings.ToLower(msg) + return strings.Contains(lower, "invalidsessionkey") || + strings.Contains(lower, "usersessionbo is null") || + strings.Contains(lower, "userinvalidopentoken") || + strings.Contains(lower, "unifyaccountinfo is null") } func (d *Driver) signatureHeaders(method, rawURL, params string) (map[string]string, error) { - sessionKey, sessionSecret := d.currentSession() + return d.signatureHeadersFor(method, rawURL, params, d.isFamily()) +} + +func (d *Driver) signatureHeadersFor(method, rawURL, params string, family bool) (map[string]string, error) { + sessionKey, sessionSecret := d.currentSessionFor(family) if sessionKey == "" || sessionSecret == "" { return nil, domain.Errorf(domain.CodeAuthExpired, "天翼云盘会话未初始化") } diff --git a/drivers/189Cloud/upload.go b/drivers/189Cloud/upload.go index 67ae2b2..8a97cdd 100644 --- a/drivers/189Cloud/upload.go +++ b/drivers/189Cloud/upload.go @@ -7,8 +7,10 @@ import ( "encoding/base64" "encoding/hex" "encoding/xml" + "errors" "fmt" "io" + "net" "net/http" "net/url" "os" @@ -16,6 +18,7 @@ import ( "sort" "strconv" "strings" + "time" "litepan/internal/domain" "litepan/internal/driver" @@ -30,6 +33,7 @@ type uploadHashInfo struct { } type cloud189ResumeCtx struct { + space string parentID string requestedName string targetName string @@ -58,8 +62,9 @@ func (d *Driver) UploadLocalFile(ctx context.Context, req driver.LocalUploadRequ fileSize := local.Size partSize := uploadPartSize(fileSize) policy := uploadutil.NormalizeConflictPolicy(req.ConflictPolicy) + space := d.uploadSpaceKey() - resume := normalize189ResumeState(req.ResumeState, parentID, requestedName, fileSize, partSize, uploadHashInfo{}) + resume := normalize189ResumeState(req.ResumeState, space, parentID, requestedName, fileSize, partSize, uploadHashInfo{}) hadResumeCandidate := resume != nil targetName := requestedName if resume != nil { @@ -85,7 +90,7 @@ func (d *Driver) UploadLocalFile(ctx context.Context, req driver.LocalUploadRequ if err != nil { return nil, err } - resume = normalize189ResumeState(req.ResumeState, parentID, requestedName, fileSize, partSize, hashInfo) + resume = normalize189ResumeState(req.ResumeState, space, parentID, requestedName, fileSize, partSize, hashInfo) if hadResumeCandidate && resume == nil { var skipped bool targetName, skipped, err = d.prepareUploadName(ctx, parentID, requestedName, policy) @@ -110,11 +115,14 @@ func (d *Driver) UploadLocalFile(ctx context.Context, req driver.LocalUploadRequ } else { uploadutil.NotifyProgress(req.OnProgress, 0, fileSize, "正在创建上传会话") initParams := map[string]string{ - "parentFolderId": parentID, + "parentFolderId": d.apiParentID(parentID), "fileName": url.QueryEscape(targetName), "fileSize": fmt.Sprintf("%d", fileSize), "sliceSize": fmt.Sprintf("%d", partSize), } + if d.isFamily() { + initParams["familyId"] = d.currentFamilyID() + } if singlePart || emptyFile { initParams["fileMd5"] = hashInfo.fileMD5 initParams["sliceMd5"] = hashInfo.sliceMD5 @@ -132,6 +140,7 @@ func (d *Driver) UploadLocalFile(ctx context.Context, req driver.LocalUploadRequ } rapid = anyInt(initData["fileDataExists"]) == 1 || anyInt(initResp["fileDataExists"]) == 1 resume = &cloud189ResumeCtx{ + space: space, parentID: parentID, requestedName: requestedName, targetName: targetName, @@ -153,10 +162,7 @@ func (d *Driver) UploadLocalFile(ctx context.Context, req driver.LocalUploadRequ if _, completed := resume.completedParts[partNumber]; completed { continue } - urlsResp, err := d.uploadEncryptedRequest(ctx, "getMultiUploadUrls", map[string]string{ - "uploadFileId": uploadFileID, - "partInfo": partInfo, - }) + urlsResp, err := d.getMultiUploadURLs(ctx, uploadFileID, partInfo, partNumber, totalParts) if err != nil { return nil, err } @@ -214,8 +220,70 @@ func (d *Driver) UploadLocalFile(ctx context.Context, req driver.LocalUploadRequ }, nil } -func normalize189ResumeState(state map[string]any, parentID, requestedName string, fileSize, partSize int64, hashes uploadHashInfo) *cloud189ResumeCtx { - if len(state) == 0 || strings.TrimSpace(uploadutil.AnyString(state["parent_id"])) != parentID || +func (d *Driver) getMultiUploadURLs(ctx context.Context, uploadFileID, partInfo string, partNumber, totalParts int) (map[string]any, error) { + var lastErr error + for attempt := 0; attempt < 3; attempt++ { + if attempt > 0 { + delay := time.Duration(attempt) * 300 * time.Millisecond + timer := time.NewTimer(delay) + select { + case <-ctx.Done(): + timer.Stop() + return nil, ctx.Err() + case <-timer.C: + } + } + resp, err := d.uploadEncryptedRequest(ctx, "getMultiUploadUrls", map[string]string{ + "uploadFileId": uploadFileID, + "partInfo": partInfo, + }) + if err == nil { + return resp, nil + } + lastErr = err + if !retryableUploadURLFailure(ctx, err) { + return nil, err + } + } + return nil, domain.Errorf( + domain.CodeDriverError, + "获取第 %d/%d 个分片上传地址失败(已自动重试 2 次):%s", + partNumber, + totalParts, + rootErrorMessage(lastErr), + ) +} + +func retryableUploadURLFailure(ctx context.Context, err error) bool { + if err == nil || ctx.Err() != nil || isSessionExpired(err) { + return false + } + var urlErr *url.Error + var netErr net.Error + return errors.As(err, &urlErr) || + errors.As(err, &netErr) || + errors.Is(err, io.EOF) || + errors.Is(err, io.ErrUnexpectedEOF) +} + +func rootErrorMessage(err error) string { + for err != nil { + next := errors.Unwrap(err) + if next == nil { + return err.Error() + } + err = next + } + return "未知网络错误" +} + +func normalize189ResumeState(state map[string]any, space, parentID, requestedName string, fileSize, partSize int64, hashes uploadHashInfo) *cloud189ResumeCtx { + stateSpace := strings.TrimSpace(uploadutil.AnyString(state["space"])) + if stateSpace == "" { + stateSpace = "personal" + } + if len(state) == 0 || stateSpace != space || + strings.TrimSpace(uploadutil.AnyString(state["parent_id"])) != parentID || strings.TrimSpace(uploadutil.AnyString(state["requested_name"])) != requestedName { return nil } @@ -236,6 +304,7 @@ func normalize189ResumeState(state map[string]any, parentID, requestedName strin totalParts = int((fileSize + partSize - 1) / partSize) } return &cloud189ResumeCtx{ + space: space, parentID: parentID, requestedName: requestedName, targetName: targetName, @@ -259,6 +328,7 @@ func persist189ResumeState(onState driver.UploadStateCallback, resume *cloud189R progress = 99 } onState(map[string]any{ + "space": resume.space, "parent_id": resume.parentID, "requested_name": resume.requestedName, "target_name": resume.targetName, @@ -410,7 +480,11 @@ func (d *Driver) uploadEncryptedRequestOnce(ctx context.Context, endpoint string if err != nil { return nil, err } - rawURL := uploadURL + "/person/" + strings.TrimLeft(endpoint, "/") + space := "person" + if d.isFamily() { + space = "family" + } + rawURL := uploadURL + "/" + space + "/" + strings.TrimLeft(endpoint, "/") query := clientSuffix() query.Set("params", encrypted) headers, err := d.signatureHeaders(http.MethodGet, rawURL, encrypted) @@ -582,6 +656,13 @@ func (d *Driver) ResolveTransferHash(ctx context.Context, item *domain.FileItem, if !allowStream || item == nil || strings.TrimSpace(item.ID) == "" { return "", nil } + if d.isFamily() { + info, err := d.GetFileInfo(ctx, item.ID) + if err != nil { + return "", err + } + return driver.HashFromItem(info, "md5"), nil + } info, err := d.fetchFileInfo(ctx, item.ID) if err != nil { return "", err @@ -608,13 +689,19 @@ func (d *Driver) RapidUploadByHash(ctx context.Context, req driver.RapidUploadRe operation = "3" } var committed rapidCommitResp - if err := d.formRequest(ctx, http.MethodPost, commitURL, url.Values{ - "opertype": {operation}, - "resumePolicy": {"1"}, - "uploadFileId": {uploadFileID}, - "isLog": {"0"}, - }, &committed); err != nil { - return nil, err + if d.isFamily() { + if err := d.commitFamilyRapidUpload(ctx, commitURL, uploadFileID, &committed); err != nil { + return nil, err + } + } else { + if err := d.formRequest(ctx, http.MethodPost, commitURL, url.Values{ + "opertype": {operation}, + "resumePolicy": {"1"}, + "uploadFileId": {uploadFileID}, + "isLog": {"0"}, + }, &committed); err != nil { + return nil, err + } } return &driver.RapidUploadResult{ Reuse: true, @@ -659,21 +746,55 @@ func (d *Driver) createRapidUpload(ctx context.Context, req driver.RapidUploadRe parentID := d.normalizeParent(req.ParentID) var created rapidCreateResp - if err := d.formRequest(ctx, http.MethodPost, apiURL+"/createUploadFile.action", url.Values{ - "parentFolderId": {parentID}, - "fileName": {fileName}, - "size": {strconv.FormatInt(req.Size, 10)}, - "md5": {hash}, - "opertype": {"3"}, - "flag": {"1"}, - "resumePolicy": {"1"}, - "isLog": {"0"}, - }, &created); err != nil { - return rapidCreateResp{}, "", err + if d.isFamily() { + if err := d.apiRequest(ctx, http.MethodPost, apiURL+"/family/file/createFamilyFile.action", map[string]string{ + "familyId": d.currentFamilyID(), + "parentId": d.apiParentID(parentID), + "fileMd5": hash, + "fileName": fileName, + "fileSize": strconv.FormatInt(req.Size, 10), + "resumePolicy": "1", + }, &created); err != nil { + return rapidCreateResp{}, "", err + } + } else { + if err := d.formRequest(ctx, http.MethodPost, apiURL+"/createUploadFile.action", url.Values{ + "parentFolderId": {parentID}, + "fileName": {fileName}, + "size": {strconv.FormatInt(req.Size, 10)}, + "md5": {hash}, + "opertype": {"3"}, + "flag": {"1"}, + "resumePolicy": {"1"}, + "isLog": {"0"}, + }, &created); err != nil { + return rapidCreateResp{}, "", err + } } return created, parentID, nil } +func (d *Driver) commitFamilyRapidUpload(ctx context.Context, commitURL, uploadFileID string, out any) error { + commit := func() error { + headers, err := d.signatureHeadersFor(http.MethodPost, commitURL, "", true) + if err != nil { + return err + } + headers["ResumePolicy"] = "1" + headers["UploadFileId"] = uploadFileID + headers["FamilyId"] = d.currentFamilyID() + return d.rawForm(ctx, http.MethodPost, commitURL, clientSuffix(), url.Values{}, headers, out) + } + if err := commit(); isSessionExpired(err) { + if _, refreshErr := d.doRefresh(ctx); refreshErr != nil { + return refreshErr + } + return commit() + } else { + return err + } +} + func normalize189RapidProbeError(err error) error { message := err.Error() switch { diff --git a/internal/api/web/assets/AccountFolderField-CIw1Ylua.js.gz b/internal/api/web/assets/AccountFolderField-CIw1Ylua.js.gz new file mode 100644 index 0000000..5bc135f Binary files /dev/null and b/internal/api/web/assets/AccountFolderField-CIw1Ylua.js.gz differ diff --git a/internal/api/web/assets/AccountFolderField-DkNVBU67.js.gz b/internal/api/web/assets/AccountFolderField-DkNVBU67.js.gz deleted file mode 100644 index 0aace02..0000000 Binary files a/internal/api/web/assets/AccountFolderField-DkNVBU67.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AccountManagement-BEOp1GKI.css.gz b/internal/api/web/assets/AccountManagement-BEOp1GKI.css.gz new file mode 100644 index 0000000..0711748 Binary files /dev/null and b/internal/api/web/assets/AccountManagement-BEOp1GKI.css.gz differ diff --git a/internal/api/web/assets/AccountManagement-Blcjaw7X.js.gz b/internal/api/web/assets/AccountManagement-Blcjaw7X.js.gz new file mode 100644 index 0000000..4558ecc Binary files /dev/null and b/internal/api/web/assets/AccountManagement-Blcjaw7X.js.gz differ diff --git a/internal/api/web/assets/AccountManagement-CJtL5r-2.js.gz b/internal/api/web/assets/AccountManagement-CJtL5r-2.js.gz deleted file mode 100644 index 83ae226..0000000 Binary files a/internal/api/web/assets/AccountManagement-CJtL5r-2.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AccountManagement-CUR0Aho_.css.gz b/internal/api/web/assets/AccountManagement-CUR0Aho_.css.gz deleted file mode 100644 index 8ef6600..0000000 Binary files a/internal/api/web/assets/AccountManagement-CUR0Aho_.css.gz and /dev/null differ diff --git a/internal/api/web/assets/AdminRunStatusCell-HpyFM-4D.js.gz b/internal/api/web/assets/AdminRunStatusCell-HpyFM-4D.js.gz deleted file mode 100644 index 8539869..0000000 Binary files a/internal/api/web/assets/AdminRunStatusCell-HpyFM-4D.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AdminRunStatusCell-INFHRb4M.js.gz b/internal/api/web/assets/AdminRunStatusCell-INFHRb4M.js.gz new file mode 100644 index 0000000..4bd092b Binary files /dev/null and b/internal/api/web/assets/AdminRunStatusCell-INFHRb4M.js.gz differ diff --git a/internal/api/web/assets/AdminSettingsDrawer-6cTAe_Ud.js.gz b/internal/api/web/assets/AdminSettingsDrawer-6cTAe_Ud.js.gz new file mode 100644 index 0000000..d683254 Binary files /dev/null and b/internal/api/web/assets/AdminSettingsDrawer-6cTAe_Ud.js.gz differ diff --git a/internal/api/web/assets/AdminSettingsDrawer-87OL9yAv.js.gz b/internal/api/web/assets/AdminSettingsDrawer-87OL9yAv.js.gz deleted file mode 100644 index b415c54..0000000 Binary files a/internal/api/web/assets/AdminSettingsDrawer-87OL9yAv.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AdminTaskTabHeader-DblYizs8.js.gz b/internal/api/web/assets/AdminTaskTabHeader-DblYizs8.js.gz deleted file mode 100644 index d176172..0000000 Binary files a/internal/api/web/assets/AdminTaskTabHeader-DblYizs8.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AdminTaskTabHeader-FY2QGnx8.js.gz b/internal/api/web/assets/AdminTaskTabHeader-FY2QGnx8.js.gz new file mode 100644 index 0000000..f2067f1 Binary files /dev/null and b/internal/api/web/assets/AdminTaskTabHeader-FY2QGnx8.js.gz differ diff --git a/internal/api/web/assets/AdminView-BOLqZ0_X.js.gz b/internal/api/web/assets/AdminView-BOLqZ0_X.js.gz deleted file mode 100644 index 8cf8766..0000000 Binary files a/internal/api/web/assets/AdminView-BOLqZ0_X.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AdminView-gBaQgH_5.js.gz b/internal/api/web/assets/AdminView-gBaQgH_5.js.gz new file mode 100644 index 0000000..6639f9c Binary files /dev/null and b/internal/api/web/assets/AdminView-gBaQgH_5.js.gz differ diff --git a/internal/api/web/assets/AppBadge-BJvjGBzh.js.gz b/internal/api/web/assets/AppBadge-BJvjGBzh.js.gz deleted file mode 100644 index 04f4d49..0000000 Binary files a/internal/api/web/assets/AppBadge-BJvjGBzh.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AppBadge-CQ8E0-y1.js.gz b/internal/api/web/assets/AppBadge-CQ8E0-y1.js.gz new file mode 100644 index 0000000..e0acde3 Binary files /dev/null and b/internal/api/web/assets/AppBadge-CQ8E0-y1.js.gz differ diff --git a/internal/api/web/assets/AppCardActionButton-BoUTPMMG.js.gz b/internal/api/web/assets/AppCardActionButton-BoUTPMMG.js.gz deleted file mode 100644 index b7c3c73..0000000 Binary files a/internal/api/web/assets/AppCardActionButton-BoUTPMMG.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AppCardActionButton-r0InvKOm.js.gz b/internal/api/web/assets/AppCardActionButton-r0InvKOm.js.gz new file mode 100644 index 0000000..fa4ab6a Binary files /dev/null and b/internal/api/web/assets/AppCardActionButton-r0InvKOm.js.gz differ diff --git a/internal/api/web/assets/AppDropdown-BxDK8gWu.js.gz b/internal/api/web/assets/AppDropdown-BxDK8gWu.js.gz new file mode 100644 index 0000000..e3608b4 Binary files /dev/null and b/internal/api/web/assets/AppDropdown-BxDK8gWu.js.gz differ diff --git a/internal/api/web/assets/AppDropdown-Cqo1Bi2N.js.gz b/internal/api/web/assets/AppDropdown-Cqo1Bi2N.js.gz deleted file mode 100644 index dcf1d7f..0000000 Binary files a/internal/api/web/assets/AppDropdown-Cqo1Bi2N.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AppIconButton-BFep9w9n.js.gz b/internal/api/web/assets/AppIconButton-BFep9w9n.js.gz deleted file mode 100644 index 8d18484..0000000 Binary files a/internal/api/web/assets/AppIconButton-BFep9w9n.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AppIconButton-CJ3LMNcA.js.gz b/internal/api/web/assets/AppIconButton-CJ3LMNcA.js.gz new file mode 100644 index 0000000..29f56b3 Binary files /dev/null and b/internal/api/web/assets/AppIconButton-CJ3LMNcA.js.gz differ diff --git a/internal/api/web/assets/AppInput-CykMJKqh.js.gz b/internal/api/web/assets/AppInput-CykMJKqh.js.gz deleted file mode 100644 index b0f1565..0000000 Binary files a/internal/api/web/assets/AppInput-CykMJKqh.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AppInput-ksY5PNH0.js.gz b/internal/api/web/assets/AppInput-ksY5PNH0.js.gz new file mode 100644 index 0000000..6a09cbd Binary files /dev/null and b/internal/api/web/assets/AppInput-ksY5PNH0.js.gz differ diff --git a/internal/api/web/assets/AppSelect-BsQzmvJt.js.gz b/internal/api/web/assets/AppSelect-BsQzmvJt.js.gz new file mode 100644 index 0000000..b386662 Binary files /dev/null and b/internal/api/web/assets/AppSelect-BsQzmvJt.js.gz differ diff --git a/internal/api/web/assets/AppSelect-CPmZfLvJ.js.gz b/internal/api/web/assets/AppSelect-CPmZfLvJ.js.gz deleted file mode 100644 index 312a279..0000000 Binary files a/internal/api/web/assets/AppSelect-CPmZfLvJ.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AppStateBlock-D2XNyiD5.js.gz b/internal/api/web/assets/AppStateBlock-D2XNyiD5.js.gz new file mode 100644 index 0000000..ef69490 Binary files /dev/null and b/internal/api/web/assets/AppStateBlock-D2XNyiD5.js.gz differ diff --git a/internal/api/web/assets/AppStateBlock-VEZ7AYkk.js.gz b/internal/api/web/assets/AppStateBlock-VEZ7AYkk.js.gz deleted file mode 100644 index 0ca9c29..0000000 Binary files a/internal/api/web/assets/AppStateBlock-VEZ7AYkk.js.gz and /dev/null differ diff --git a/internal/api/web/assets/ArchivePreview-DN44WfL2.js.gz b/internal/api/web/assets/ArchivePreview-DN44WfL2.js.gz deleted file mode 100644 index 97702f9..0000000 Binary files a/internal/api/web/assets/ArchivePreview-DN44WfL2.js.gz and /dev/null differ diff --git a/internal/api/web/assets/ArchivePreview-EuguVgxk.js.gz b/internal/api/web/assets/ArchivePreview-EuguVgxk.js.gz new file mode 100644 index 0000000..c2c6c04 Binary files /dev/null and b/internal/api/web/assets/ArchivePreview-EuguVgxk.js.gz differ diff --git a/internal/api/web/assets/AudioPreview-BIERFaeu.js.gz b/internal/api/web/assets/AudioPreview-BIERFaeu.js.gz new file mode 100644 index 0000000..ecb5467 Binary files /dev/null and b/internal/api/web/assets/AudioPreview-BIERFaeu.js.gz differ diff --git a/internal/api/web/assets/AudioPreview-D7sJz8EF.js.gz b/internal/api/web/assets/AudioPreview-D7sJz8EF.js.gz deleted file mode 100644 index 43a6f1e..0000000 Binary files a/internal/api/web/assets/AudioPreview-D7sJz8EF.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AutomationPanel-Ct6CKUg8.js.gz b/internal/api/web/assets/AutomationPanel-Ct6CKUg8.js.gz deleted file mode 100644 index 28c0045..0000000 Binary files a/internal/api/web/assets/AutomationPanel-Ct6CKUg8.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AutomationPanel-DXiwvVzZ.js.gz b/internal/api/web/assets/AutomationPanel-DXiwvVzZ.js.gz new file mode 100644 index 0000000..da7e0ae Binary files /dev/null and b/internal/api/web/assets/AutomationPanel-DXiwvVzZ.js.gz differ diff --git a/internal/api/web/assets/AuxToolsManagement-Ccd8nmBh.js.gz b/internal/api/web/assets/AuxToolsManagement-Ccd8nmBh.js.gz deleted file mode 100644 index 9732996..0000000 Binary files a/internal/api/web/assets/AuxToolsManagement-Ccd8nmBh.js.gz and /dev/null differ diff --git a/internal/api/web/assets/AuxToolsManagement-IAUY2BRc.js.gz b/internal/api/web/assets/AuxToolsManagement-IAUY2BRc.js.gz new file mode 100644 index 0000000..5af348f Binary files /dev/null and b/internal/api/web/assets/AuxToolsManagement-IAUY2BRc.js.gz differ diff --git a/internal/api/web/assets/BreadcrumbNav-B9QtCeZS.js.gz b/internal/api/web/assets/BreadcrumbNav-B9QtCeZS.js.gz deleted file mode 100644 index 4bb330b..0000000 Binary files a/internal/api/web/assets/BreadcrumbNav-B9QtCeZS.js.gz and /dev/null differ diff --git a/internal/api/web/assets/BreadcrumbNav-IXF-GL5l.js.gz b/internal/api/web/assets/BreadcrumbNav-IXF-GL5l.js.gz new file mode 100644 index 0000000..765eca6 Binary files /dev/null and b/internal/api/web/assets/BreadcrumbNav-IXF-GL5l.js.gz differ diff --git a/internal/api/web/assets/BusySpinner-C1pg354I.js.gz b/internal/api/web/assets/BusySpinner-C1pg354I.js.gz new file mode 100644 index 0000000..d344265 Binary files /dev/null and b/internal/api/web/assets/BusySpinner-C1pg354I.js.gz differ diff --git a/internal/api/web/assets/BusySpinner-D2xCGgmz.js.gz b/internal/api/web/assets/BusySpinner-D2xCGgmz.js.gz deleted file mode 100644 index ca4bef5..0000000 Binary files a/internal/api/web/assets/BusySpinner-D2xCGgmz.js.gz and /dev/null differ diff --git a/internal/api/web/assets/CacheRetentionPanel-CyV-YIjQ.js.gz b/internal/api/web/assets/CacheRetentionPanel-CyV-YIjQ.js.gz deleted file mode 100644 index e898451..0000000 Binary files a/internal/api/web/assets/CacheRetentionPanel-CyV-YIjQ.js.gz and /dev/null differ diff --git a/internal/api/web/assets/CacheRetentionPanel-ICgd7bSF.js.gz b/internal/api/web/assets/CacheRetentionPanel-ICgd7bSF.js.gz new file mode 100644 index 0000000..97a92b6 Binary files /dev/null and b/internal/api/web/assets/CacheRetentionPanel-ICgd7bSF.js.gz differ diff --git a/internal/api/web/assets/CacheSettingsPanel-N1NWB64_.js.gz b/internal/api/web/assets/CacheSettingsPanel-N1NWB64_.js.gz new file mode 100644 index 0000000..f7b24b8 Binary files /dev/null and b/internal/api/web/assets/CacheSettingsPanel-N1NWB64_.js.gz differ diff --git a/internal/api/web/assets/CacheSettingsPanel-wrvgfLH2.js.gz b/internal/api/web/assets/CacheSettingsPanel-wrvgfLH2.js.gz deleted file mode 100644 index a757abb..0000000 Binary files a/internal/api/web/assets/CacheSettingsPanel-wrvgfLH2.js.gz and /dev/null differ diff --git a/internal/api/web/assets/CrossDriveTransfer-B6SLMoCh.js.gz b/internal/api/web/assets/CrossDriveTransfer-B6SLMoCh.js.gz deleted file mode 100644 index b615036..0000000 Binary files a/internal/api/web/assets/CrossDriveTransfer-B6SLMoCh.js.gz and /dev/null differ diff --git a/internal/api/web/assets/CrossDriveTransfer-BQE95xwF.js.gz b/internal/api/web/assets/CrossDriveTransfer-BQE95xwF.js.gz new file mode 100644 index 0000000..e2bc439 Binary files /dev/null and b/internal/api/web/assets/CrossDriveTransfer-BQE95xwF.js.gz differ diff --git a/internal/api/web/assets/DashboardManagement-C-67HbEv.js.gz b/internal/api/web/assets/DashboardManagement-C-67HbEv.js.gz deleted file mode 100644 index 8f3c385..0000000 Binary files a/internal/api/web/assets/DashboardManagement-C-67HbEv.js.gz and /dev/null differ diff --git a/internal/api/web/assets/DashboardManagement-XKUzMfjC.js.gz b/internal/api/web/assets/DashboardManagement-XKUzMfjC.js.gz new file mode 100644 index 0000000..161927c Binary files /dev/null and b/internal/api/web/assets/DashboardManagement-XKUzMfjC.js.gz differ diff --git a/internal/api/web/assets/DocxPreview-DsusPJ-N.js.gz b/internal/api/web/assets/DocxPreview-DsusPJ-N.js.gz new file mode 100644 index 0000000..40d6e40 Binary files /dev/null and b/internal/api/web/assets/DocxPreview-DsusPJ-N.js.gz differ diff --git a/internal/api/web/assets/DocxPreview-ttx8rzyi.js.gz b/internal/api/web/assets/DocxPreview-ttx8rzyi.js.gz deleted file mode 100644 index bc1eb75..0000000 Binary files a/internal/api/web/assets/DocxPreview-ttx8rzyi.js.gz and /dev/null differ diff --git a/internal/api/web/assets/FileShareManagement-CBHzRpyU.js.gz b/internal/api/web/assets/FileShareManagement-CBHzRpyU.js.gz new file mode 100644 index 0000000..44905f5 Binary files /dev/null and b/internal/api/web/assets/FileShareManagement-CBHzRpyU.js.gz differ diff --git a/internal/api/web/assets/FileShareManagement-Dm2kC5C0.js.gz b/internal/api/web/assets/FileShareManagement-Dm2kC5C0.js.gz deleted file mode 100644 index e79d979..0000000 Binary files a/internal/api/web/assets/FileShareManagement-Dm2kC5C0.js.gz and /dev/null differ diff --git a/internal/api/web/assets/FolderPickerModal-BdG9isAe.js.gz b/internal/api/web/assets/FolderPickerModal-BdG9isAe.js.gz new file mode 100644 index 0000000..b3197a1 Binary files /dev/null and b/internal/api/web/assets/FolderPickerModal-BdG9isAe.js.gz differ diff --git a/internal/api/web/assets/FolderPickerModal-CHUG0CMY.js.gz b/internal/api/web/assets/FolderPickerModal-CHUG0CMY.js.gz deleted file mode 100644 index f488c43..0000000 Binary files a/internal/api/web/assets/FolderPickerModal-CHUG0CMY.js.gz and /dev/null differ diff --git a/internal/api/web/assets/FormField-B8myKMEf.js.gz b/internal/api/web/assets/FormField-B8myKMEf.js.gz new file mode 100644 index 0000000..7828d29 Binary files /dev/null and b/internal/api/web/assets/FormField-B8myKMEf.js.gz differ diff --git a/internal/api/web/assets/FormField-D1sdmztC.js.gz b/internal/api/web/assets/FormField-D1sdmztC.js.gz deleted file mode 100644 index 7a92702..0000000 Binary files a/internal/api/web/assets/FormField-D1sdmztC.js.gz and /dev/null differ diff --git a/internal/api/web/assets/FuseManagement-jWhnL50f.js.gz b/internal/api/web/assets/FuseManagement-jWhnL50f.js.gz deleted file mode 100644 index 3a9c862..0000000 Binary files a/internal/api/web/assets/FuseManagement-jWhnL50f.js.gz and /dev/null differ diff --git a/internal/api/web/assets/FuseManagement-w2v3Q5zM.js.gz b/internal/api/web/assets/FuseManagement-w2v3Q5zM.js.gz new file mode 100644 index 0000000..94d875c Binary files /dev/null and b/internal/api/web/assets/FuseManagement-w2v3Q5zM.js.gz differ diff --git a/internal/api/web/assets/ImagePreview-CrfQ5ZHq.js.gz b/internal/api/web/assets/ImagePreview-CrfQ5ZHq.js.gz deleted file mode 100644 index be7a10e..0000000 Binary files a/internal/api/web/assets/ImagePreview-CrfQ5ZHq.js.gz and /dev/null differ diff --git a/internal/api/web/assets/ImagePreview-DTbH7UeG.js.gz b/internal/api/web/assets/ImagePreview-DTbH7UeG.js.gz new file mode 100644 index 0000000..f65d3ae Binary files /dev/null and b/internal/api/web/assets/ImagePreview-DTbH7UeG.js.gz differ diff --git a/internal/api/web/assets/IndexView-Btn23L82.js.gz b/internal/api/web/assets/IndexView-Btn23L82.js.gz deleted file mode 100644 index 0df945c..0000000 Binary files a/internal/api/web/assets/IndexView-Btn23L82.js.gz and /dev/null differ diff --git a/internal/api/web/assets/IndexView-Ch2dEHP8.js.gz b/internal/api/web/assets/IndexView-Ch2dEHP8.js.gz new file mode 100644 index 0000000..d9565a1 Binary files /dev/null and b/internal/api/web/assets/IndexView-Ch2dEHP8.js.gz differ diff --git a/internal/api/web/assets/LoginView-ClCSuof6.js.gz b/internal/api/web/assets/LoginView-ClCSuof6.js.gz new file mode 100644 index 0000000..08bcaa2 Binary files /dev/null and b/internal/api/web/assets/LoginView-ClCSuof6.js.gz differ diff --git a/internal/api/web/assets/LoginView-CtE4efGY.js.gz b/internal/api/web/assets/LoginView-CtE4efGY.js.gz deleted file mode 100644 index 4c1bc9d..0000000 Binary files a/internal/api/web/assets/LoginView-CtE4efGY.js.gz and /dev/null differ diff --git a/internal/api/web/assets/MediaOrganizePanel-CoTW6Lwg.js.gz b/internal/api/web/assets/MediaOrganizePanel-CoTW6Lwg.js.gz new file mode 100644 index 0000000..ddecde9 Binary files /dev/null and b/internal/api/web/assets/MediaOrganizePanel-CoTW6Lwg.js.gz differ diff --git a/internal/api/web/assets/MediaOrganizePanel-DVoJ_OzO.js.gz b/internal/api/web/assets/MediaOrganizePanel-DVoJ_OzO.js.gz deleted file mode 100644 index dbad91c..0000000 Binary files a/internal/api/web/assets/MediaOrganizePanel-DVoJ_OzO.js.gz and /dev/null differ diff --git a/internal/api/web/assets/MediaOrganizeSettings-DP_INQGz.js.gz b/internal/api/web/assets/MediaOrganizeSettings-DP_INQGz.js.gz new file mode 100644 index 0000000..b3aa829 Binary files /dev/null and b/internal/api/web/assets/MediaOrganizeSettings-DP_INQGz.js.gz differ diff --git a/internal/api/web/assets/MediaOrganizeSettings-DXuzA0Rb.js.gz b/internal/api/web/assets/MediaOrganizeSettings-DXuzA0Rb.js.gz deleted file mode 100644 index 7b3cf14..0000000 Binary files a/internal/api/web/assets/MediaOrganizeSettings-DXuzA0Rb.js.gz and /dev/null differ diff --git a/internal/api/web/assets/PdfPreview-MmZHNriZ.js.gz b/internal/api/web/assets/PdfPreview-MmZHNriZ.js.gz deleted file mode 100644 index aeb170b..0000000 Binary files a/internal/api/web/assets/PdfPreview-MmZHNriZ.js.gz and /dev/null differ diff --git a/internal/api/web/assets/PdfPreview-vjThA7Wf.js.gz b/internal/api/web/assets/PdfPreview-vjThA7Wf.js.gz new file mode 100644 index 0000000..52a424f Binary files /dev/null and b/internal/api/web/assets/PdfPreview-vjThA7Wf.js.gz differ diff --git a/internal/api/web/assets/PptxPreview-AdKz3beY.js.gz b/internal/api/web/assets/PptxPreview-AdKz3beY.js.gz new file mode 100644 index 0000000..3880e45 Binary files /dev/null and b/internal/api/web/assets/PptxPreview-AdKz3beY.js.gz differ diff --git a/internal/api/web/assets/PptxPreview-BHbcpDZo.js.gz b/internal/api/web/assets/PptxPreview-BHbcpDZo.js.gz deleted file mode 100644 index 0dfc186..0000000 Binary files a/internal/api/web/assets/PptxPreview-BHbcpDZo.js.gz and /dev/null differ diff --git a/internal/api/web/assets/PreviewHeader-Bp0446jw.js.gz b/internal/api/web/assets/PreviewHeader-Bp0446jw.js.gz deleted file mode 100644 index bb91d32..0000000 Binary files a/internal/api/web/assets/PreviewHeader-Bp0446jw.js.gz and /dev/null differ diff --git a/internal/api/web/assets/PreviewHeader-CG9joD8m.js.gz b/internal/api/web/assets/PreviewHeader-CG9joD8m.js.gz new file mode 100644 index 0000000..89062fc Binary files /dev/null and b/internal/api/web/assets/PreviewHeader-CG9joD8m.js.gz differ diff --git a/internal/api/web/assets/PreviewSideNavigation-Dp2D3jWE.js.gz b/internal/api/web/assets/PreviewSideNavigation-Dp2D3jWE.js.gz deleted file mode 100644 index 17e3218..0000000 Binary files a/internal/api/web/assets/PreviewSideNavigation-Dp2D3jWE.js.gz and /dev/null differ diff --git a/internal/api/web/assets/PreviewSideNavigation-d6SParcD.js.gz b/internal/api/web/assets/PreviewSideNavigation-d6SParcD.js.gz new file mode 100644 index 0000000..0ea99ac Binary files /dev/null and b/internal/api/web/assets/PreviewSideNavigation-d6SParcD.js.gz differ diff --git a/internal/api/web/assets/ProxyToolsPanel-DyJYuWv5.js.gz b/internal/api/web/assets/ProxyToolsPanel-DyJYuWv5.js.gz new file mode 100644 index 0000000..866ce1b Binary files /dev/null and b/internal/api/web/assets/ProxyToolsPanel-DyJYuWv5.js.gz differ diff --git a/internal/api/web/assets/ProxyToolsPanel-GvQ5RSy1.js.gz b/internal/api/web/assets/ProxyToolsPanel-GvQ5RSy1.js.gz deleted file mode 100644 index ea6fa0f..0000000 Binary files a/internal/api/web/assets/ProxyToolsPanel-GvQ5RSy1.js.gz and /dev/null differ diff --git a/internal/api/web/assets/SettingsRow-C3ytFJww.js.gz b/internal/api/web/assets/SettingsRow-C3ytFJww.js.gz new file mode 100644 index 0000000..825ff0c Binary files /dev/null and b/internal/api/web/assets/SettingsRow-C3ytFJww.js.gz differ diff --git a/internal/api/web/assets/SettingsRow-DYBRylJu.js.gz b/internal/api/web/assets/SettingsRow-DYBRylJu.js.gz deleted file mode 100644 index 64ccec3..0000000 Binary files a/internal/api/web/assets/SettingsRow-DYBRylJu.js.gz and /dev/null differ diff --git a/internal/api/web/assets/SettingsSegment-Jgl2af3J.js.gz b/internal/api/web/assets/SettingsSegment-Jgl2af3J.js.gz new file mode 100644 index 0000000..885b0e3 Binary files /dev/null and b/internal/api/web/assets/SettingsSegment-Jgl2af3J.js.gz differ diff --git a/internal/api/web/assets/SettingsSegment-LFY3lsym.js.gz b/internal/api/web/assets/SettingsSegment-LFY3lsym.js.gz deleted file mode 100644 index b4ae876..0000000 Binary files a/internal/api/web/assets/SettingsSegment-LFY3lsym.js.gz and /dev/null differ diff --git a/internal/api/web/assets/SpreadsheetPreview-B0uorVyA.js.gz b/internal/api/web/assets/SpreadsheetPreview-B0uorVyA.js.gz new file mode 100644 index 0000000..9ae8a2f Binary files /dev/null and b/internal/api/web/assets/SpreadsheetPreview-B0uorVyA.js.gz differ diff --git a/internal/api/web/assets/SpreadsheetPreview-JQu7jxJw.js.gz b/internal/api/web/assets/SpreadsheetPreview-JQu7jxJw.js.gz deleted file mode 100644 index 4398eea..0000000 Binary files a/internal/api/web/assets/SpreadsheetPreview-JQu7jxJw.js.gz and /dev/null differ diff --git a/internal/api/web/assets/StatCard-DQS9cR1-.js.gz b/internal/api/web/assets/StatCard-DQS9cR1-.js.gz deleted file mode 100644 index 3663588..0000000 Binary files a/internal/api/web/assets/StatCard-DQS9cR1-.js.gz and /dev/null differ diff --git a/internal/api/web/assets/StatCard-F22HIVy7.js.gz b/internal/api/web/assets/StatCard-F22HIVy7.js.gz new file mode 100644 index 0000000..5fd08ed Binary files /dev/null and b/internal/api/web/assets/StatCard-F22HIVy7.js.gz differ diff --git a/internal/api/web/assets/StrmScrapePanel-ChlusmYS.js.gz b/internal/api/web/assets/StrmScrapePanel-ChlusmYS.js.gz deleted file mode 100644 index 66d8530..0000000 Binary files a/internal/api/web/assets/StrmScrapePanel-ChlusmYS.js.gz and /dev/null differ diff --git a/internal/api/web/assets/StrmScrapePanel-i574RwGH.js.gz b/internal/api/web/assets/StrmScrapePanel-i574RwGH.js.gz new file mode 100644 index 0000000..b4813dd Binary files /dev/null and b/internal/api/web/assets/StrmScrapePanel-i574RwGH.js.gz differ diff --git a/internal/api/web/assets/StrmScrapeSettings-CGjk4WxF.js.gz b/internal/api/web/assets/StrmScrapeSettings-CGjk4WxF.js.gz deleted file mode 100644 index 7910fd5..0000000 Binary files a/internal/api/web/assets/StrmScrapeSettings-CGjk4WxF.js.gz and /dev/null differ diff --git a/internal/api/web/assets/StrmScrapeSettings-CHCAZ_1c.js.gz b/internal/api/web/assets/StrmScrapeSettings-CHCAZ_1c.js.gz new file mode 100644 index 0000000..47f62ac Binary files /dev/null and b/internal/api/web/assets/StrmScrapeSettings-CHCAZ_1c.js.gz differ diff --git a/internal/api/web/assets/SystemLogs-Cb2XGdYz.js.gz b/internal/api/web/assets/SystemLogs-Cb2XGdYz.js.gz new file mode 100644 index 0000000..e988a8f Binary files /dev/null and b/internal/api/web/assets/SystemLogs-Cb2XGdYz.js.gz differ diff --git a/internal/api/web/assets/SystemLogs-DTpqczkD.js.gz b/internal/api/web/assets/SystemLogs-DTpqczkD.js.gz deleted file mode 100644 index ca92c30..0000000 Binary files a/internal/api/web/assets/SystemLogs-DTpqczkD.js.gz and /dev/null differ diff --git a/internal/api/web/assets/SystemSettings-B5QSADjQ.js.gz b/internal/api/web/assets/SystemSettings-B5QSADjQ.js.gz new file mode 100644 index 0000000..a1af7ec Binary files /dev/null and b/internal/api/web/assets/SystemSettings-B5QSADjQ.js.gz differ diff --git a/internal/api/web/assets/SystemSettings-yNrwtsTG.js.gz b/internal/api/web/assets/SystemSettings-yNrwtsTG.js.gz deleted file mode 100644 index b5c3228..0000000 Binary files a/internal/api/web/assets/SystemSettings-yNrwtsTG.js.gz and /dev/null differ diff --git a/internal/api/web/assets/TaskManagement-CGMIo7dd.js.gz b/internal/api/web/assets/TaskManagement-CGMIo7dd.js.gz new file mode 100644 index 0000000..631959e Binary files /dev/null and b/internal/api/web/assets/TaskManagement-CGMIo7dd.js.gz differ diff --git a/internal/api/web/assets/TaskManagement-DnbY6vDw.js.gz b/internal/api/web/assets/TaskManagement-DnbY6vDw.js.gz deleted file mode 100644 index 3ff3c96..0000000 Binary files a/internal/api/web/assets/TaskManagement-DnbY6vDw.js.gz and /dev/null differ diff --git a/internal/api/web/assets/TextPreview-Bx0Y1w3f.js.gz b/internal/api/web/assets/TextPreview-Bx0Y1w3f.js.gz deleted file mode 100644 index 6091a78..0000000 Binary files a/internal/api/web/assets/TextPreview-Bx0Y1w3f.js.gz and /dev/null differ diff --git a/internal/api/web/assets/TextPreview-Dyeu-kvp.js.gz b/internal/api/web/assets/TextPreview-Dyeu-kvp.js.gz new file mode 100644 index 0000000..23f8418 Binary files /dev/null and b/internal/api/web/assets/TextPreview-Dyeu-kvp.js.gz differ diff --git a/internal/api/web/assets/TimeWheelPicker-BWfrpRiK.js.gz b/internal/api/web/assets/TimeWheelPicker-BWfrpRiK.js.gz deleted file mode 100644 index 340789a..0000000 Binary files a/internal/api/web/assets/TimeWheelPicker-BWfrpRiK.js.gz and /dev/null differ diff --git a/internal/api/web/assets/TimeWheelPicker-qyt7dj6q.js.gz b/internal/api/web/assets/TimeWheelPicker-qyt7dj6q.js.gz new file mode 100644 index 0000000..71f21f3 Binary files /dev/null and b/internal/api/web/assets/TimeWheelPicker-qyt7dj6q.js.gz differ diff --git a/internal/api/web/assets/TmdbHostsHelpTip-8xdofjUO.js.gz b/internal/api/web/assets/TmdbHostsHelpTip-8xdofjUO.js.gz deleted file mode 100644 index 3876604..0000000 Binary files a/internal/api/web/assets/TmdbHostsHelpTip-8xdofjUO.js.gz and /dev/null differ diff --git a/internal/api/web/assets/TmdbHostsHelpTip-Do3S2kTn.js.gz b/internal/api/web/assets/TmdbHostsHelpTip-Do3S2kTn.js.gz new file mode 100644 index 0000000..82fa335 Binary files /dev/null and b/internal/api/web/assets/TmdbHostsHelpTip-Do3S2kTn.js.gz differ diff --git a/internal/api/web/assets/VideoPreview-Bxuux0uh.js.gz b/internal/api/web/assets/VideoPreview-Bxuux0uh.js.gz new file mode 100644 index 0000000..fd2a7a6 Binary files /dev/null and b/internal/api/web/assets/VideoPreview-Bxuux0uh.js.gz differ diff --git a/internal/api/web/assets/VideoPreview-CqDTDDvp.js.gz b/internal/api/web/assets/VideoPreview-CqDTDDvp.js.gz deleted file mode 100644 index 2e6cebc..0000000 Binary files a/internal/api/web/assets/VideoPreview-CqDTDDvp.js.gz and /dev/null differ diff --git a/internal/api/web/assets/accounts-CWjn1f4r.js.gz b/internal/api/web/assets/accounts-CWjn1f4r.js.gz new file mode 100644 index 0000000..afab321 Binary files /dev/null and b/internal/api/web/assets/accounts-CWjn1f4r.js.gz differ diff --git a/internal/api/web/assets/accounts-DaIFwxBa.js.gz b/internal/api/web/assets/accounts-DaIFwxBa.js.gz new file mode 100644 index 0000000..ce37525 Binary files /dev/null and b/internal/api/web/assets/accounts-DaIFwxBa.js.gz differ diff --git a/internal/api/web/assets/accounts-Dk4ltUf3.js.gz b/internal/api/web/assets/accounts-Dk4ltUf3.js.gz deleted file mode 100644 index 9d311c7..0000000 Binary files a/internal/api/web/assets/accounts-Dk4ltUf3.js.gz and /dev/null differ diff --git a/internal/api/web/assets/accounts-vPd3zyzM.js.gz b/internal/api/web/assets/accounts-vPd3zyzM.js.gz deleted file mode 100644 index a4d6080..0000000 Binary files a/internal/api/web/assets/accounts-vPd3zyzM.js.gz and /dev/null differ diff --git a/internal/api/web/assets/admin-table-BPcW7-mV.js.gz b/internal/api/web/assets/admin-table-BPcW7-mV.js.gz new file mode 100644 index 0000000..bf1f891 Binary files /dev/null and b/internal/api/web/assets/admin-table-BPcW7-mV.js.gz differ diff --git a/internal/api/web/assets/admin-table-XJEYJAe4.js.gz b/internal/api/web/assets/admin-table-XJEYJAe4.js.gz deleted file mode 100644 index c4a2c4d..0000000 Binary files a/internal/api/web/assets/admin-table-XJEYJAe4.js.gz and /dev/null differ diff --git a/internal/api/web/assets/cache-QDqAyOAg.js.gz b/internal/api/web/assets/cache-QDqAyOAg.js.gz deleted file mode 100644 index 5bec66a..0000000 Binary files a/internal/api/web/assets/cache-QDqAyOAg.js.gz and /dev/null differ diff --git a/internal/api/web/assets/cache-uwyBz4s9.js.gz b/internal/api/web/assets/cache-uwyBz4s9.js.gz new file mode 100644 index 0000000..341bad8 Binary files /dev/null and b/internal/api/web/assets/cache-uwyBz4s9.js.gz differ diff --git a/internal/api/web/assets/cacheRetention-BnQWKzmM.js.gz b/internal/api/web/assets/cacheRetention-BnQWKzmM.js.gz deleted file mode 100644 index 1d4cc91..0000000 Binary files a/internal/api/web/assets/cacheRetention-BnQWKzmM.js.gz and /dev/null differ diff --git a/internal/api/web/assets/cacheRetention-CiDcDW4V.js.gz b/internal/api/web/assets/cacheRetention-CiDcDW4V.js.gz new file mode 100644 index 0000000..95a5f1c Binary files /dev/null and b/internal/api/web/assets/cacheRetention-CiDcDW4V.js.gz differ diff --git a/internal/api/web/assets/cacheSettings-BaTJJrfW.js.gz b/internal/api/web/assets/cacheSettings-BaTJJrfW.js.gz deleted file mode 100644 index 5d42052..0000000 Binary files a/internal/api/web/assets/cacheSettings-BaTJJrfW.js.gz and /dev/null differ diff --git a/internal/api/web/assets/cacheSettings-CdYB1n92.js.gz b/internal/api/web/assets/cacheSettings-CdYB1n92.js.gz new file mode 100644 index 0000000..a02f74d Binary files /dev/null and b/internal/api/web/assets/cacheSettings-CdYB1n92.js.gz differ diff --git a/internal/api/web/assets/crossTransfer-BWjusLWJ.js.gz b/internal/api/web/assets/crossTransfer-BWjusLWJ.js.gz deleted file mode 100644 index 419bfc5..0000000 Binary files a/internal/api/web/assets/crossTransfer-BWjusLWJ.js.gz and /dev/null differ diff --git a/internal/api/web/assets/crossTransfer-cix1_c_y.js.gz b/internal/api/web/assets/crossTransfer-cix1_c_y.js.gz new file mode 100644 index 0000000..2521e41 Binary files /dev/null and b/internal/api/web/assets/crossTransfer-cix1_c_y.js.gz differ diff --git a/internal/api/web/assets/dist-CkXx8BF7.js.gz b/internal/api/web/assets/dist-CkXx8BF7.js.gz deleted file mode 100644 index e7809e6..0000000 Binary files a/internal/api/web/assets/dist-CkXx8BF7.js.gz and /dev/null differ diff --git a/internal/api/web/assets/dist-DBNs4QOW.js.gz b/internal/api/web/assets/dist-DBNs4QOW.js.gz new file mode 100644 index 0000000..d98c860 Binary files /dev/null and b/internal/api/web/assets/dist-DBNs4QOW.js.gz differ diff --git a/internal/api/web/assets/errors-BSgDFfdJ.js.gz b/internal/api/web/assets/errors-BSgDFfdJ.js.gz deleted file mode 100644 index ec00e73..0000000 Binary files a/internal/api/web/assets/errors-BSgDFfdJ.js.gz and /dev/null differ diff --git a/internal/api/web/assets/errors-rKdHVCiP.js.gz b/internal/api/web/assets/errors-rKdHVCiP.js.gz new file mode 100644 index 0000000..6a6019f Binary files /dev/null and b/internal/api/web/assets/errors-rKdHVCiP.js.gz differ diff --git a/internal/api/web/assets/fuse-C2bYT0bJ.js.gz b/internal/api/web/assets/fuse-C2bYT0bJ.js.gz deleted file mode 100644 index ddc3010..0000000 Binary files a/internal/api/web/assets/fuse-C2bYT0bJ.js.gz and /dev/null differ diff --git a/internal/api/web/assets/fuse-DAO6gNpe.js.gz b/internal/api/web/assets/fuse-DAO6gNpe.js.gz new file mode 100644 index 0000000..6fa44ae Binary files /dev/null and b/internal/api/web/assets/fuse-DAO6gNpe.js.gz differ diff --git a/internal/api/web/assets/index-Bqk2PnDP.js.gz b/internal/api/web/assets/index-Bqk2PnDP.js.gz deleted file mode 100644 index 279b820..0000000 Binary files a/internal/api/web/assets/index-Bqk2PnDP.js.gz and /dev/null differ diff --git a/internal/api/web/assets/index-C6SXh4HH.js.gz b/internal/api/web/assets/index-C6SXh4HH.js.gz new file mode 100644 index 0000000..13b7f82 Binary files /dev/null and b/internal/api/web/assets/index-C6SXh4HH.js.gz differ diff --git a/internal/api/web/assets/logs-C2t-c0LX.js.gz b/internal/api/web/assets/logs-C2t-c0LX.js.gz deleted file mode 100644 index 85773ae..0000000 Binary files a/internal/api/web/assets/logs-C2t-c0LX.js.gz and /dev/null differ diff --git a/internal/api/web/assets/logs-DB7JednP.js.gz b/internal/api/web/assets/logs-DB7JednP.js.gz new file mode 100644 index 0000000..d46521e Binary files /dev/null and b/internal/api/web/assets/logs-DB7JednP.js.gz differ diff --git a/internal/api/web/assets/mediaOrganize-BWrMDB1T.js.gz b/internal/api/web/assets/mediaOrganize-BWrMDB1T.js.gz new file mode 100644 index 0000000..8844a8c Binary files /dev/null and b/internal/api/web/assets/mediaOrganize-BWrMDB1T.js.gz differ diff --git a/internal/api/web/assets/mediaOrganize-BcG3Wy_8.js.gz b/internal/api/web/assets/mediaOrganize-BcG3Wy_8.js.gz deleted file mode 100644 index 425a26d..0000000 Binary files a/internal/api/web/assets/mediaOrganize-BcG3Wy_8.js.gz and /dev/null differ diff --git a/internal/api/web/assets/notifications-Chrv84rt.js.gz b/internal/api/web/assets/notifications-Chrv84rt.js.gz deleted file mode 100644 index f5bc9c7..0000000 Binary files a/internal/api/web/assets/notifications-Chrv84rt.js.gz and /dev/null differ diff --git a/internal/api/web/assets/notifications-sy8RwmbT.js.gz b/internal/api/web/assets/notifications-sy8RwmbT.js.gz new file mode 100644 index 0000000..c9e9007 Binary files /dev/null and b/internal/api/web/assets/notifications-sy8RwmbT.js.gz differ diff --git a/internal/api/web/assets/prod-B7rDAOxJ.js b/internal/api/web/assets/prod-B7rDAOxJ.js new file mode 100644 index 0000000..036b5a9 --- /dev/null +++ b/internal/api/web/assets/prod-B7rDAOxJ.js @@ -0,0 +1 @@ +import{s as e}from"./prod-DOzONh3Q.js";export{e as parseText}; \ No newline at end of file diff --git a/internal/api/web/assets/prod-CKNEc51o.js.gz b/internal/api/web/assets/prod-CKNEc51o.js.gz deleted file mode 100644 index 0129644..0000000 Binary files a/internal/api/web/assets/prod-CKNEc51o.js.gz and /dev/null differ diff --git a/internal/api/web/assets/prod-DOzONh3Q.js.gz b/internal/api/web/assets/prod-DOzONh3Q.js.gz new file mode 100644 index 0000000..14de59a Binary files /dev/null and b/internal/api/web/assets/prod-DOzONh3Q.js.gz differ diff --git a/internal/api/web/assets/prod-H6H8-B44.js b/internal/api/web/assets/prod-H6H8-B44.js deleted file mode 100644 index 0a834bb..0000000 --- a/internal/api/web/assets/prod-H6H8-B44.js +++ /dev/null @@ -1 +0,0 @@ -import{s as e}from"./prod-CKNEc51o.js";export{e as parseText}; \ No newline at end of file diff --git a/internal/api/web/assets/srt-parser-BgSZ7L4u.js.gz b/internal/api/web/assets/srt-parser-BgSZ7L4u.js.gz new file mode 100644 index 0000000..505d00a Binary files /dev/null and b/internal/api/web/assets/srt-parser-BgSZ7L4u.js.gz differ diff --git a/internal/api/web/assets/srt-parser-C-Pk3MTr.js.gz b/internal/api/web/assets/srt-parser-C-Pk3MTr.js.gz deleted file mode 100644 index 615f822..0000000 Binary files a/internal/api/web/assets/srt-parser-C-Pk3MTr.js.gz and /dev/null differ diff --git a/internal/api/web/assets/ssa-parser-CjbKlASz.js.gz b/internal/api/web/assets/ssa-parser-CjbKlASz.js.gz new file mode 100644 index 0000000..1ea4ce1 Binary files /dev/null and b/internal/api/web/assets/ssa-parser-CjbKlASz.js.gz differ diff --git a/internal/api/web/assets/ssa-parser-Dbi9WN8s.js.gz b/internal/api/web/assets/ssa-parser-Dbi9WN8s.js.gz deleted file mode 100644 index c242fd1..0000000 Binary files a/internal/api/web/assets/ssa-parser-Dbi9WN8s.js.gz and /dev/null differ diff --git a/internal/api/web/assets/strm-CUD0dOrW.js.gz b/internal/api/web/assets/strm-CUD0dOrW.js.gz new file mode 100644 index 0000000..fceb466 Binary files /dev/null and b/internal/api/web/assets/strm-CUD0dOrW.js.gz differ diff --git a/internal/api/web/assets/strm-Cd867b-W.js.gz b/internal/api/web/assets/strm-Cd867b-W.js.gz deleted file mode 100644 index 68cab9d..0000000 Binary files a/internal/api/web/assets/strm-Cd867b-W.js.gz and /dev/null differ diff --git a/internal/api/web/assets/strmScrape-CXn47n0-.js.gz b/internal/api/web/assets/strmScrape-CXn47n0-.js.gz deleted file mode 100644 index 8899182..0000000 Binary files a/internal/api/web/assets/strmScrape-CXn47n0-.js.gz and /dev/null differ diff --git a/internal/api/web/assets/strmScrape-EZgl4LNB.js.gz b/internal/api/web/assets/strmScrape-EZgl4LNB.js.gz new file mode 100644 index 0000000..a4119b9 Binary files /dev/null and b/internal/api/web/assets/strmScrape-EZgl4LNB.js.gz differ diff --git a/internal/api/web/assets/useSectionTabRoute-CEl0hp5d.js.gz b/internal/api/web/assets/useSectionTabRoute-CEl0hp5d.js.gz deleted file mode 100644 index ee5de62..0000000 Binary files a/internal/api/web/assets/useSectionTabRoute-CEl0hp5d.js.gz and /dev/null differ diff --git a/internal/api/web/assets/useSectionTabRoute-DxrQHlfw.js.gz b/internal/api/web/assets/useSectionTabRoute-DxrQHlfw.js.gz new file mode 100644 index 0000000..8337715 Binary files /dev/null and b/internal/api/web/assets/useSectionTabRoute-DxrQHlfw.js.gz differ diff --git a/internal/api/web/assets/useSettingsLoad-D88qJ2hP.js.gz b/internal/api/web/assets/useSettingsLoad-D88qJ2hP.js.gz new file mode 100644 index 0000000..127e300 Binary files /dev/null and b/internal/api/web/assets/useSettingsLoad-D88qJ2hP.js.gz differ diff --git a/internal/api/web/assets/useSettingsLoad-sxQCt1Ql.js.gz b/internal/api/web/assets/useSettingsLoad-sxQCt1Ql.js.gz deleted file mode 100644 index 295359c..0000000 Binary files a/internal/api/web/assets/useSettingsLoad-sxQCt1Ql.js.gz and /dev/null differ diff --git a/internal/api/web/assets/useSettingsPageDirty-CgOtT1xF.js.gz b/internal/api/web/assets/useSettingsPageDirty-CgOtT1xF.js.gz new file mode 100644 index 0000000..4386975 Binary files /dev/null and b/internal/api/web/assets/useSettingsPageDirty-CgOtT1xF.js.gz differ diff --git a/internal/api/web/assets/useSettingsPageDirty-YwuyYLJp.js.gz b/internal/api/web/assets/useSettingsPageDirty-YwuyYLJp.js.gz deleted file mode 100644 index bedff52..0000000 Binary files a/internal/api/web/assets/useSettingsPageDirty-YwuyYLJp.js.gz and /dev/null differ diff --git a/internal/api/web/assets/useStartupCountdown-BnwMcKYh.js.gz b/internal/api/web/assets/useStartupCountdown-BnwMcKYh.js.gz new file mode 100644 index 0000000..655fd16 Binary files /dev/null and b/internal/api/web/assets/useStartupCountdown-BnwMcKYh.js.gz differ diff --git a/internal/api/web/assets/useStartupCountdown-CW1NUNkT.js.gz b/internal/api/web/assets/useStartupCountdown-CW1NUNkT.js.gz deleted file mode 100644 index 32eb5d3..0000000 Binary files a/internal/api/web/assets/useStartupCountdown-CW1NUNkT.js.gz and /dev/null differ diff --git a/internal/api/web/assets/useUnsavedChanges-BGpdrcfJ.js.gz b/internal/api/web/assets/useUnsavedChanges-BGpdrcfJ.js.gz deleted file mode 100644 index bfa4e2e..0000000 Binary files a/internal/api/web/assets/useUnsavedChanges-BGpdrcfJ.js.gz and /dev/null differ diff --git a/internal/api/web/assets/useUnsavedChanges-C5qeD_Jv.js.gz b/internal/api/web/assets/useUnsavedChanges-C5qeD_Jv.js.gz new file mode 100644 index 0000000..7e6d4b7 Binary files /dev/null and b/internal/api/web/assets/useUnsavedChanges-C5qeD_Jv.js.gz differ diff --git a/internal/api/web/assets/version-D99XhZC0.js.gz b/internal/api/web/assets/version-D99XhZC0.js.gz deleted file mode 100644 index 8e9e495..0000000 Binary files a/internal/api/web/assets/version-D99XhZC0.js.gz and /dev/null differ diff --git a/internal/api/web/assets/version-iEXGMmO7.js.gz b/internal/api/web/assets/version-iEXGMmO7.js.gz new file mode 100644 index 0000000..74cfc0a Binary files /dev/null and b/internal/api/web/assets/version-iEXGMmO7.js.gz differ diff --git a/internal/api/web/index.html b/internal/api/web/index.html index 9cd9bc3..ff38149 100644 --- a/internal/api/web/index.html +++ b/internal/api/web/index.html @@ -5,7 +5,7 @@