Live video on the AT Protocol
at eli/lexicon-dev 165 lines 4.6 kB view raw
1package api 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "regexp" 10 "strings" 11 12 "github.com/julienschmidt/httprouter" 13 apierrors "stream.place/streamplace/pkg/errors" 14 "stream.place/streamplace/pkg/log" 15) 16 17var ( 18 re = regexp.MustCompile(`^streamplace(-desktop)?-(v[0-9]+\.[0-9]+\.[0-9]+)(-[0-9a-f]+)?-([0-9a-z]+)-([0-9a-z]+)\.(?:([0-9a-f]+)\.)?(.+)$`) 19 inputRe = regexp.MustCompile(`^streamplace(-desktop)?-([0-9a-z]+)-([0-9a-z]+)\.(.+)$`) 20) 21 22func queryGitlabReal(url string) (io.ReadCloser, error) { 23 req, err := http.Get(url) 24 if err != nil { 25 return nil, err 26 } 27 return req.Body, nil 28} 29 30var queryGitlab = queryGitlabReal 31 32func (a *StreamplaceAPI) HandleAppDownload(ctx context.Context) httprouter.Handle { 33 return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) { 34 log.Log(ctx, "got here") 35 pathname := r.URL.Path 36 parts := strings.Split(pathname, "/") 37 if len(parts) < 4 { 38 apierrors.WriteHTTPBadRequest(w, "usage: /dl/latest/streamplace-linux-arm64.tar.gz", nil) 39 return 40 } 41 42 _, branch, file := parts[1], parts[2], parts[3] 43 if branch == "" || file == "" { 44 apierrors.WriteHTTPBadRequest(w, "usage: /dl/latest/streamplace-linux-arm64.tar.gz", nil) 45 return 46 } 47 48 inputPieces := inputRe.FindStringSubmatch(file) 49 if inputPieces == nil { 50 apierrors.WriteHTTPBadRequest(w, fmt.Sprintf("could not parse filename %s", file), nil) 51 return 52 } 53 54 inputDesktop, inputPlatform, inputArch, inputExt := inputPieces[1], inputPieces[2], inputPieces[3], inputPieces[4] 55 files, err := a.getGitlabPackage(branch) 56 if err != nil { 57 apierrors.WriteHTTPBadRequest(w, fmt.Sprintf("could not get gitlab package %s", file), err) 58 return 59 } 60 61 var foundFile *GitlabFile 62 for _, f := range files { 63 if f.Desktop == inputDesktop && f.Platform == inputPlatform && f.Architecture == inputArch && f.Extension == inputExt { 64 foundFile = &f 65 break 66 } 67 } 68 69 if foundFile == nil { 70 apierrors.WriteHTTPNotFound(w, fmt.Sprintf("could not find a file for desktop=%s platform=%s arch=%s ext=%s", inputDesktop, inputPlatform, inputArch, inputExt), nil) 71 return 72 } 73 74 http.Redirect(w, r, foundFile.URL(), http.StatusTemporaryRedirect) 75 } 76} 77 78type GitlabFile struct { 79 GitLabURL string 80 Branch string 81 Filename string 82 Desktop string 83 Version string 84 Hash string 85 Platform string 86 Architecture string 87 SHA1 string 88 Extension string 89 Size int 90} 91 92func (f GitlabFile) FullVer() string { 93 return f.Version + f.Hash 94} 95 96func (f GitlabFile) URL() string { 97 return fmt.Sprintf("%s/packages/generic/%s/%s/%s", f.GitLabURL, f.Branch, f.FullVer(), f.Filename) 98} 99 100func (a *StreamplaceAPI) getGitlabPackage(branch string) ([]GitlabFile, error) { 101 packageURL := fmt.Sprintf("%s/packages?order_by=created_at&sort=desc&package_name=%s", a.CLI.GitLabURL, branch) 102 103 packageBody, err := queryGitlab(packageURL) 104 if err != nil { 105 return nil, fmt.Errorf("failed to fetch packages: %w", err) 106 } 107 defer packageBody.Close() 108 109 var packages []map[string]any 110 if err := json.NewDecoder(packageBody).Decode(&packages); err != nil { 111 return nil, fmt.Errorf("failed to decode package response: %w", err) 112 } 113 // bs, _ := json.Marshal(packages) 114 // fmt.Println(string(bs)) 115 116 if len(packages) == 0 { 117 return nil, fmt.Errorf("package for branch %s not found", branch) 118 } 119 120 pkg := packages[0] 121 fileURL := fmt.Sprintf("%s/packages/%v/package_files", a.CLI.GitLabURL, pkg["id"]) 122 123 fileBody, err := queryGitlab(fileURL) 124 if err != nil { 125 return nil, fmt.Errorf("failed to fetch files: %w", err) 126 } 127 defer fileBody.Close() 128 129 var files []map[string]any 130 if err := json.NewDecoder(fileBody).Decode(&files); err != nil { 131 return nil, fmt.Errorf("failed to decode file response: %w", err) 132 } 133 134 out := []GitlabFile{} 135 for _, f := range files { 136 filename, ok := f["file_name"].(string) 137 if !ok { 138 continue 139 } 140 pieces := re.FindStringSubmatch(filename) 141 if pieces == nil { 142 // log.Log(ctx, "could not parse filename %s", "filename", filename) 143 continue 144 } 145 size, ok := f["size"].(float64) 146 if !ok { 147 continue 148 } 149 desktop, ver, hash, platform, arch, sha1, ext := pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7] 150 out = append(out, GitlabFile{ 151 GitLabURL: a.CLI.GitLabURL, 152 Branch: branch, 153 Filename: filename, 154 Desktop: desktop, 155 Version: ver, 156 Hash: hash, 157 Platform: platform, 158 Architecture: arch, 159 SHA1: sha1, 160 Extension: ext, 161 Size: int(size), 162 }) 163 } 164 return out, nil 165}