├── .github └── workflows │ ├── codeql-analysis.yml │ ├── go.yml │ └── goreleaser.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bin ├── arm │ └── go-pd ├── linux │ └── go-pd └── windows │ └── go-pd.exe ├── cmd ├── download.go ├── root.go └── upload.go ├── dist ├── artifacts.json ├── config.yaml ├── go-pd_1.1.0-SNAPSHOT-85d261a_checksums.txt ├── go-pd_1.1.0-SNAPSHOT-85d261a_darwin_amd64.tar.gz ├── go-pd_1.1.0-SNAPSHOT-85d261a_darwin_arm64.tar.gz ├── go-pd_1.1.0-SNAPSHOT-85d261a_linux_386.tar.gz ├── go-pd_1.1.0-SNAPSHOT-85d261a_linux_amd64.tar.gz ├── go-pd_1.1.0-SNAPSHOT-85d261a_linux_arm64.tar.gz ├── go-pd_darwin_amd64_v1 │ └── go-pd ├── go-pd_darwin_arm64 │ └── go-pd ├── go-pd_linux_386 │ └── go-pd ├── go-pd_linux_amd64_v1 │ └── go-pd ├── go-pd_linux_arm64 │ └── go-pd └── metadata.json ├── go-pd-upload-and-download.gif ├── go.mod ├── go.sum ├── internal └── app │ ├── cmdDownload.go │ └── cmdUpload.go ├── logo.jpg ├── main.go └── pkg └── pd ├── .env ├── .gitignore ├── mock_server.go ├── pd.go ├── pd_test.go ├── request.go ├── request_test.go ├── response.go ├── response_test.go └── testdata ├── cat.jpg └── cat_thumbnail.jpg /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '43 1 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'go' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v2 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v2 71 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | test-build: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | go-version: ['1.21'] 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Go ${{ matrix.go-version }} 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: ${{ matrix.go-version }} 23 | 24 | - name: Display Go Version 25 | run: go version 26 | 27 | - name: Install dependencies 28 | run: go get . 29 | 30 | - name: test 31 | run: make test 32 | 33 | - name: build 34 | run: make build 35 | -------------------------------------------------------------------------------- /.github/workflows/goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Checkout 14 | uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: Set up Go 19 | uses: actions/setup-go@v4 20 | with: 21 | go-version: stable 22 | - 23 | name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v4 25 | with: 26 | # either 'goreleaser' (default) or 'goreleaser-pro' 27 | distribution: goreleaser 28 | version: latest 29 | args: release --rm-dist 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | # Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution 33 | # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | c.out 3 | coverage.html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Manuel Reschke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FILES ?= $(shell find . -type f -name '*.go' -not -path "./vendor/*") 2 | 3 | .PHONY: hello 4 | hello: 5 | echo "Hello World" 6 | .PHONY: hello 7 | 8 | test: ## run all unit test 9 | go test ./... -v -short 10 | .PHONY: test 11 | 12 | test-integration: ## run all integration test 13 | go test ./... -v -run Integration 14 | .PHONY: test-integration 15 | 16 | coverage: ## create coverage report with go get golang.org/x/tools/cmd/cover 17 | go test -cover -coverprofile=c.out ./... 18 | go tool cover -html=c.out -o coverage.html 19 | .PHONY: coverage 20 | 21 | fmt: ## format the go source files 22 | go fmt ./... 23 | .PHONY: fmt 24 | 25 | build: 26 | env GOOS=linux GOARCH=amd64 go build -o bin/linux/go-pd 27 | env GOOS=linux GOARCH=arm64 go build -o bin/arm/go-pd 28 | env GOOS=windows GOARCH=amd64 go build -o bin/windows/go-pd.exe 29 | .PHONY: build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Version](https://img.shields.io/github/v/release/ManuelReschke/go-pd)](https://github.com/ManuelReschke/go-pd/releases) 2 | ![GitHub](https://img.shields.io/github/license/ManuelReschke/go-pd) 3 | ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/ManuelReschke/go-pd) 4 | ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/ManuelReschke/go-pd) 5 | ![GitHub top language](https://img.shields.io/github/languages/top/ManuelReschke/go-pd) 6 | 7 | # Go-PD - A pixeldrain.com client pkg and CLI tool 8 | 9 | ![Go-PD](logo.jpg) 10 | 11 | A free pixeldrain.com client written in go. We use the super power from [imroc/req](https://github.com/imroc/req) (v3.43.x) to build a robust and fast pixeldrain client and [cobra](https://github.com/spf13/cobra) for our CLI tool. 12 | 13 | ![Go-PD](go-pd-upload-and-download.gif) 14 | 15 | ## Content: 16 | - [Using the CLI tool](#cli-tool) 17 | - [CLI Tool: Install](#cli-tool-install) 18 | - [CLI Tool: Upload a file](#cli-tool-upload-a-file) 19 | - [CLI Tool: Download a file](#cli-tool-download-a-file) 20 | - [Using the client pkg](#client-pkg) 21 | - [Why?](#why) 22 | - [Import pkg](#import-the-pkg) 23 | - [Example 1 - the easiest way to upload an anonymous file](#example-1---the-easiest-way-to-upload-an-anonymous-file) 24 | - [Example 2 - advanced way to upload a file to user account](#example-2---advanced-way-to-upload-a-file-to-user-account) 25 | - [ToDo's](#todos) 26 | - [Covered methods](#pixeldrain-methods-covered-by-this-package) 27 | - [License](#license) 28 | 29 | 30 | # Using the CLI tool 31 | 32 | ## CLI Tool: Install 33 | 34 | Follow the link to download the correct binary for your system. [View Releases](https://github.com/ManuelReschke/go-pd/releases). It's available for Linux, ARM and Windows. 35 | Download the correct archive, extract it and use the binary. 36 | 37 | ## CLI Tool: Upload a file 38 | 39 | Go to the folder where you download the binary file and run the following command in a CLI. 40 | 41 | **Simple upload:** 42 | 43 | ``` 44 | ./go-pd upload my-cat.jpg 45 | 46 | Output: 47 | https://pixeldrain.com/u/aaaaaaaa 48 | ``` 49 | 50 | **Upload to your account (-verbose):** 51 | The verbose option enable also the progress in % and the env check message. 52 | 53 | ``` 54 | ./go-pd upload -k -v my-cat.jpg my-cat2.jpg 55 | 56 | Output: 57 | Using API Key from environment variable: PIXELDRAIN_API_KEY 58 | "my-cat.jpg" uploaded 100.00% 59 | Successful! Anonymous upload: false | ID: xBxxxxxx | URL: https://pixeldrain.com/u/xBxxxxxx 60 | "my-cat2.jpg" uploaded 100.00% 61 | Successful! Anonymous upload: false | ID: xAxxxxxx | URL: https://pixeldrain.com/u/xAxxxxxx 62 | ``` 63 | 64 | ## CLI Tool: Download a file 65 | 66 | Go to the folder where you download the binary file and run the following command in a CLI. 67 | 68 | **Simple download:** 69 | 70 | ``` 71 | ./go-pd download https://pixeldrain.com/u/YqiUjXXX 72 | 73 | Output: 74 | /the/path/to/your/file 75 | ``` 76 | 77 | **Download multiple files to a specific path (-verbose):** 78 | 79 | ``` 80 | ./go-pd download -k -p /home/pixeldrain/pictures/ YqiUjXXX YqiUjX02 YqiUjX03 81 | 82 | Output: 83 | Successful! Download complete: filename01.jpg | ID: xBxxxxxx | Stored to: /home/pixeldrain/pictures/filename01.jpg 84 | Successful! Download complete: filename02.jpg | ID: xBxxxxxx | Stored to: /home/pixeldrain/pictures/filename02.jpg 85 | Successful! Download complete: filename03.jpg | ID: xBxxxxxx | Stored to: /home/pixeldrain/pictures/filename03.jpg 86 | ``` 87 | 88 | 89 | # Using the client pkg 90 | 91 | 92 | ## Why the package? 93 | 94 | Because we want a simple, fast, robust and tested go package to upload to pixeldrain.com. 95 | 96 | 97 | ### Import the pkg 98 | 99 | ``` 100 | go get github.com/ManuelReschke/go-pd/pkg/pd 101 | ``` 102 | 103 | ## Example 1 - the easiest way to upload an anonymous file 104 | 105 | ```go 106 | package main 107 | 108 | import ( 109 | "fmt" 110 | "time" 111 | 112 | "github.com/ManuelReschke/go-pd/pkg/pd" 113 | ) 114 | 115 | func main() { 116 | req := &pd.RequestUpload{ 117 | PathToFile: "testdata/cat.jpg", 118 | Anonymous: true, 119 | } 120 | 121 | c := pd.New(nil, nil) 122 | rsp, err := c.UploadPOST(req) 123 | if err != nil { 124 | fmt.Println(err) 125 | } 126 | 127 | // print the full URL 128 | fmt.Println(rsp.GetFileURL()) 129 | 130 | // example ID = xFNz76Vp 131 | // example URL = https://pixeldrain.com/u/xFNz76Vp 132 | } 133 | ``` 134 | 135 | ## Example 2 - advanced way - upload a file to user account with progress callback 136 | 137 | ```go 138 | package main 139 | 140 | import ( 141 | "fmt" 142 | "time" 143 | 144 | "github.com/ManuelReschke/go-pd/pkg/pd" 145 | "github.com/imroc/req/v3" 146 | ) 147 | 148 | func main() { 149 | req := &pd.RequestUpload{ 150 | PathToFile: "testdata/cat.jpg", 151 | FileName: "test_post_cat.jpg", 152 | Anonymous: false, 153 | Auth: pd.Auth{ 154 | APIKey: "you-api-key-from-pixeldrain-account", 155 | }, 156 | } 157 | 158 | // set specific request options 159 | opt := &pd.ClientOptions{ 160 | Debug: false, 161 | ProxyURL: "example.socks5.proxy", 162 | EnableCookies: true, 163 | EnableInsecureTLS: true, 164 | Timeout: 1 * time.Hour, 165 | } 166 | 167 | c := pd.New(opt, nil) 168 | 169 | // enable progress 170 | c.SetUploadCallback(func(info req.UploadInfo) { 171 | if info.FileSize > 0 { 172 | fmt.Printf("%q uploaded %.2f%%\n", info.FileName, float64(info.UploadedSize)/float64(info.FileSize)*100.0) 173 | } else { 174 | fmt.Printf("%q uploaded 0%% (file size is zero)\n", info.FileName) 175 | } 176 | }) 177 | 178 | rsp, err := c.UploadPOST(req) 179 | if err != nil { 180 | fmt.Println(err) 181 | } 182 | 183 | // print the full URL 184 | fmt.Println(rsp.GetFileURL()) 185 | 186 | // example ID = xFNz76Vp 187 | // example URL = https://pixeldrain.com/u/xFNz76Vp 188 | } 189 | ``` 190 | ## ToDo's: 191 | 192 | - [x] implement simple upload method over POST /file 193 | - [x] implement simple upload over PUT /file/{filename} 194 | - [x] write unit tests 195 | - [x] write integration tests 196 | - [x] add API-KEY auth to requests 197 | - [x] implement all other API methods 198 | - [x] implement GET - /file/{id} 199 | - [x] implement GET - /file/{id}/info 200 | - [x] implement GET - /file/{id}/thumbnail?width=x&height=x 201 | - [x] implement DELETE - /file/{id} 202 | - [x] implement POST - /list 203 | - [X] implement GET - /list/{id} 204 | - [x] implement GET - /user 205 | - [x] implement GET - /user/files 206 | - [x] implement GET - /user/lists 207 | - [x] create CLI tool for uploading to pixeldrain.com 208 | - [x] update imroc/req to the latest version 209 | - [ ] refactor the hole shit and use nice to have patterns (like Option Pattern) 210 | 211 | ## PixelDrain methods covered by this package 212 | 213 | ### File Methods 214 | | PixelDrain Call | Package Func | 215 | |-------------------------------------------------|---| 216 | | [x] POST - /file | UploadPOST(r *RequestUpload) (*ResponseUpload, error) | 217 | | [x] PUT - /file/{name} | UploadPUT(r *RequestUpload) (*ResponseUpload, error) | 218 | | [x] GET - /file/{id} | Download(r *RequestDownload) (*ResponseDownload, error) | 219 | | [x] GET - /file/{id}/info | GetFileInfo(r *RequestFileInfo) (*ResponseFileInfo, error) | 220 | | [x] GET - /file/{id}/thumbnail?width=x&height=x | DownloadThumbnail(r *RequestThumbnail) (*ResponseThumbnail, error) | 221 | | [x] DELETE - /file/{id} | Delete(r *RequestDelete) (*ResponseDelete, error) | 222 | ### List Methods 223 | | PixelDrain Call | Package Func | 224 | |----------------------|---| 225 | | [x] POST - /list | CreateList(r *RequestCreateList) (*ResponseCreateList, error) | 226 | | [x] GET - /list/{id} | GetList(r *RequestGetList) (*ResponseGetList, error) | 227 | ### User Methods 228 | | PixelDrain Call | Package Func | 229 | |------------------------|---| 230 | | [x] GET - /user | GetUser(r *RequestGetUser) (*ResponseGetUser, error) | 231 | | [x] POST - /user/files | GetUserFiles(r *RequestGetUserFiles) (*ResponseGetUserFiles, error) | 232 | | [x] GET - /user/lists | GetUserLists(r *RequestGetUserLists) (*ResponseGetUserLists, error) | 233 | 234 | ## Package CLI commands 235 | 236 | ### Unit Tests - Run pkg unit tests 237 | Run unit tests against a local emulated server. 238 | ```shell 239 | make test 240 | ``` 241 | 242 | ### Integration Tests - Run pkg integration tests 243 | Run real integration tests against the real pixeldrain.com website. 244 | ```shell 245 | make test-integration 246 | ``` 247 | 248 | ### Test Coverage - create test coverage report 249 | Create a coverage report c.out and a coverage.html to view the results in web browser 250 | ```shell 251 | make coverage 252 | ``` 253 | 254 | ## Thanks to 255 | Special thanks to Visual Studio Code and to Jetbrains for this amazing IDE and [supporting the open source community](https://www.jetbrains.com/de-de/community/opensource/#support). 256 | 257 | ## License 258 | 259 | This software is released under the MIT License, see LICENSE. 260 | -------------------------------------------------------------------------------- /bin/arm/go-pd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/bin/arm/go-pd -------------------------------------------------------------------------------- /bin/linux/go-pd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/bin/linux/go-pd -------------------------------------------------------------------------------- /bin/windows/go-pd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/bin/windows/go-pd.exe -------------------------------------------------------------------------------- /cmd/download.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/ManuelReschke/go-pd/internal/app" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | const ( 10 | cmdDownloadUse = "download" 11 | cmdDownloadShort = "With that command you can download a file" 12 | cmdDownloadLong = "Download file by passing the file url or file id and your API Key with -k" 13 | ) 14 | 15 | // downloadCmd represents the upload command 16 | var downloadCmd = &cobra.Command{ 17 | Use: cmdDownloadUse, 18 | Short: cmdDownloadShort, 19 | Long: cmdDownloadLong, 20 | RunE: app.RunDownload, 21 | } 22 | 23 | func init() { 24 | rootCmd.AddCommand(downloadCmd) 25 | downloadCmd.Flags().StringP("path", "p", "", "Path where the files are stored") 26 | downloadCmd.Flags().StringP("api-key", "k", "", "Auth key for authentication") 27 | downloadCmd.Flags().BoolP("verbose", "v", true, "Show more information after an upload (Anonymous, ID, URL)") 28 | } 29 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | // rootCmd represents the base command when called without any subcommands 10 | var rootCmd = &cobra.Command{ 11 | Use: "go-pd", 12 | Short: "go-pd is a CLI tool to manage your pixeldrain.com account via cli.", 13 | Long: ` 14 | go-pd is a CLI tool to manage your pixeldrain.com account via cli. 15 | 16 | @Copyright by Manuel Reschke 17 | `, 18 | // Uncomment the following line if your bare application 19 | // has an action associated with it: 20 | // Run: func(cmd *cobra.Command, args []string) { }, 21 | } 22 | 23 | // Execute adds all child commands to the root command and sets flags appropriately. 24 | // This is called by main.main(). It only needs to happen once to the rootCmd. 25 | func Execute() { 26 | err := rootCmd.Execute() 27 | if err != nil { 28 | os.Exit(1) 29 | } 30 | } 31 | 32 | func init() { 33 | // Here you will define your flags and configuration settings. 34 | // Cobra supports persistent flags, which, if defined here, 35 | // will be global for your application. 36 | 37 | // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.go-pd.yaml)") 38 | 39 | // Cobra also supports local flags, which will only run 40 | // when this action is called directly. 41 | //rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 42 | } 43 | -------------------------------------------------------------------------------- /cmd/upload.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/ManuelReschke/go-pd/internal/app" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | const ( 10 | cmdUploadUse = "upload" 11 | cmdUploadShort = "With that command you can upload files" 12 | cmdUploadLong = "Upload files by passing the -f flag for your file and your API Key with -k" 13 | ) 14 | 15 | // uploadCmd represents the upload command 16 | var uploadCmd = &cobra.Command{ 17 | Use: cmdUploadUse, 18 | Short: cmdUploadShort, 19 | Long: cmdUploadLong, 20 | RunE: app.RunUpload, 21 | } 22 | 23 | func init() { 24 | rootCmd.AddCommand(uploadCmd) 25 | uploadCmd.Flags().StringP("api-key", "k", "", "Auth key for authentication") 26 | uploadCmd.Flags().BoolP("verbose", "v", true, "Show more information after an upload (Anonymous, ID, URL)") 27 | } 28 | -------------------------------------------------------------------------------- /dist/artifacts.json: -------------------------------------------------------------------------------- 1 | [{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_linux_amd64_v1/go-pd","goos":"linux","goarch":"amd64","goamd64":"v1","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}},{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_linux_386/go-pd","goos":"linux","goarch":"386","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}},{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_linux_arm64/go-pd","goos":"linux","goarch":"arm64","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}},{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_darwin_amd64_v1/go-pd","goos":"darwin","goarch":"amd64","goamd64":"v1","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}},{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_darwin_arm64/go-pd","goos":"darwin","goarch":"arm64","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}},{"name":"go-pd_1.1.0-SNAPSHOT-85d261a_linux_386.tar.gz","path":"dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_386.tar.gz","goos":"linux","goarch":"386","type":"Archive","extra":{"Binaries":["go-pd"],"Builds":[{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_linux_386/go-pd","goos":"linux","goarch":"386","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}}],"Format":"tar.gz","ID":"default","Replaces":null,"WrappedIn":""}},{"name":"go-pd_1.1.0-SNAPSHOT-85d261a_linux_arm64.tar.gz","path":"dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_arm64.tar.gz","goos":"linux","goarch":"arm64","type":"Archive","extra":{"Binaries":["go-pd"],"Builds":[{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_linux_arm64/go-pd","goos":"linux","goarch":"arm64","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}}],"Format":"tar.gz","ID":"default","Replaces":null,"WrappedIn":""}},{"name":"go-pd_1.1.0-SNAPSHOT-85d261a_darwin_arm64.tar.gz","path":"dist/go-pd_1.1.0-SNAPSHOT-85d261a_darwin_arm64.tar.gz","goos":"darwin","goarch":"arm64","type":"Archive","extra":{"Binaries":["go-pd"],"Builds":[{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_darwin_arm64/go-pd","goos":"darwin","goarch":"arm64","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}}],"Format":"tar.gz","ID":"default","Replaces":null,"WrappedIn":""}},{"name":"go-pd_1.1.0-SNAPSHOT-85d261a_darwin_amd64.tar.gz","path":"dist/go-pd_1.1.0-SNAPSHOT-85d261a_darwin_amd64.tar.gz","goos":"darwin","goarch":"amd64","goamd64":"v1","type":"Archive","extra":{"Binaries":["go-pd"],"Builds":[{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_darwin_amd64_v1/go-pd","goos":"darwin","goarch":"amd64","goamd64":"v1","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}}],"Format":"tar.gz","ID":"default","Replaces":null,"WrappedIn":""}},{"name":"go-pd_1.1.0-SNAPSHOT-85d261a_linux_amd64.tar.gz","path":"dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_amd64.tar.gz","goos":"linux","goarch":"amd64","goamd64":"v1","type":"Archive","extra":{"Binaries":["go-pd"],"Builds":[{"name":"go-pd","path":"/home/manuel/Workspace/go/go-pd/dist/go-pd_linux_amd64_v1/go-pd","goos":"linux","goarch":"amd64","goamd64":"v1","type":"Binary","extra":{"Binary":"go-pd","Ext":"","ID":"go-pd"}}],"Format":"tar.gz","ID":"default","Replaces":null,"WrappedIn":""}},{"name":"go-pd_1.1.0-SNAPSHOT-85d261a_checksums.txt","path":"dist/go-pd_1.1.0-SNAPSHOT-85d261a_checksums.txt","type":"Checksum","extra":{"Refresh":"func() error"}}] -------------------------------------------------------------------------------- /dist/config.yaml: -------------------------------------------------------------------------------- 1 | project_name: go-pd 2 | release: 3 | github: 4 | owner: ManuelReschke 5 | name: go-pd 6 | name_template: '{{.Tag}}' 7 | scoop: 8 | name: go-pd 9 | commit_author: 10 | name: goreleaserbot 11 | email: bot@goreleaser.com 12 | commit_msg_template: Scoop update for {{ .ProjectName }} version {{ .Tag }} 13 | goamd64: v1 14 | builds: 15 | - id: go-pd 16 | goos: 17 | - linux 18 | - darwin 19 | - windows 20 | goarch: 21 | - amd64 22 | - arm64 23 | - "386" 24 | goarm: 25 | - 7 26 | gomips: 27 | - hardfloat 28 | goamd64: 29 | - v1 30 | dir: . 31 | main: . 32 | binary: go-pd 33 | builder: go 34 | gobinary: go 35 | command: build 36 | ldflags: 37 | - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser 38 | archives: 39 | - id: default 40 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}' 41 | format: tar.gz 42 | files: 43 | - src: license* 44 | - src: LICENSE* 45 | - src: readme* 46 | - src: README* 47 | - src: changelog* 48 | - src: CHANGELOG* 49 | snapshot: 50 | name_template: '{{ .Version }}-SNAPSHOT-{{ .ShortCommit }}' 51 | checksum: 52 | name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt' 53 | algorithm: sha256 54 | dist: dist 55 | env_files: 56 | github_token: ~/.config/goreleaser/github_token 57 | gitlab_token: ~/.config/goreleaser/gitlab_token 58 | gitea_token: ~/.config/goreleaser/gitea_token 59 | source: 60 | name_template: '{{ .ProjectName }}-{{ .Version }}' 61 | format: tar.gz 62 | gomod: 63 | gobinary: go 64 | announce: 65 | twitter: 66 | message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' 67 | reddit: 68 | title_template: '{{ .ProjectName }} {{ .Tag }} is out!' 69 | url_template: '{{ .ReleaseURL }}' 70 | slack: 71 | message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' 72 | username: GoReleaser 73 | discord: 74 | message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' 75 | author: GoReleaser 76 | color: "3888754" 77 | icon_url: https://goreleaser.com/static/avatar.png 78 | teams: 79 | title_template: '{{ .ProjectName }} {{ .Tag }} is out!' 80 | message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' 81 | color: '#2D313E' 82 | icon_url: https://goreleaser.com/static/avatar.png 83 | smtp: 84 | subject_template: '{{ .ProjectName }} {{ .Tag }} is out!' 85 | body_template: 'You can view details from: {{ .ReleaseURL }}' 86 | mattermost: 87 | message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' 88 | title_template: '{{ .ProjectName }} {{ .Tag }} is out!' 89 | username: GoReleaser 90 | linkedin: 91 | message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' 92 | telegram: 93 | message_template: '{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}' 94 | webhook: 95 | message_template: '{ "message": "{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}"}' 96 | content_type: application/json; charset=utf-8 97 | github_urls: 98 | download: https://github.com 99 | gitlab_urls: 100 | download: https://gitlab.com 101 | -------------------------------------------------------------------------------- /dist/go-pd_1.1.0-SNAPSHOT-85d261a_checksums.txt: -------------------------------------------------------------------------------- 1 | 1de185c883496e85ea02c549e8e84959f22c43c542658934c89f04f67b432a90 go-pd_1.1.0-SNAPSHOT-85d261a_linux_386.tar.gz 2 | a14cc163ce1e54c451f2522b5e99233d25fc24f0b78c7877e47d6f8067a41b65 go-pd_1.1.0-SNAPSHOT-85d261a_linux_amd64.tar.gz 3 | c62389c77ea243fa6025c49bf7c27b49bd3110cb5ac3b1f81fae699dbe6c8650 go-pd_1.1.0-SNAPSHOT-85d261a_darwin_amd64.tar.gz 4 | ee1667dfea2f2e80d0dc4c1a2144c0de2840f6dca96a941e36f8916afd8e8917 go-pd_1.1.0-SNAPSHOT-85d261a_darwin_arm64.tar.gz 5 | f910f67600f6ac80c8c8842f7ce8bdd462fbf2883e257c0dca55bcec16be512c go-pd_1.1.0-SNAPSHOT-85d261a_linux_arm64.tar.gz 6 | -------------------------------------------------------------------------------- /dist/go-pd_1.1.0-SNAPSHOT-85d261a_darwin_amd64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_1.1.0-SNAPSHOT-85d261a_darwin_amd64.tar.gz -------------------------------------------------------------------------------- /dist/go-pd_1.1.0-SNAPSHOT-85d261a_darwin_arm64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_1.1.0-SNAPSHOT-85d261a_darwin_arm64.tar.gz -------------------------------------------------------------------------------- /dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_386.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_386.tar.gz -------------------------------------------------------------------------------- /dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_amd64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_amd64.tar.gz -------------------------------------------------------------------------------- /dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_arm64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_1.1.0-SNAPSHOT-85d261a_linux_arm64.tar.gz -------------------------------------------------------------------------------- /dist/go-pd_darwin_amd64_v1/go-pd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_darwin_amd64_v1/go-pd -------------------------------------------------------------------------------- /dist/go-pd_darwin_arm64/go-pd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_darwin_arm64/go-pd -------------------------------------------------------------------------------- /dist/go-pd_linux_386/go-pd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_linux_386/go-pd -------------------------------------------------------------------------------- /dist/go-pd_linux_amd64_v1/go-pd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_linux_amd64_v1/go-pd -------------------------------------------------------------------------------- /dist/go-pd_linux_arm64/go-pd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/dist/go-pd_linux_arm64/go-pd -------------------------------------------------------------------------------- /dist/metadata.json: -------------------------------------------------------------------------------- 1 | {"project_name":"go-pd","tag":"1.1.0","previous_tag":"1.0.0","version":"1.1.0-SNAPSHOT-85d261a","commit":"85d261a48a0ff7d0c80db5200f09808c71c42268","date":"2022-05-25T11:41:43.172837013+02:00","runtime":{"goos":"linux","goarch":"amd64"}} -------------------------------------------------------------------------------- /go-pd-upload-and-download.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/go-pd-upload-and-download.gif -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ManuelReschke/go-pd 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/imroc/req v0.3.2 7 | github.com/imroc/req/v3 v3.43.5 8 | github.com/joho/godotenv v1.4.0 9 | github.com/spf13/cobra v1.8.0 10 | github.com/stretchr/testify v1.8.4 11 | ) 12 | 13 | require ( 14 | github.com/andybalholm/brotli v1.1.0 // indirect 15 | github.com/cloudflare/circl v1.3.8 // indirect 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect 18 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 19 | github.com/google/pprof v0.0.0-20240509144519-723abb6459b7 // indirect 20 | github.com/hashicorp/errwrap v1.1.0 // indirect 21 | github.com/hashicorp/go-multierror v1.1.1 // indirect 22 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 23 | github.com/klauspost/compress v1.17.8 // indirect 24 | github.com/onsi/ginkgo/v2 v2.17.3 // indirect 25 | github.com/pmezard/go-difflib v1.0.0 // indirect 26 | github.com/quic-go/qpack v0.4.0 // indirect 27 | github.com/quic-go/quic-go v0.44.0 // indirect 28 | github.com/refraction-networking/utls v1.6.6 // indirect 29 | github.com/spf13/pflag v1.0.5 // indirect 30 | go.uber.org/mock v0.4.0 // indirect 31 | golang.org/x/crypto v0.23.0 // indirect 32 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect 33 | golang.org/x/mod v0.17.0 // indirect 34 | golang.org/x/net v0.25.0 // indirect 35 | golang.org/x/sys v0.20.0 // indirect 36 | golang.org/x/text v0.15.0 // indirect 37 | golang.org/x/tools v0.21.0 // indirect 38 | gopkg.in/yaml.v3 v3.0.1 // indirect 39 | ) 40 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 2 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 3 | github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= 4 | github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= 5 | github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 6 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 7 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 8 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 9 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 14 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 15 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 16 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 17 | github.com/google/pprof v0.0.0-20240509144519-723abb6459b7 h1:velgFPYr1X9TDwLIfkV7fWqsFlf7TeP11M/7kPd/dVI= 18 | github.com/google/pprof v0.0.0-20240509144519-723abb6459b7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= 19 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 20 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 21 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 22 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 23 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 24 | github.com/imroc/req v0.3.2 h1:M/JkeU6RPmX+WYvT2vaaOL0K+q8ufL5LxwvJc4xeB4o= 25 | github.com/imroc/req v0.3.2/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw= 26 | github.com/imroc/req/v3 v3.43.4 h1:NSXlB5dELZuxzGEFRWLWEQ9dQmh8d9pUMPa7MevK1K4= 27 | github.com/imroc/req/v3 v3.43.4/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8ToyQc2xA= 28 | github.com/imroc/req/v3 v3.43.5 h1:fL7dOEfld+iEv1rwnIxseJz2/Y7JZ/HgbAURLZkat80= 29 | github.com/imroc/req/v3 v3.43.5/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8ToyQc2xA= 30 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 31 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 32 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 33 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 34 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 35 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 36 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 37 | github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= 38 | github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 39 | github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= 40 | github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= 41 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 42 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 43 | github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= 44 | github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 45 | github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= 46 | github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= 47 | github.com/quic-go/quic-go v0.44.0 h1:So5wOr7jyO4vzL2sd8/pD9Kesciv91zSk8BoFngItQ0= 48 | github.com/quic-go/quic-go v0.44.0/go.mod h1:z4cx/9Ny9UtGITIPzmPTXh1ULfOyWh4qGQlpnPcWmek= 49 | github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig= 50 | github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= 51 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 52 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 53 | github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= 54 | github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= 55 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 56 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 57 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 58 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 59 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= 60 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 61 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 62 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 63 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 64 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 65 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 66 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 67 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 68 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 69 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 70 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 71 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 72 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 73 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 74 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 75 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 76 | go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= 77 | go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 78 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= 79 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 80 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= 81 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= 82 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= 83 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 84 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 85 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 86 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 87 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 88 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 89 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 90 | golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= 91 | golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 92 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 93 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 94 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 95 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 96 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 97 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 98 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 99 | -------------------------------------------------------------------------------- /internal/app/cmdDownload.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/ManuelReschke/go-pd/pkg/pd" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | func RunDownload(cmd *cobra.Command, args []string) error { 15 | if len(args) == 0 { 16 | return errors.New("please add a pixeldrain URL or file id to your download request") 17 | } 18 | 19 | path, err := cmd.Flags().GetString("path") 20 | if err != nil { 21 | return errors.New("please add a valid path where you want to save the files") 22 | } 23 | if path == "" { 24 | path, _ = os.Getwd() 25 | } 26 | 27 | apiKey, err := cmd.Flags().GetString("api-key") 28 | if err != nil { 29 | return errors.New("please add a valid API-Key to your request") 30 | } 31 | 32 | // file is here an url or an ID to a file 33 | for _, file := range args { 34 | fileID := file 35 | if strings.ContainsAny(file, pd.BaseURL) { 36 | fileID = filepath.Base(file) 37 | } 38 | 39 | req01 := &pd.RequestFileInfo{ 40 | ID: fileID, 41 | } 42 | if apiKey != "" { 43 | req01.Auth.APIKey = apiKey 44 | } 45 | 46 | c := pd.New(nil, nil) 47 | rsp, err := c.GetFileInfo(req01) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | req := &pd.RequestDownload{ 53 | ID: fileID, 54 | PathToSave: filepath.FromSlash(path + "/" + rsp.Name), 55 | } 56 | if apiKey != "" { 57 | req.Auth.APIKey = apiKey 58 | } 59 | 60 | rspDL, err := c.Download(req) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | msg := "" 66 | if rspDL.Success { 67 | if cmd.Flags().Changed("verbose") { 68 | msg = fmt.Sprintf("Successful! Download complete: %s | ID: %s | Stored to: %s", rspDL.FileName, req.ID, req.PathToSave) 69 | } else { 70 | msg = fmt.Sprintf("%s", req.PathToSave) 71 | } 72 | } else { 73 | msg = fmt.Sprintf("Failed! ID: %s | Value: %s | Message: %s", req.ID, rspDL.Value, rspDL.Message) 74 | } 75 | 76 | fmt.Println(msg) 77 | } 78 | 79 | return nil 80 | } 81 | -------------------------------------------------------------------------------- /internal/app/cmdUpload.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/ManuelReschke/go-pd/pkg/pd" 10 | "github.com/imroc/req/v3" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | func RunUpload(cmd *cobra.Command, args []string) error { 15 | if len(args) == 0 { 16 | return errors.New("please add a file to your upload request") 17 | } 18 | 19 | verboseFlag := cmd.Flags().Changed("verbose") 20 | 21 | // get key from ENV if available 22 | apiKeyEnv, ok := os.LookupEnv("PIXELDRAIN_API_KEY") 23 | if ok && verboseFlag { 24 | fmt.Println("Using API Key from environment variable: PIXELDRAIN_API_KEY") 25 | } 26 | 27 | apiKeyParam, err := cmd.Flags().GetString("api-key") 28 | if err != nil { 29 | return errors.New("please add a valid API-Key to your upload request") 30 | } 31 | 32 | for _, file := range args { 33 | // check if file exist 34 | if _, err := os.Stat(filepath.FromSlash(file)); errors.Is(err, os.ErrNotExist) { 35 | return errors.New("one of the given files does not exist") 36 | } 37 | 38 | r := &pd.RequestUpload{ 39 | PathToFile: file, 40 | Anonymous: true, 41 | } 42 | 43 | if apiKeyEnv != "" { 44 | r.Anonymous = false 45 | r.Auth.APIKey = apiKeyEnv 46 | } 47 | if apiKeyParam != "" { 48 | r.Anonymous = false 49 | r.Auth.APIKey = apiKeyParam 50 | } 51 | 52 | c := pd.New(nil, nil) 53 | if verboseFlag { 54 | c.SetUploadCallback(func(info req.UploadInfo) { 55 | if info.FileSize > 0 { 56 | fmt.Printf("%q uploaded %.2f%%\n", info.FileName, float64(info.UploadedSize)/float64(info.FileSize)*100.0) 57 | } else { 58 | fmt.Printf("%q uploaded 0%% (file size is zero)\n", info.FileName) 59 | } 60 | }) 61 | } 62 | rsp, err := c.UploadPOST(r) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | msg := "" 68 | if verboseFlag { 69 | msg = fmt.Sprintf("Successful! Anonymous upload: %v | ID: %s | URL: %s", r.Anonymous, rsp.ID, rsp.GetFileURL()) 70 | } else { 71 | msg = fmt.Sprintf("%s", rsp.GetFileURL()) 72 | } 73 | 74 | fmt.Println(msg) 75 | } 76 | 77 | return nil 78 | } 79 | -------------------------------------------------------------------------------- /logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/logo.jpg -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/ManuelReschke/go-pd/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /pkg/pd/.env: -------------------------------------------------------------------------------- 1 | API_KEY="" -------------------------------------------------------------------------------- /pkg/pd/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .env_test 3 | testdata/cat_download.jpg 4 | testdata/cat_download_thumbnail.jpg -------------------------------------------------------------------------------- /pkg/pd/mock_server.go: -------------------------------------------------------------------------------- 1 | package pd 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "net/http" 7 | "net/http/httptest" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | func MockFileUploadServer() *httptest.Server { 13 | return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 14 | 15 | switch r.Method { 16 | case "POST": 17 | // ########################################## 18 | // POST /file 19 | if r.URL.EscapedPath() == "/file" { 20 | _ = r.ParseMultipartForm(10485760) 21 | file := r.MultipartForm.File["file"] 22 | 23 | if file == nil || len(file) == 0 { 24 | log.Fatalln("Except request to have 'file'") 25 | } 26 | 27 | if r.FormValue("anonymous") == "" { 28 | log.Fatalln("Except request to have form value 'anonymous'") 29 | } 30 | 31 | w.WriteHeader(http.StatusCreated) 32 | str := `{ 33 | "success": true, 34 | "id": "123456" 35 | }` 36 | _, _ = w.Write([]byte(str)) 37 | } 38 | 39 | // ########################################## 40 | // POST /list 41 | if r.URL.EscapedPath() == "/list" { 42 | _ = r.ParseForm() 43 | 44 | w.WriteHeader(http.StatusOK) 45 | str := `{ 46 | "success": true, 47 | "id": "123456" 48 | }` 49 | _, _ = w.Write([]byte(str)) 50 | } 51 | 52 | case "PUT": 53 | // ########################################## 54 | // PUT /file/{name} 55 | if !strings.Contains(r.URL.EscapedPath(), "/file/") { 56 | log.Fatalf("wrong path'%s'", r.URL.EscapedPath()) 57 | } 58 | 59 | _ = r.ParseForm() 60 | 61 | if r.Body == nil || r.ContentLength == 0 { 62 | log.Fatalln("Empty body in PUT request") 63 | } 64 | 65 | //if r.FormValue("anonymous") == "" { 66 | // log.Fatalln("Except request to have form value 'anonymous'") 67 | //} 68 | 69 | w.WriteHeader(http.StatusCreated) 70 | str := `{ 71 | "id": "123456" 72 | }` 73 | _, _ = w.Write([]byte(str)) 74 | case "GET": 75 | // ########################################## 76 | // GET /file/{id} 77 | if r.URL.EscapedPath() == "/file/K1dA8U5W" { 78 | _ = r.ParseForm() 79 | 80 | fileID := filepath.Base(r.URL.EscapedPath()) 81 | if len(fileID) == 0 { 82 | log.Fatalf("empty file ID '%s'", fileID) 83 | } 84 | 85 | fileContent, err := ioutil.ReadFile("testdata/cat.jpg") 86 | if err != nil { 87 | log.Fatalln(err) 88 | } 89 | 90 | w.WriteHeader(http.StatusOK) 91 | w.Write(fileContent) 92 | } 93 | 94 | // ########################################## 95 | // GET /file/{id}/info 96 | if r.URL.EscapedPath() == "/file/K1dA8U5W/info" { 97 | _ = r.ParseForm() 98 | 99 | w.WriteHeader(http.StatusOK) 100 | str := `{ 101 | "id": "K1dA8U5W", 102 | "name": "screenshot.png", 103 | "size": 37621, 104 | "views": 1234, 105 | "bandwidth_used": 1234567890, 106 | "bandwidth_used_paid": 1234567890, 107 | "downloads": 1234, 108 | "date_upload": "2020-02-04T18:34:05.706801Z", 109 | "date_last_view": "2020-02-04T18:34:05.706801Z", 110 | "mime_type": "image/png", 111 | "thumbnail_href": "/file/1234abcd/thumbnail", 112 | "hash_sha256": "1af93d68009bdfd52e1da100a019a30b5fe083d2d1130919225ad0fd3d1fed0b", 113 | "can_edit": true 114 | }` 115 | _, _ = w.Write([]byte(str)) 116 | } 117 | 118 | // ########################################## 119 | // GET /file/{id}/thumbnail?width=x&height=x 120 | if r.URL.EscapedPath() == "/file/K1dA8U5W/thumbnail" { 121 | _ = r.ParseForm() 122 | 123 | fileContent, err := ioutil.ReadFile("testdata/cat_thumbnail.jpg") 124 | if err != nil { 125 | log.Fatalln(err) 126 | } 127 | 128 | w.WriteHeader(http.StatusOK) 129 | w.Write(fileContent) 130 | } 131 | 132 | // ########################################## 133 | // GET /list/{id} 134 | if r.URL.EscapedPath() == "/list/123" { 135 | _ = r.ParseForm() 136 | 137 | w.WriteHeader(http.StatusOK) 138 | str := `{ 139 | "success": true, 140 | "id": "123", 141 | "title": "Rust in Peace", 142 | "date_created": "2020-02-04T18:34:13.466276Z", 143 | "files": [ 144 | { 145 | "detail_href": "/file/_SqVWi/info", 146 | "description": "", 147 | "success": true, 148 | "id": "_SqVWi", 149 | "name": "01 Holy Wars... The Punishment Due.mp3", 150 | "size": 123456, 151 | "date_created": "2020-02-04T18:34:13.466276Z", 152 | "date_last_view": "2020-02-04T18:34:13.466276Z", 153 | "mime_type": "audio/mp3", 154 | "views": 1, 155 | "bandwidth_used": 1234567890, 156 | "thumbnail_href": "/file/_SqVWi/thumbnail" 157 | }, 158 | { 159 | "detail_href": "/file/RKwgZb/info", 160 | "description": "", 161 | "success": true, 162 | "id": "RKwgZb", 163 | "name": "02 Hangar 18.mp3", 164 | "size": 123456, 165 | "date_created": "2020-02-04T18:34:13.466276Z", 166 | "date_last_view": "2020-02-04T18:34:13.466276Z", 167 | "mime_type": "audio/mp3", 168 | "views": 2, 169 | "bandwidth_used": 1234567890, 170 | "thumbnail_href": "/file/RKwgZb/thumbnail" 171 | } 172 | ] 173 | }` 174 | _, _ = w.Write([]byte(str)) 175 | } 176 | 177 | // ########################################## 178 | // GET /user/files 179 | if r.URL.EscapedPath() == "/user/files" { 180 | _ = r.ParseForm() 181 | 182 | w.WriteHeader(http.StatusOK) 183 | str := `{ 184 | "files": [ 185 | { 186 | "id": "tUxgDCoQ", 187 | "name": "test_post_cat.jpg", 188 | "size": 37621, 189 | "views": 0, 190 | "bandwidth_used": 0, 191 | "bandwidth_used_paid": 0, 192 | "downloads": 0, 193 | "date_upload": "2022-03-30T16:30:17.152Z", 194 | "date_last_view": "2022-03-30T16:30:17.152Z", 195 | "mime_type": "image/jpeg", 196 | "thumbnail_href": "/file/tUxgDCoQ/thumbnail", 197 | "hash_sha256": "1af93d68009bdfd52e1da100a019a30b5fe083d2d1130919225ad0fd3d1fed0b", 198 | "availability": "", 199 | "availability_message": "", 200 | "abuse_type": "", 201 | "abuse_reporter_name": "", 202 | "can_edit": true, 203 | "show_ads": false, 204 | "allow_video_player": true, 205 | "download_speed_limit": 0 206 | } 207 | ] 208 | }` 209 | _, _ = w.Write([]byte(str)) 210 | } 211 | 212 | // ########################################## 213 | // GET /user 214 | if r.URL.EscapedPath() == "/user" { 215 | _ = r.ParseForm() 216 | 217 | w.WriteHeader(http.StatusOK) 218 | str := `{ 219 | "username":"TestTest", 220 | "email":"test@test.de", 221 | "subscription":{ 222 | "id":"", 223 | "name":"Free", 224 | "type":"", 225 | "file_size_limit":20000000000, 226 | "file_expiry_days":60, 227 | "storage_space":-1, 228 | "price_per_tb_storage":0, 229 | "price_per_tb_bandwidth":0, 230 | "monthly_transfer_cap":0, 231 | "file_viewer_branding":false 232 | }, 233 | "storage_space_used":18834, 234 | "is_admin":false, 235 | "balance_micro_eur":0, 236 | "hotlinking_enabled":true, 237 | "monthly_transfer_cap":0, 238 | "monthly_transfer_used":0, 239 | "file_viewer_branding":null, 240 | "file_embed_domains":"", 241 | "skip_file_viewer":false 242 | }` 243 | _, _ = w.Write([]byte(str)) 244 | } 245 | 246 | // ########################################## 247 | // GET /user/lists 248 | if r.URL.EscapedPath() == "/user/lists" { 249 | _ = r.ParseForm() 250 | 251 | w.WriteHeader(http.StatusOK) 252 | str := `{ 253 | "lists": [ 254 | { 255 | "id": "Cap4T1LP", 256 | "title": "Test List", 257 | "date_created": "2022-04-04T15:24:06.834Z", 258 | "file_count": 2, 259 | "files": null, 260 | "can_edit": true 261 | }, 262 | { 263 | "id": "fiEm5arj", 264 | "title": "Wallpaper", 265 | "date_created": "2022-04-20T09:46:42.017Z", 266 | "file_count": 2, 267 | "files": null, 268 | "can_edit": true 269 | } 270 | ] 271 | }` 272 | _, _ = w.Write([]byte(str)) 273 | } 274 | 275 | return 276 | case "DELETE": 277 | // ########################################## 278 | // DELETE /file/{id} 279 | if r.URL.EscapedPath() == "/file/K1dA8U5W" { 280 | 281 | w.WriteHeader(http.StatusOK) 282 | str := `{ 283 | "success": true, 284 | "value": "file_deleted", 285 | "message": "The file has been deleted." 286 | }` 287 | _, _ = w.Write([]byte(str)) 288 | } 289 | 290 | return 291 | default: 292 | return 293 | } 294 | })) 295 | } 296 | -------------------------------------------------------------------------------- /pkg/pd/pd.go: -------------------------------------------------------------------------------- 1 | package pd 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "net/http" 10 | "os" 11 | "strconv" 12 | "time" 13 | 14 | "github.com/imroc/req/v3" 15 | ) 16 | 17 | const ( 18 | Name = "PixelDrain.com" 19 | BaseURL = "https://pixeldrain.com/" 20 | APIURL = BaseURL + "api" 21 | DefaultUserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36" 22 | // errors 23 | ErrMissingPathToFile = "file path or file reader is required" 24 | ErrMissingFileID = "file id is required" 25 | ErrMissingFilename = "if you use ReadCloser you need to specify the filename" 26 | ) 27 | 28 | type ErrorMessage struct { 29 | ErrorCode int `json:"error_code" xml:"ErrorCode"` 30 | ErrorMessage string `json:"error_message" xml:"ErrorMessage"` 31 | } 32 | 33 | type ClientOptions struct { 34 | Debug bool 35 | ProxyURL string 36 | EnableCookies bool 37 | EnableInsecureTLS bool 38 | Timeout time.Duration 39 | } 40 | 41 | type ClientWrapper struct { 42 | Client *req.Client 43 | UploadCallback func(info req.UploadInfo) 44 | } 45 | 46 | type PixelDrainClient struct { 47 | Client *ClientWrapper 48 | Debug bool 49 | } 50 | 51 | // New - create a new PixelDrainClient 52 | func New(opt *ClientOptions, c *ClientWrapper) *PixelDrainClient { 53 | // set default values if no other options available 54 | if opt == nil { 55 | opt = &ClientOptions{ 56 | Debug: false, 57 | ProxyURL: "", 58 | EnableCookies: true, 59 | EnableInsecureTLS: true, 60 | Timeout: 1 * time.Hour, 61 | } 62 | } 63 | 64 | // build default client if not available 65 | if c == nil { 66 | client := req.C() 67 | client.SetUserAgent(DefaultUserAgent) 68 | 69 | c = &ClientWrapper{ 70 | Client: client, 71 | UploadCallback: nil, 72 | } 73 | } 74 | 75 | if opt.EnableInsecureTLS { 76 | c.Client.EnableInsecureSkipVerify() 77 | } 78 | 79 | if opt.Timeout != 0 { 80 | c.Client.SetTimeout(opt.Timeout) 81 | } 82 | 83 | if opt.ProxyURL != "" { 84 | _ = c.Client.SetProxyURL(opt.ProxyURL) 85 | } 86 | // cookie is available by default in v3 87 | if opt.EnableCookies == false { 88 | c.Client.SetCookieJar(nil) 89 | } 90 | 91 | pdc := &PixelDrainClient{ 92 | Client: c, 93 | Debug: opt.Debug, 94 | } 95 | 96 | return pdc 97 | } 98 | 99 | func (pd *PixelDrainClient) SetUploadCallback(callback func(info req.UploadInfo)) { 100 | pd.Client.UploadCallback = callback 101 | } 102 | 103 | // UploadPOST POST /api/file 104 | // curl -X POST -i -H "Authorization: Basic " -F "file=@cat.jpg" https://pixeldrain.com/api/file 105 | func (pd *PixelDrainClient) UploadPOST(r *RequestUpload) (*ResponseUpload, error) { 106 | if r.PathToFile == "" && r.File == nil { 107 | return nil, errors.New(ErrMissingPathToFile) 108 | } 109 | 110 | if r.URL == "" { 111 | r.URL = fmt.Sprint(APIURL + "/file") 112 | } 113 | 114 | if r.File != nil { 115 | if r.FileName == "" { 116 | return nil, errors.New(ErrMissingFilename) 117 | } 118 | } else { 119 | file, err := os.Open(r.PathToFile) 120 | defer file.Close() 121 | if err != nil { 122 | return nil, err 123 | } 124 | } 125 | 126 | headers := map[string]string{} 127 | 128 | // pixeldrain want an empty username and the APIKey as password 129 | if r.Auth.IsAuthAvailable() && !r.Anonymous { 130 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 131 | } 132 | 133 | var uploadRsp ResponseUpload 134 | var errMsg ErrorMessage 135 | rsp, err := pd.Client.Client.R(). 136 | SetHeaders(headers). 137 | SetQueryParam("anonymous", strconv.FormatBool(r.Anonymous)). 138 | SetSuccessResult(&uploadRsp). 139 | SetErrorResult(&errMsg). 140 | // currently a bug - https://github.com/imroc/req/issues/352 141 | // SetFileReader("file", r.GetFileName(), r.File). 142 | SetFile("file", r.PathToFile). 143 | SetUploadCallback(pd.Client.UploadCallback). 144 | Post(r.URL) 145 | if pd.Debug && rsp != nil { 146 | log.Println(rsp.Dump()) 147 | } 148 | if err != nil { 149 | return nil, err 150 | } 151 | 152 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 153 | err = errors.New(errMsg.ErrorMessage) 154 | return nil, err 155 | } 156 | 157 | uploadRsp.StatusCode = rsp.GetStatusCode() 158 | 159 | return &uploadRsp, nil 160 | } 161 | 162 | // UploadPUT PUT /api/file/{name} 163 | // curl -X PUT -i -H "Authorization: Basic " --upload-file cat.jpg https://pixeldrain.com/api/file/test_cat.jpg 164 | func (pd *PixelDrainClient) UploadPUT(r *RequestUpload) (*ResponseUpload, error) { 165 | if r.PathToFile == "" && r.File == nil { 166 | return nil, errors.New(ErrMissingPathToFile) 167 | } 168 | 169 | if r.File == nil && r.FileName == "" { 170 | return nil, errors.New(ErrMissingFilename) 171 | } 172 | 173 | file, err := os.Open(r.PathToFile) 174 | defer file.Close() 175 | if err != nil { 176 | return nil, err 177 | } 178 | r.File = file 179 | 180 | if r.URL == "" { 181 | r.URL = fmt.Sprintf(APIURL+"/file/%s", r.GetFileName()) 182 | } 183 | 184 | headers := map[string]string{} 185 | 186 | // pixeldrain want an empty username and the APIKey as password 187 | if r.Auth.IsAuthAvailable() && !r.Anonymous { 188 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 189 | } 190 | 191 | var uploadRsp ResponseUpload 192 | var errMsg ErrorMessage 193 | rsp, err := pd.Client.Client.R(). 194 | SetHeaders(headers). 195 | // SetQueryParam("anonymous", strconv.FormatBool(r.Anonymous)). 196 | SetSuccessResult(&uploadRsp). 197 | SetErrorResult(&errMsg). 198 | SetBody(r.File). 199 | SetUploadCallback(pd.Client.UploadCallback). 200 | Put(r.URL) 201 | if pd.Debug && rsp != nil { 202 | log.Println(rsp.Dump()) 203 | } 204 | if err != nil { 205 | return nil, err 206 | } 207 | 208 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 209 | err = errors.New(errMsg.ErrorMessage) 210 | return nil, err 211 | } 212 | 213 | uploadRsp.StatusCode = rsp.GetStatusCode() 214 | 215 | return &uploadRsp, nil 216 | } 217 | 218 | // Download GET /api/file/{id} 219 | func (pd *PixelDrainClient) Download(r *RequestDownload) (*ResponseDownload, error) { 220 | if r.PathToSave == "" { 221 | return nil, errors.New(ErrMissingPathToFile) 222 | } 223 | 224 | if r.ID == "" { 225 | return nil, errors.New(ErrMissingFileID) 226 | } 227 | 228 | if r.URL == "" { 229 | r.URL = fmt.Sprintf(APIURL+"/file/%s", r.ID) 230 | } 231 | 232 | headers := map[string]string{} 233 | // pixeldrain want an empty username and the APIKey as password 234 | if r.Auth.IsAuthAvailable() { 235 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 236 | } 237 | 238 | var errMsg ErrorMessage 239 | rsp, err := pd.Client.Client.R(). 240 | SetHeaders(headers). 241 | SetErrorResult(&errMsg). 242 | SetOutputFile(r.PathToSave). 243 | SetRetryCount(5). 244 | SetRetryFixedInterval(3 * time.Second). 245 | Get(r.URL) 246 | if pd.Debug && rsp != nil { 247 | log.Println(rsp.Dump()) 248 | } 249 | if err != nil { 250 | return nil, err 251 | } 252 | 253 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 254 | err = errors.New(errMsg.ErrorMessage) 255 | return nil, err 256 | } 257 | 258 | if rsp.GetStatusCode() != 200 { 259 | defaultRsp := &ResponseDefault{} 260 | defaultRsp.StatusCode = rsp.GetStatusCode() 261 | defaultRsp.Success = false 262 | downloadRsp := &ResponseDownload{ 263 | ResponseDefault: *defaultRsp, 264 | } 265 | 266 | return downloadRsp, nil 267 | } 268 | 269 | fInfo, err := os.Stat(r.PathToSave) 270 | if err != nil { 271 | return nil, err 272 | } 273 | 274 | downloadRsp := &ResponseDownload{ 275 | FilePath: r.PathToSave, 276 | FileName: fInfo.Name(), 277 | FileSize: fInfo.Size(), 278 | ResponseDefault: ResponseDefault{ 279 | StatusCode: rsp.GetStatusCode(), 280 | Success: true, 281 | }, 282 | } 283 | 284 | return downloadRsp, nil 285 | } 286 | 287 | // GetFileInfo GET /api/file/{id}/info 288 | func (pd *PixelDrainClient) GetFileInfo(r *RequestFileInfo) (*ResponseFileInfo, error) { 289 | if r.ID == "" { 290 | return nil, errors.New(ErrMissingFileID) 291 | } 292 | 293 | if r.URL == "" { 294 | r.URL = fmt.Sprintf(APIURL+"/file/%s/info", r.ID) 295 | } 296 | 297 | headers := map[string]string{} 298 | // pixeldrain want an empty username and the APIKey as password 299 | if r.Auth.IsAuthAvailable() { 300 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 301 | } 302 | 303 | var fileInfoRsp ResponseFileInfo 304 | var errMsg ErrorMessage 305 | rsp, err := pd.Client.Client.R(). 306 | SetHeaders(headers). 307 | SetSuccessResult(&fileInfoRsp). 308 | SetErrorResult(&errMsg). 309 | Get(r.URL) 310 | if pd.Debug && rsp != nil { 311 | log.Println(rsp.Dump()) 312 | } 313 | if err != nil { 314 | return nil, err 315 | } 316 | 317 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 318 | err = errors.New(errMsg.ErrorMessage) 319 | return nil, err 320 | } 321 | 322 | fileInfoRsp.StatusCode = rsp.GetStatusCode() 323 | if fileInfoRsp.StatusCode == http.StatusOK { 324 | fileInfoRsp.Success = true 325 | } 326 | 327 | return &fileInfoRsp, nil 328 | } 329 | 330 | // DownloadThumbnail GET /api/file/{id}/thumbnail?width=x&height=x 331 | func (pd *PixelDrainClient) DownloadThumbnail(r *RequestThumbnail) (*ResponseThumbnail, error) { 332 | if r.PathToSave == "" { 333 | return nil, errors.New(ErrMissingPathToFile) 334 | } 335 | 336 | if r.ID == "" { 337 | return nil, errors.New(ErrMissingFileID) 338 | } 339 | 340 | if r.URL == "" { 341 | r.URL = fmt.Sprintf(APIURL+"/file/%s/thumbnail", r.ID) 342 | } 343 | 344 | queryParams := map[string]string{} 345 | if r.Width != "" { 346 | queryParams["width"] = r.Width 347 | } 348 | if r.Height != "" { 349 | queryParams["height"] = r.Height 350 | } 351 | 352 | headers := map[string]string{} 353 | // pixeldrain want an empty username and the APIKey as password 354 | if r.Auth.IsAuthAvailable() { 355 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 356 | } 357 | 358 | var errMsg ErrorMessage 359 | rsp, err := pd.Client.Client.R(). 360 | SetQueryParams(queryParams). 361 | SetHeaders(headers). 362 | SetErrorResult(&errMsg). 363 | SetOutputFile(r.PathToSave). 364 | Get(r.URL) 365 | if pd.Debug && rsp != nil { 366 | log.Println(rsp.Dump()) 367 | } 368 | if err != nil { 369 | return nil, err 370 | } 371 | 372 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 373 | err = errors.New(errMsg.ErrorMessage) 374 | return nil, err 375 | } 376 | 377 | fInfo, err := os.Stat(r.PathToSave) 378 | if err != nil { 379 | return nil, err 380 | } 381 | 382 | rspStruct := &ResponseThumbnail{ 383 | FilePath: r.PathToSave, 384 | FileName: fInfo.Name(), 385 | FileSize: fInfo.Size(), 386 | ResponseDefault: ResponseDefault{ 387 | StatusCode: rsp.GetStatusCode(), 388 | Success: true, 389 | }, 390 | } 391 | 392 | return rspStruct, nil 393 | } 394 | 395 | // Delete DELETE /api/file/{id} 396 | func (pd *PixelDrainClient) Delete(r *RequestDelete) (*ResponseDelete, error) { 397 | if r.ID == "" { 398 | return nil, errors.New(ErrMissingFileID) 399 | } 400 | 401 | if r.URL == "" { 402 | r.URL = fmt.Sprintf(APIURL+"/file/%s", r.ID) 403 | } 404 | 405 | headers := map[string]string{} 406 | // pixeldrain want an empty username and the APIKey as password 407 | if r.Auth.IsAuthAvailable() { 408 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 409 | } 410 | 411 | var rspStruct ResponseDelete 412 | var errMsg ErrorMessage 413 | rsp, err := pd.Client.Client.R(). 414 | SetHeaders(headers). 415 | SetSuccessResult(&rspStruct). 416 | SetErrorResult(&errMsg). 417 | Delete(r.URL) 418 | if pd.Debug && rsp != nil { 419 | log.Println(rsp.Dump()) 420 | } 421 | if err != nil { 422 | return nil, err 423 | } 424 | 425 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 426 | err = errors.New(errMsg.ErrorMessage) 427 | return nil, err 428 | } 429 | 430 | rspStruct.StatusCode = rsp.GetStatusCode() 431 | 432 | return &rspStruct, nil 433 | } 434 | 435 | // CreateList POST /api/list 436 | func (pd *PixelDrainClient) CreateList(r *RequestCreateList) (*ResponseCreateList, error) { 437 | if r.URL == "" { 438 | r.URL = APIURL + "/list" 439 | } 440 | 441 | headers := map[string]string{} 442 | // pixeldrain want an empty username and the APIKey as password 443 | if r.Auth.IsAuthAvailable() { 444 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 445 | } 446 | 447 | data, err := json.Marshal(r) 448 | if err != nil { 449 | return nil, err 450 | } 451 | 452 | var rspStruct ResponseCreateList 453 | var errMsg ErrorMessage 454 | rsp, err := pd.Client.Client.R(). 455 | SetHeaders(headers). 456 | SetBodyJsonBytes(data). 457 | SetSuccessResult(&rspStruct). 458 | SetErrorResult(&errMsg). 459 | Post(r.URL) 460 | if pd.Debug && rsp != nil { 461 | log.Println(rsp.Dump()) 462 | } 463 | if err != nil { 464 | return nil, err 465 | } 466 | 467 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 468 | err = errors.New(errMsg.ErrorMessage) 469 | return nil, err 470 | } 471 | 472 | rspStruct.StatusCode = rsp.GetStatusCode() 473 | 474 | return &rspStruct, nil 475 | } 476 | 477 | // GetList GET /api/list/{id} 478 | func (pd *PixelDrainClient) GetList(r *RequestGetList) (*ResponseGetList, error) { 479 | if r.ID == "" { 480 | return nil, errors.New(ErrMissingFileID) 481 | } 482 | 483 | if r.URL == "" { 484 | r.URL = fmt.Sprintf(APIURL+"/list/%s", r.ID) 485 | } 486 | 487 | headers := map[string]string{} 488 | // pixeldrain want an empty username and the APIKey as password 489 | if r.Auth.IsAuthAvailable() { 490 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 491 | } 492 | 493 | var rspStruct ResponseGetList 494 | var errMsg ErrorMessage 495 | rsp, err := pd.Client.Client.R(). 496 | SetHeaders(headers). 497 | SetSuccessResult(&rspStruct). 498 | SetErrorResult(&errMsg). 499 | Get(r.URL) 500 | if pd.Debug && rsp != nil { 501 | log.Println(rsp.Dump()) 502 | } 503 | if err != nil { 504 | return nil, err 505 | } 506 | 507 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 508 | err = errors.New(errMsg.ErrorMessage) 509 | return nil, err 510 | } 511 | 512 | rspStruct.StatusCode = rsp.GetStatusCode() 513 | 514 | return &rspStruct, nil 515 | } 516 | 517 | // GetUser GET /api/user 518 | func (pd *PixelDrainClient) GetUser(r *RequestGetUser) (*ResponseGetUser, error) { 519 | if r.URL == "" { 520 | r.URL = APIURL + "/user" 521 | } 522 | 523 | headers := map[string]string{} 524 | // pixeldrain want an empty username and the APIKey as password 525 | if r.Auth.IsAuthAvailable() { 526 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 527 | } 528 | 529 | var rspStruct ResponseGetUser 530 | var errMsg ErrorMessage 531 | rsp, err := pd.Client.Client.R(). 532 | SetHeaders(headers). 533 | SetSuccessResult(&rspStruct). 534 | SetErrorResult(&errMsg). 535 | Get(r.URL) 536 | if pd.Debug && rsp != nil { 537 | log.Println(rsp.Dump()) 538 | } 539 | if err != nil { 540 | return nil, err 541 | } 542 | 543 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 544 | err = errors.New(errMsg.ErrorMessage) 545 | return nil, err 546 | } 547 | 548 | status := false 549 | if rsp.GetStatusCode() == http.StatusOK { 550 | status = true 551 | } 552 | 553 | rspStruct.Success = status 554 | rspStruct.StatusCode = rsp.GetStatusCode() 555 | 556 | return &rspStruct, nil 557 | } 558 | 559 | // GetUserFiles GET /api/user/files 560 | func (pd *PixelDrainClient) GetUserFiles(r *RequestGetUserFiles) (*ResponseGetUserFiles, error) { 561 | if r.URL == "" { 562 | r.URL = APIURL + "/user/files" 563 | } 564 | 565 | headers := map[string]string{} 566 | // pixeldrain want an empty username and the APIKey as password 567 | if r.Auth.IsAuthAvailable() { 568 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 569 | } 570 | 571 | var rspStruct ResponseGetUserFiles 572 | var errMsg ErrorMessage 573 | rsp, err := pd.Client.Client.R(). 574 | SetHeaders(headers). 575 | SetSuccessResult(&rspStruct). 576 | SetErrorResult(&errMsg). 577 | Get(r.URL) 578 | if pd.Debug && rsp != nil { 579 | log.Println(rsp.Dump()) 580 | } 581 | if err != nil { 582 | return nil, err 583 | } 584 | 585 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 586 | err = errors.New(errMsg.ErrorMessage) 587 | return nil, err 588 | } 589 | 590 | status := false 591 | if rsp.GetStatusCode() == http.StatusOK { 592 | status = true 593 | } 594 | 595 | rspStruct.Success = status 596 | rspStruct.StatusCode = rsp.GetStatusCode() 597 | 598 | return &rspStruct, nil 599 | } 600 | 601 | // GetUserLists GET /api/user/lists 602 | func (pd *PixelDrainClient) GetUserLists(r *RequestGetUserLists) (*ResponseGetUserLists, error) { 603 | if r.URL == "" { 604 | r.URL = APIURL + "/user/lists" 605 | } 606 | 607 | headers := map[string]string{} 608 | // pixeldrain want an empty username and the APIKey as password 609 | if r.Auth.IsAuthAvailable() { 610 | addBasicAuthHeader(headers, "", r.Auth.APIKey) 611 | } 612 | 613 | var rspStruct ResponseGetUserLists 614 | var errMsg ErrorMessage 615 | rsp, err := pd.Client.Client.R(). 616 | SetHeaders(headers). 617 | SetSuccessResult(&rspStruct). 618 | SetErrorResult(&errMsg). 619 | Get(r.URL) 620 | if pd.Debug && rsp != nil { 621 | log.Println(rsp.Dump()) 622 | } 623 | if err != nil { 624 | return nil, err 625 | } 626 | 627 | if rsp != nil && rsp.IsErrorState() { // Status code >= 400. 628 | err = errors.New(errMsg.ErrorMessage) 629 | return nil, err 630 | } 631 | 632 | status := false 633 | if rsp.GetStatusCode() == http.StatusOK { 634 | status = true 635 | } 636 | 637 | rspStruct.Success = status 638 | rspStruct.StatusCode = rsp.GetStatusCode() 639 | 640 | return &rspStruct, nil 641 | } 642 | 643 | // pixeldrain want an empty username and the APIKey as password 644 | // addBasicAuthHeader create a http basic auth header from username and password 645 | func addBasicAuthHeader(h map[string]string, u string, p string) map[string]string { 646 | h["Authorization"] = "Basic " + generateBasicAuthToken(u, p) 647 | 648 | return h 649 | } 650 | 651 | // generateBasicAuthToken generate string for basic auth header 652 | func generateBasicAuthToken(u string, p string) string { 653 | auth := u + ":" + p 654 | 655 | return base64.StdEncoding.EncodeToString([]byte(auth)) 656 | } 657 | -------------------------------------------------------------------------------- /pkg/pd/pd_test.go: -------------------------------------------------------------------------------- 1 | package pd_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | 8 | "github.com/ManuelReschke/go-pd/pkg/pd" 9 | "github.com/joho/godotenv" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | const SkipIntegrationTest = "skipping integration test" 14 | 15 | var fileIDPost string 16 | var fileIDPut string 17 | var listID string 18 | 19 | // TestPD_UploadPOST is a unit test for the POST upload method 20 | func TestPD_UploadPOST(t *testing.T) { 21 | server := pd.MockFileUploadServer() 22 | defer server.Close() 23 | testURL := server.URL + "/file" 24 | 25 | rq := &pd.RequestUpload{ 26 | PathToFile: "testdata/cat.jpg", 27 | FileName: "test_post_cat.jpg", 28 | Anonymous: true, 29 | URL: testURL, 30 | } 31 | 32 | c := pd.New(nil, nil) 33 | rsp, err := c.UploadPOST(rq) 34 | if err != nil { 35 | t.Error(err) 36 | } 37 | 38 | assert.Equal(t, 201, rsp.StatusCode) 39 | assert.NotEmpty(t, rsp.ID) 40 | assert.Equal(t, "https://pixeldrain.com/u/123456", rsp.GetFileURL()) 41 | fmt.Println("POST Req: " + rsp.GetFileURL()) 42 | } 43 | 44 | // TestPD_UploadPOST_Integration run a real integration test against the service 45 | func TestPD_UploadPOST_Integration(t *testing.T) { 46 | if testing.Short() { 47 | t.Skip(SkipIntegrationTest) 48 | } 49 | 50 | rq := &pd.RequestUpload{ 51 | PathToFile: "testdata/cat.jpg", 52 | FileName: "test_post_cat.jpg", 53 | } 54 | 55 | rq.Auth = setAuthFromEnv() 56 | 57 | c := pd.New(nil, nil) 58 | rsp, err := c.UploadPOST(rq) 59 | if err != nil { 60 | t.Error(err) 61 | } 62 | 63 | assert.Equal(t, 201, rsp.StatusCode) 64 | assert.NotEmpty(t, rsp.ID) 65 | assert.True(t, rsp.Success) 66 | fileIDPost = rsp.ID 67 | fmt.Println("POST Req: " + rsp.GetFileURL()) 68 | } 69 | 70 | // currently not supported 71 | // 72 | // // TestPD_UploadPOST_WithReadCloser_Integration run a real integration test against the service 73 | // func TestPD_UploadPOST_WithReadCloser_Integration(t *testing.T) { 74 | // if testing.Short() { 75 | // t.Skip(SkipIntegrationTest) 76 | // } 77 | // 78 | // // ReadCloser 79 | // file, _ := os.Open("testdata/cat.jpg") 80 | // 81 | // req := &pd.RequestUpload{ 82 | // File: file, 83 | // FileName: "test_post_cat.jpg", 84 | // } 85 | // 86 | // req.Auth = setAuthFromEnv() 87 | // 88 | // c := pd.New(nil, nil) 89 | // rsp, err := c.UploadPOST(req) 90 | // if err != nil { 91 | // t.Error(err) 92 | // } 93 | // 94 | // assert.Equal(t, 201, rsp.StatusCode) 95 | // assert.NotEmpty(t, rsp.ID) 96 | // fmt.Println("POST Req: " + rsp.GetFileURL()) 97 | // } 98 | 99 | // TestPD_UploadPUT is a unit test for the PUT upload method 100 | func TestPD_UploadPUT(t *testing.T) { 101 | server := pd.MockFileUploadServer() 102 | defer server.Close() 103 | testURL := server.URL + "/file/" 104 | 105 | req := &pd.RequestUpload{ 106 | PathToFile: "testdata/cat.jpg", 107 | FileName: "test_put_cat.jpg", 108 | Anonymous: true, 109 | URL: testURL + "test_put_cat.jpg", 110 | } 111 | 112 | c := pd.New(nil, nil) 113 | rsp, err := c.UploadPUT(req) 114 | if err != nil { 115 | t.Error(err) 116 | } 117 | 118 | assert.Equal(t, 201, rsp.StatusCode) 119 | assert.NotEmpty(t, rsp.ID) 120 | assert.Equal(t, "https://pixeldrain.com/u/123456", rsp.GetFileURL()) 121 | fmt.Println("PUT Req: " + rsp.GetFileURL()) 122 | } 123 | 124 | // TestPD_UploadPUT_Integration run a real integration test against the service 125 | func TestPD_UploadPUT_Integration(t *testing.T) { 126 | if testing.Short() { 127 | t.Skip(SkipIntegrationTest) 128 | } 129 | 130 | req := &pd.RequestUpload{ 131 | PathToFile: "testdata/cat.jpg", 132 | FileName: "test_put_cat.jpg", 133 | } 134 | 135 | req.Auth = setAuthFromEnv() 136 | 137 | c := pd.New(nil, nil) 138 | rsp, err := c.UploadPUT(req) 139 | if err != nil { 140 | t.Error(err) 141 | } 142 | 143 | assert.Equal(t, 201, rsp.StatusCode) 144 | assert.NotEmpty(t, rsp.ID) 145 | fileIDPut = rsp.ID 146 | fmt.Println("PUT Req: " + rsp.GetFileURL()) 147 | } 148 | 149 | // TestPD_Download is a unit test for the GET "download" method 150 | func TestPD_Download(t *testing.T) { 151 | server := pd.MockFileUploadServer() 152 | defer server.Close() 153 | testURL := server.URL + "/file/K1dA8U5W" 154 | 155 | req := &pd.RequestDownload{ 156 | PathToSave: "testdata/cat_download.jpg", 157 | ID: "K1dA8U5W", 158 | URL: testURL, 159 | } 160 | 161 | c := pd.New(nil, nil) 162 | rsp, err := c.Download(req) 163 | if err != nil { 164 | t.Error(err) 165 | } 166 | 167 | assert.Equal(t, 200, rsp.StatusCode) 168 | assert.Equal(t, true, rsp.Success) 169 | } 170 | 171 | // TestPD_Download_Integration run a real integration test against the service 172 | func TestPD_Download_Integration(t *testing.T) { 173 | if testing.Short() { 174 | t.Skip(SkipIntegrationTest) 175 | } 176 | 177 | req := &pd.RequestDownload{ 178 | PathToSave: "testdata/cat_download.jpg", 179 | ID: fileIDPost, 180 | } 181 | 182 | req.Auth = setAuthFromEnv() 183 | 184 | c := pd.New(nil, nil) 185 | rsp, err := c.Download(req) 186 | if err != nil { 187 | t.Error(err) 188 | } 189 | 190 | assert.Equal(t, 200, rsp.StatusCode) 191 | assert.Equal(t, "cat_download.jpg", rsp.FileName) 192 | assert.Equal(t, int64(37621), rsp.FileSize) 193 | } 194 | 195 | // TestPD_GetFileInfo is a unit test for the GET "file info" method 196 | func TestPD_GetFileInfo(t *testing.T) { 197 | server := pd.MockFileUploadServer() 198 | defer server.Close() 199 | testURL := server.URL + "/file/K1dA8U5W/info" 200 | 201 | req := &pd.RequestFileInfo{ 202 | ID: "K1dA8U5W", 203 | URL: testURL, 204 | } 205 | 206 | c := pd.New(nil, nil) 207 | rsp, err := c.GetFileInfo(req) 208 | if err != nil { 209 | t.Error(err) 210 | } 211 | 212 | assert.Equal(t, 200, rsp.StatusCode) 213 | assert.Equal(t, true, rsp.Success) 214 | assert.Equal(t, "K1dA8U5W", rsp.ID) 215 | assert.Equal(t, int64(37621), rsp.Size) 216 | assert.Equal(t, "1af93d68009bdfd52e1da100a019a30b5fe083d2d1130919225ad0fd3d1fed0b", rsp.HashSha256) 217 | } 218 | 219 | // TestPD_GetFileInfo_Integration run a real integration test against the service 220 | func TestPD_GetFileInfo_Integration(t *testing.T) { 221 | if testing.Short() { 222 | t.Skip(SkipIntegrationTest) 223 | } 224 | 225 | req := &pd.RequestFileInfo{ 226 | ID: fileIDPost, 227 | } 228 | 229 | req.Auth = setAuthFromEnv() 230 | 231 | c := pd.New(nil, nil) 232 | rsp, err := c.GetFileInfo(req) 233 | if err != nil { 234 | t.Error(err) 235 | } 236 | 237 | assert.Equal(t, 200, rsp.StatusCode) 238 | assert.Equal(t, true, rsp.Success) 239 | assert.Equal(t, fileIDPost, rsp.ID) 240 | assert.Equal(t, int64(37621), rsp.Size) 241 | assert.Equal(t, "1af93d68009bdfd52e1da100a019a30b5fe083d2d1130919225ad0fd3d1fed0b", rsp.HashSha256) 242 | } 243 | 244 | // TestPD_DownloadThumbnail is a unit test for the GET "download thumbnail" method 245 | func TestPD_DownloadThumbnail(t *testing.T) { 246 | server := pd.MockFileUploadServer() 247 | defer server.Close() 248 | testURL := server.URL + "/file/K1dA8U5W/thumbnail?width=64&height=64" 249 | 250 | req := &pd.RequestThumbnail{ 251 | ID: "K1dA8U5W", 252 | Height: "64", 253 | Width: "64", 254 | PathToSave: "testdata/cat_download_thumbnail.jpg", 255 | URL: testURL, 256 | } 257 | 258 | req.Auth = setAuthFromEnv() 259 | 260 | c := pd.New(nil, nil) 261 | rsp, err := c.DownloadThumbnail(req) 262 | if err != nil { 263 | t.Error(err) 264 | } 265 | 266 | assert.Equal(t, 200, rsp.StatusCode) 267 | assert.Equal(t, "cat_download_thumbnail.jpg", rsp.FileName) 268 | assert.Equal(t, int64(7056), rsp.FileSize) 269 | } 270 | 271 | // TestPD_DownloadThumbnail_Integration run a real integration test against the service 272 | func TestPD_DownloadThumbnail_Integration(t *testing.T) { 273 | if testing.Short() { 274 | t.Skip(SkipIntegrationTest) 275 | } 276 | 277 | req := &pd.RequestThumbnail{ 278 | ID: fileIDPost, 279 | Height: "64", 280 | Width: "64", 281 | PathToSave: "testdata/cat_download_thumbnail.jpg", 282 | } 283 | 284 | req.Auth = setAuthFromEnv() 285 | 286 | c := pd.New(nil, nil) 287 | rsp, err := c.DownloadThumbnail(req) 288 | if err != nil { 289 | t.Error(err) 290 | } 291 | 292 | assert.Equal(t, 200, rsp.StatusCode) 293 | assert.Equal(t, "cat_download_thumbnail.jpg", rsp.FileName) 294 | assert.Equal(t, int64(7056), rsp.FileSize) 295 | } 296 | 297 | // TestPD_CreateList is a unit test for the POST "list" method 298 | func TestPD_CreateList(t *testing.T) { 299 | server := pd.MockFileUploadServer() 300 | defer server.Close() 301 | testURL := server.URL + "/list" 302 | 303 | // files to add 304 | files := []pd.ListFile{ 305 | {ID: "K1dA8U5W", Description: "Hallo Welt"}, 306 | {ID: "bmrc4iyD", Description: "Hallo Welt 2"}, 307 | } 308 | 309 | // create list request 310 | req := &pd.RequestCreateList{ 311 | Title: "Test List", 312 | Anonymous: false, 313 | Files: files, 314 | URL: testURL, 315 | } 316 | 317 | req.Auth = setAuthFromEnv() 318 | 319 | c := pd.New(nil, nil) 320 | rsp, err := c.CreateList(req) 321 | if err != nil { 322 | t.Error(err) 323 | } 324 | 325 | assert.Equal(t, 200, rsp.StatusCode) 326 | assert.Equal(t, true, rsp.Success) 327 | assert.NotEmpty(t, rsp.ID) 328 | } 329 | 330 | // TestPD_Delete_Integration run a real integration test against the service 331 | func TestPD_CreateList_Integration(t *testing.T) { 332 | if testing.Short() { 333 | t.Skip(SkipIntegrationTest) 334 | } 335 | 336 | // files to add 337 | files := []pd.ListFile{ 338 | {ID: fileIDPost, Description: "Hallo Welt"}, 339 | {ID: fileIDPut, Description: "Hallo Welt 2"}, 340 | } 341 | 342 | // create list request 343 | req := &pd.RequestCreateList{ 344 | Title: "Test List", 345 | Anonymous: false, 346 | Files: files, 347 | } 348 | 349 | req.Auth = setAuthFromEnv() 350 | 351 | c := pd.New(nil, nil) 352 | rsp, err := c.CreateList(req) 353 | if err != nil { 354 | t.Error(err) 355 | } 356 | 357 | assert.Equal(t, 201, rsp.StatusCode) 358 | assert.Equal(t, true, rsp.Success) 359 | listID = rsp.ID 360 | } 361 | 362 | // TestPD_GetList is a unit test for the GET "list/{id}" method 363 | func TestPD_GetList(t *testing.T) { 364 | server := pd.MockFileUploadServer() 365 | defer server.Close() 366 | testURL := server.URL + "/list/123" 367 | 368 | req := &pd.RequestGetList{ 369 | ID: "123", 370 | URL: testURL, 371 | } 372 | 373 | req.Auth = setAuthFromEnv() 374 | 375 | c := pd.New(nil, nil) 376 | rsp, err := c.GetList(req) 377 | if err != nil { 378 | t.Error(err) 379 | } 380 | 381 | assert.Equal(t, 200, rsp.StatusCode) 382 | assert.Equal(t, true, rsp.Success) 383 | assert.NotEmpty(t, rsp.ID) 384 | assert.Equal(t, "Rust in Peace", rsp.Title) 385 | assert.Equal(t, int64(123456), rsp.Files[0].Size) 386 | } 387 | 388 | // TestPD_GetList_Integration run a real integration test against the service 389 | func TestPD_GetList_Integration(t *testing.T) { 390 | if testing.Short() { 391 | t.Skip(SkipIntegrationTest) 392 | } 393 | 394 | req := &pd.RequestGetList{ 395 | ID: listID, 396 | } 397 | 398 | req.Auth = setAuthFromEnv() 399 | 400 | c := pd.New(nil, nil) 401 | rsp, err := c.GetList(req) 402 | if err != nil { 403 | t.Error(err) 404 | } 405 | 406 | assert.Equal(t, 200, rsp.StatusCode) 407 | assert.Equal(t, true, rsp.Success) 408 | assert.NotEmpty(t, rsp.ID) 409 | assert.Equal(t, "Test List", rsp.Title) 410 | assert.Equal(t, int64(37621), rsp.Files[0].Size) 411 | } 412 | 413 | // TestPD_GetUser is a unit test for the GET "/user" method 414 | func TestPD_GetUser(t *testing.T) { 415 | server := pd.MockFileUploadServer() 416 | defer server.Close() 417 | testURL := server.URL + "/user" 418 | 419 | req := &pd.RequestGetUser{ 420 | URL: testURL, 421 | } 422 | 423 | req.Auth = setAuthFromEnv() 424 | 425 | c := pd.New(nil, nil) 426 | rsp, err := c.GetUser(req) 427 | if err != nil { 428 | t.Error(err) 429 | } 430 | 431 | assert.Equal(t, 200, rsp.StatusCode) 432 | assert.Equal(t, true, rsp.Success) 433 | assert.Equal(t, "TestTest", rsp.Username) 434 | assert.Equal(t, "Free", rsp.Subscription.Name) 435 | } 436 | 437 | // TestPD_GetUser_Integration run a real integration test against the service 438 | func TestPD_GetUser_Integration(t *testing.T) { 439 | if testing.Short() { 440 | t.Skip(SkipIntegrationTest) 441 | } 442 | 443 | req := &pd.RequestGetUser{} 444 | 445 | req.Auth = setAuthFromEnv() 446 | 447 | c := pd.New(nil, nil) 448 | rsp, err := c.GetUser(req) 449 | if err != nil { 450 | t.Error(err) 451 | } 452 | 453 | assert.Equal(t, 200, rsp.StatusCode) 454 | assert.Equal(t, true, rsp.Success) 455 | assert.Equal(t, "ManuelReschke", rsp.Username) 456 | assert.Equal(t, "Free", rsp.Subscription.Name) 457 | } 458 | 459 | // TestPD_GetUserFiles is a unit test for the GET "/user/files" method 460 | func TestPD_GetUserFiles(t *testing.T) { 461 | server := pd.MockFileUploadServer() 462 | defer server.Close() 463 | testURL := server.URL + "/user/files" 464 | 465 | req := &pd.RequestGetUserFiles{ 466 | URL: testURL, 467 | } 468 | 469 | req.Auth = setAuthFromEnv() 470 | 471 | c := pd.New(nil, nil) 472 | rsp, err := c.GetUserFiles(req) 473 | if err != nil { 474 | t.Error(err) 475 | } 476 | 477 | assert.Equal(t, 200, rsp.StatusCode) 478 | assert.Equal(t, true, rsp.Success) 479 | assert.Equal(t, "tUxgDCoQ", rsp.Files[0].ID) 480 | assert.Equal(t, "test_post_cat.jpg", rsp.Files[0].Name) 481 | } 482 | 483 | // TestPD_GetUserFiles_Integration run a real integration test against the service 484 | func TestPD_GetUserFiles_Integration(t *testing.T) { 485 | if testing.Short() { 486 | t.Skip(SkipIntegrationTest) 487 | } 488 | 489 | req := &pd.RequestGetUserFiles{} 490 | 491 | req.Auth = setAuthFromEnv() 492 | 493 | c := pd.New(nil, nil) 494 | rsp, err := c.GetUserFiles(req) 495 | if err != nil { 496 | t.Error(err) 497 | } 498 | 499 | assert.Equal(t, 200, rsp.StatusCode) 500 | assert.Equal(t, true, rsp.Success) 501 | 502 | if len(rsp.Files) >= 2 { 503 | assert.True(t, true) 504 | } 505 | 506 | for _, file := range rsp.Files { 507 | if file.ID == fileIDPost { 508 | assert.Equal(t, fileIDPost, file.ID) 509 | } 510 | 511 | if file.ID == fileIDPut { 512 | assert.Equal(t, fileIDPut, file.ID) 513 | } 514 | } 515 | } 516 | 517 | // TestPD_GetUserLists is a unit test for the GET "/user/files" method 518 | func TestPD_GetUserLists(t *testing.T) { 519 | server := pd.MockFileUploadServer() 520 | defer server.Close() 521 | testURL := server.URL + "/user/lists" 522 | 523 | req := &pd.RequestGetUserLists{ 524 | URL: testURL, 525 | } 526 | 527 | req.Auth = setAuthFromEnv() 528 | 529 | c := pd.New(nil, nil) 530 | rsp, err := c.GetUserLists(req) 531 | if err != nil { 532 | t.Error(err) 533 | } 534 | 535 | assert.Equal(t, 200, rsp.StatusCode) 536 | assert.Equal(t, true, rsp.Success) 537 | assert.Equal(t, "Test List", rsp.Lists[0].Title) 538 | } 539 | 540 | // TestPD_GetUserLists_Integration run a real integration test against the service 541 | func TestPD_GetUserLists_Integration(t *testing.T) { 542 | if testing.Short() { 543 | t.Skip(SkipIntegrationTest) 544 | } 545 | 546 | req := &pd.RequestGetUserLists{} 547 | 548 | req.Auth = setAuthFromEnv() 549 | 550 | c := pd.New(nil, nil) 551 | rsp, err := c.GetUserLists(req) 552 | if err != nil { 553 | t.Error(err) 554 | } 555 | 556 | assert.Equal(t, 200, rsp.StatusCode) 557 | assert.Equal(t, true, rsp.Success) 558 | assert.Equal(t, "Test List", rsp.Lists[0].Title) 559 | } 560 | 561 | // TestPD_Delete is a unit test for the DELETE "delete" method 562 | func TestPD_Delete(t *testing.T) { 563 | server := pd.MockFileUploadServer() 564 | defer server.Close() 565 | testURL := server.URL + "/file/K1dA8U5W" 566 | 567 | req := &pd.RequestDelete{ 568 | ID: "K1dA8U5W", 569 | URL: testURL, 570 | } 571 | 572 | req.Auth = setAuthFromEnv() 573 | 574 | c := pd.New(nil, nil) 575 | rsp, err := c.Delete(req) 576 | if err != nil { 577 | t.Error(err) 578 | } 579 | 580 | assert.Equal(t, true, rsp.Success) 581 | assert.Equal(t, "file_deleted", rsp.Value) 582 | assert.Equal(t, "The file has been deleted.", rsp.Message) 583 | } 584 | 585 | // TestPD_Delete_Integration run a real integration test against the service 586 | func TestPD_Delete_Integration(t *testing.T) { 587 | if testing.Short() { 588 | t.Skip(SkipIntegrationTest) 589 | } 590 | 591 | req := &pd.RequestDelete{ 592 | ID: fileIDPost, 593 | } 594 | 595 | req.Auth = setAuthFromEnv() 596 | 597 | c := pd.New(nil, nil) 598 | rsp, err := c.Delete(req) 599 | if err != nil { 600 | t.Error(err) 601 | } 602 | 603 | assert.Equal(t, true, rsp.Success) 604 | assert.Equal(t, "ok", rsp.Value) 605 | assert.Equal(t, "The requested action was successfully performed", rsp.Message) 606 | 607 | req = &pd.RequestDelete{ 608 | ID: fileIDPut, 609 | } 610 | 611 | req.Auth = setAuthFromEnv() 612 | 613 | c = pd.New(nil, nil) 614 | rsp, err = c.Delete(req) 615 | if err != nil { 616 | t.Error(err) 617 | } 618 | 619 | assert.Equal(t, true, rsp.Success) 620 | assert.Equal(t, "ok", rsp.Value) 621 | assert.Equal(t, "The requested action was successfully performed", rsp.Message) 622 | } 623 | 624 | func setAuthFromEnv() pd.Auth { 625 | // load api key from .env_test file 626 | currentWorkDirectory, _ := os.Getwd() 627 | _ = godotenv.Load(currentWorkDirectory + "/.env_test") 628 | apiKey := os.Getenv("API_KEY") 629 | 630 | return pd.Auth{ 631 | APIKey: apiKey, 632 | } 633 | } 634 | -------------------------------------------------------------------------------- /pkg/pd/request.go: -------------------------------------------------------------------------------- 1 | package pd 2 | 3 | import ( 4 | "io" 5 | "path/filepath" 6 | ) 7 | 8 | // Auth hold the auth information 9 | type Auth struct { 10 | APIKey string // if you have an account you can enter here your API Key for uploading in your account 11 | } 12 | 13 | // IsAuthAvailable checks if an API Key available 14 | func (a *Auth) IsAuthAvailable() bool { 15 | auth := false 16 | if a.APIKey != "" { 17 | auth = true 18 | } 19 | 20 | return auth 21 | } 22 | 23 | // RequestUpload container for the upload information 24 | type RequestUpload struct { 25 | File io.ReadCloser 26 | PathToFile string // path to the file "/home/user/cat.jpg" 27 | FileName string // just the filename "test.jpg" 28 | Anonymous bool // if the upload is anonymous or with auth 29 | Auth Auth 30 | URL string // specific the upload endpoint, is set by default with the correct values 31 | } 32 | 33 | // GetFileName return the filename from the path if no specific filename in the params 34 | func (r *RequestUpload) GetFileName() string { 35 | if r.FileName == "" { 36 | if r.PathToFile != "" { 37 | r.FileName = filepath.Base(r.PathToFile) 38 | } 39 | } 40 | 41 | return r.FileName 42 | } 43 | 44 | // RequestDownload container for the file download 45 | type RequestDownload struct { 46 | ID string 47 | PathToSave string 48 | Auth Auth 49 | URL string // specific the API endpoint, is set by default with the correct values 50 | } 51 | 52 | // RequestFileInfo the FileInfo request needs only an ID 53 | type RequestFileInfo struct { 54 | ID string 55 | Auth Auth 56 | URL string 57 | } 58 | 59 | // RequestThumbnail the Thumbnail request needs the ID and width and height 60 | type RequestThumbnail struct { 61 | ID string 62 | Width string 63 | Height string 64 | PathToSave string 65 | Auth Auth 66 | URL string 67 | } 68 | 69 | // RequestDelete delete the file if you are the owner with the given ID 70 | type RequestDelete struct { 71 | ID string 72 | Auth Auth 73 | URL string 74 | } 75 | 76 | // RequestCreateList parameters for creating new list 77 | type RequestCreateList struct { 78 | Title string `json:"title"` 79 | Anonymous bool `json:"anonymous"` 80 | Files []ListFile `json:"files"` 81 | Auth Auth 82 | URL string 83 | } 84 | 85 | // ListFile a file inside a CreateList request 86 | type ListFile struct { 87 | ID string `json:"id"` 88 | Description string `json:"description"` 89 | } 90 | 91 | // RequestGetList request to a retrieve a list 92 | type RequestGetList struct { 93 | ID string `json:"id"` 94 | Auth Auth 95 | URL string 96 | } 97 | 98 | // RequestGetUser ... 99 | type RequestGetUser struct { 100 | Auth Auth 101 | URL string 102 | } 103 | 104 | // RequestGetUserFiles ... 105 | type RequestGetUserFiles struct { 106 | Auth Auth 107 | URL string 108 | } 109 | 110 | // RequestGetUserLists ... 111 | type RequestGetUserLists struct { 112 | Auth Auth 113 | URL string 114 | } 115 | -------------------------------------------------------------------------------- /pkg/pd/request_test.go: -------------------------------------------------------------------------------- 1 | package pd_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/ManuelReschke/go-pd/pkg/pd" 9 | ) 10 | 11 | func TestPD_RequestUpload(t *testing.T) { 12 | r := &pd.RequestUpload{ 13 | PathToFile: "/test/path/file.data", 14 | Anonymous: true, 15 | FileName: "test123", 16 | URL: "http://example.url", 17 | Auth: pd.Auth{APIKey: "test-key"}, 18 | } 19 | 20 | assert.Equal(t, "/test/path/file.data", r.PathToFile) 21 | assert.Equal(t, true, r.Anonymous) 22 | assert.Equal(t, "test123", r.FileName) 23 | assert.Equal(t, "http://example.url", r.URL) 24 | assert.Equal(t, "test-key", r.Auth.APIKey) 25 | } 26 | 27 | func TestPD_RequestUpload_GetFileName(t *testing.T) { 28 | ru := &pd.RequestUpload{ 29 | PathToFile: "/test/path/file.data", 30 | } 31 | 32 | assert.Equal(t, "file.data", ru.GetFileName()) 33 | } 34 | 35 | func TestPD_RequestDownload(t *testing.T) { 36 | r := &pd.RequestDownload{ 37 | ID: "123", 38 | URL: "http://example.url", 39 | Auth: pd.Auth{APIKey: "test-key"}, 40 | } 41 | 42 | assert.Equal(t, "123", r.ID) 43 | assert.Equal(t, "http://example.url", r.URL) 44 | assert.Equal(t, "test-key", r.Auth.APIKey) 45 | } 46 | 47 | func TestPD_RequestThumbnail(t *testing.T) { 48 | r := &pd.RequestThumbnail{ 49 | ID: "123", 50 | Width: "16", 51 | Height: "16", 52 | URL: "http://example.url", 53 | Auth: pd.Auth{APIKey: "test-key"}, 54 | } 55 | 56 | assert.Equal(t, "123", r.ID) 57 | assert.Equal(t, "16", r.Width) 58 | assert.Equal(t, "16", r.Height) 59 | assert.Equal(t, "http://example.url", r.URL) 60 | assert.Equal(t, "test-key", r.Auth.APIKey) 61 | } 62 | 63 | func TestPD_RequestDelete(t *testing.T) { 64 | r := &pd.RequestDelete{ 65 | ID: "123", 66 | URL: "http://example.url", 67 | Auth: pd.Auth{APIKey: "test-key"}, 68 | } 69 | 70 | assert.Equal(t, "123", r.ID) 71 | assert.Equal(t, "http://example.url", r.URL) 72 | assert.Equal(t, "test-key", r.Auth.APIKey) 73 | } 74 | 75 | func TestPD_RequestCreateList(t *testing.T) { 76 | r := &pd.RequestCreateList{ 77 | Title: "test", 78 | Anonymous: true, 79 | Files: []pd.ListFile{ 80 | {ID: "123", Description: "Test Description"}, 81 | {ID: "456", Description: "Test Description"}, 82 | }, 83 | URL: "http://example.url", 84 | Auth: pd.Auth{APIKey: "test-key"}, 85 | } 86 | 87 | assert.Equal(t, "test", r.Title) 88 | assert.Equal(t, true, r.Anonymous) 89 | assert.Equal(t, 2, len(r.Files)) 90 | assert.Equal(t, "123", r.Files[0].ID) 91 | assert.Equal(t, "Test Description", r.Files[0].Description) 92 | assert.Equal(t, "http://example.url", r.URL) 93 | assert.Equal(t, "test-key", r.Auth.APIKey) 94 | } 95 | 96 | func TestPD_RequestGetList(t *testing.T) { 97 | r := &pd.RequestGetList{ 98 | ID: "123", 99 | URL: "http://example.url", 100 | Auth: pd.Auth{APIKey: "test-key"}, 101 | } 102 | 103 | assert.Equal(t, "123", r.ID) 104 | assert.Equal(t, "http://example.url", r.URL) 105 | assert.Equal(t, "test-key", r.Auth.APIKey) 106 | } 107 | 108 | func TestPD_RequestGetUserFiles(t *testing.T) { 109 | r := &pd.RequestGetUserFiles{ 110 | URL: "http://example.url", 111 | Auth: pd.Auth{APIKey: "test-key"}, 112 | } 113 | 114 | assert.Equal(t, "http://example.url", r.URL) 115 | assert.Equal(t, "test-key", r.Auth.APIKey) 116 | } 117 | 118 | func TestPD_RequestGetUserLists(t *testing.T) { 119 | r := &pd.RequestGetUserLists{ 120 | URL: "http://example.url", 121 | Auth: pd.Auth{APIKey: "test-key"}, 122 | } 123 | 124 | assert.Equal(t, "http://example.url", r.URL) 125 | assert.Equal(t, "test-key", r.Auth.APIKey) 126 | } 127 | -------------------------------------------------------------------------------- /pkg/pd/response.go: -------------------------------------------------------------------------------- 1 | package pd 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type ResponseDefault struct { 9 | StatusCode int `json:"status_code"` 10 | Success bool `json:"success"` 11 | Value string `json:"value,omitempty"` 12 | Message string `json:"message,omitempty"` 13 | } 14 | 15 | type ResponseUpload struct { 16 | ID string `json:"id,omitempty"` 17 | ResponseDefault 18 | } 19 | 20 | // GetFileURL return the full URl to the uploaded file 21 | func (rsp *ResponseUpload) GetFileURL() string { 22 | return fmt.Sprintf("%su/%s", BaseURL, rsp.ID) 23 | } 24 | 25 | type ResponseDownload struct { 26 | FilePath string `json:"file_path"` 27 | FileName string `json:"file_name"` 28 | FileSize int64 `json:"file_size"` 29 | ResponseDefault 30 | } 31 | 32 | type ResponseFileInfo struct { 33 | ID string `json:"id"` 34 | Name string `json:"name"` 35 | Size int64 `json:"size"` 36 | Views int64 `json:"views"` 37 | BandwidthUsed int64 `json:"bandwidth_used"` 38 | BandwidthUsedPaid int64 `json:"bandwidth_used_paid"` 39 | Downloads int64 `json:"downloads"` 40 | DateUpload time.Time `json:"date_upload"` 41 | DateLastView time.Time `json:"date_last_view"` 42 | MimeType string `json:"mime_type"` 43 | ThumbnailHref string `json:"thumbnail_href"` 44 | HashSha256 string `json:"hash_sha256"` 45 | CanEdit bool `json:"can_edit"` 46 | ResponseDefault 47 | } 48 | 49 | type ResponseThumbnail struct { 50 | FilePath string `json:"file_path"` 51 | FileName string `json:"file_name"` 52 | FileSize int64 `json:"file_size"` 53 | ResponseDefault 54 | } 55 | 56 | type ResponseDelete struct { 57 | ResponseDefault 58 | } 59 | 60 | type ResponseCreateList struct { 61 | ID string `json:"id"` 62 | ResponseDefault 63 | } 64 | 65 | type FileGetList struct { 66 | DetailHref string `json:"detail_href"` 67 | Description string `json:"description"` 68 | Success bool `json:"success"` 69 | ID string `json:"id"` 70 | Name string `json:"name"` 71 | Size int64 `json:"size"` 72 | DateCreated time.Time `json:"date_created"` 73 | DateLastView time.Time `json:"date_last_view"` 74 | MimeType string `json:"mime_type"` 75 | Views int64 `json:"views"` 76 | BandwidthUsed int64 `json:"bandwidth_used"` 77 | ThumbnailHref string `json:"thumbnail_href"` 78 | } 79 | 80 | type ResponseGetList struct { 81 | ID string `json:"id"` 82 | Title string `json:"title"` 83 | DateCreated time.Time `json:"date_created"` 84 | Files []FileGetList `json:"files"` 85 | ResponseDefault 86 | } 87 | 88 | type ResponseGetUser struct { 89 | Username string `json:"username"` 90 | Email string `json:"email"` 91 | Subscription GetUserSubscription `json:"subscription"` 92 | StorageSpaceUsed int64 `json:"storage_space_used"` 93 | IsAdmin bool `json:"is_admin"` 94 | BalanceMicroEur int64 `json:"balance_micro_eur"` 95 | HotlinkingEnabled bool `json:"hotlinking_enabled"` 96 | MonthlyTransferCap int64 `json:"monthly_transfer_cap"` 97 | MonthlyTransferUsed int64 `json:"monthly_transfer_used"` 98 | FileViewerBranding interface{} `json:"file_viewer_branding"` 99 | FileEmbedDomains string `json:"file_embed_domains"` 100 | SkipFileViewer bool `json:"skip_file_viewer"` 101 | ResponseDefault 102 | } 103 | 104 | type GetUserSubscription struct { 105 | ID string `json:"id"` 106 | Name string `json:"name"` 107 | Type string `json:"type"` 108 | FileSizeLimit int64 `json:"file_size_limit"` 109 | FileExpiryDays int64 `json:"file_expiry_days"` 110 | StorageSpace int64 `json:"storage_space"` 111 | PricePerTbStorage int64 `json:"price_per_tb_storage"` 112 | PricePerTbBandwidth int64 `json:"price_per_tb_bandwidth"` 113 | MonthlyTransferCap int64 `json:"monthly_transfer_cap"` 114 | FileViewerBranding bool `json:"file_viewer_branding"` 115 | } 116 | 117 | type FileGetUser struct { 118 | ID string `json:"id"` 119 | Name string `json:"name"` 120 | Size int64 `json:"size"` 121 | Views int64 `json:"views"` 122 | BandwidthUsed int64 `json:"bandwidth_used"` 123 | BandwidthUsedPaid int64 `json:"bandwidth_used_paid"` 124 | Downloads int64 `json:"downloads"` 125 | DateUpload time.Time `json:"date_upload"` 126 | DateLastView time.Time `json:"date_last_view"` 127 | MimeType string `json:"mime_type"` 128 | ThumbnailHref string `json:"thumbnail_href"` 129 | HashSha256 string `json:"hash_sha256"` 130 | Availability string `json:"availability"` 131 | AvailabilityMessage string `json:"availability_message"` 132 | AbuseType string `json:"abuse_type"` 133 | AbuseReporterName string `json:"abuse_reporter_name"` 134 | CanEdit bool `json:"can_edit"` 135 | ShowAds bool `json:"show_ads"` 136 | AllowVideoPlayer bool `json:"allow_video_player"` 137 | DownloadSpeedLimit int64 `json:"download_speed_limit"` 138 | } 139 | 140 | type ResponseGetUserFiles struct { 141 | Files []FileGetUser `json:"files"` 142 | ResponseDefault 143 | } 144 | 145 | type ListsGetUser struct { 146 | ID string `json:"id"` 147 | Title string `json:"title"` 148 | DateCreated time.Time `json:"date_created"` 149 | FileCount int64 `json:"file_count"` 150 | Files interface{} `json:"files"` 151 | CanEdit bool `json:"can_edit"` 152 | } 153 | 154 | type ResponseGetUserLists struct { 155 | Lists []ListsGetUser `json:"lists"` 156 | ResponseDefault 157 | } 158 | -------------------------------------------------------------------------------- /pkg/pd/response_test.go: -------------------------------------------------------------------------------- 1 | package pd_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/ManuelReschke/go-pd/pkg/pd" 10 | ) 11 | 12 | func TestPD_ResponseDefault(t *testing.T) { 13 | rsp := &pd.ResponseDefault{} 14 | rsp.Success = true 15 | rsp.Value = "test" 16 | rsp.Message = "test message" 17 | 18 | assert.Equal(t, true, rsp.Success) 19 | assert.Equal(t, "test", rsp.Value) 20 | assert.Equal(t, "test message", rsp.Message) 21 | } 22 | 23 | func TestPD_ResponseUpload(t *testing.T) { 24 | rsp := &pd.ResponseUpload{} 25 | rsp.StatusCode = 201 26 | rsp.ID = "test123" 27 | rsp.Success = true 28 | rsp.Value = "test" 29 | rsp.Message = "test message" 30 | 31 | assert.Equal(t, 201, rsp.StatusCode) 32 | assert.Equal(t, "test123", rsp.ID) 33 | assert.Equal(t, true, rsp.Success) 34 | assert.Equal(t, "test", rsp.Value) 35 | assert.Equal(t, "test message", rsp.Message) 36 | } 37 | 38 | func TestPD_ResponseDownload(t *testing.T) { 39 | rsp := &pd.ResponseDownload{} 40 | rsp.StatusCode = 200 41 | rsp.Success = true 42 | rsp.Value = "test" 43 | rsp.Message = "test message" 44 | rsp.FileName = "filename" 45 | rsp.FileSize = 123123 46 | rsp.FilePath = "/my/path/file.jpg" 47 | 48 | assert.Equal(t, 200, rsp.StatusCode) 49 | assert.Equal(t, true, rsp.Success) 50 | assert.Equal(t, "test", rsp.Value) 51 | assert.Equal(t, "test message", rsp.Message) 52 | assert.Equal(t, "filename", rsp.FileName) 53 | assert.Equal(t, int64(123123), rsp.FileSize) 54 | assert.Equal(t, "/my/path/file.jpg", rsp.FilePath) 55 | } 56 | 57 | func TestPD_ResponseThumbnail(t *testing.T) { 58 | rsp := &pd.ResponseThumbnail{} 59 | rsp.StatusCode = 200 60 | rsp.Success = true 61 | rsp.Value = "test" 62 | rsp.Message = "test message" 63 | rsp.FileName = "filename" 64 | rsp.FileSize = 123123 65 | rsp.FilePath = "/my/path/thumbnail.jpg" 66 | 67 | assert.Equal(t, 200, rsp.StatusCode) 68 | assert.Equal(t, true, rsp.Success) 69 | assert.Equal(t, "test", rsp.Value) 70 | assert.Equal(t, "test message", rsp.Message) 71 | assert.Equal(t, "filename", rsp.FileName) 72 | assert.Equal(t, int64(123123), rsp.FileSize) 73 | assert.Equal(t, "/my/path/thumbnail.jpg", rsp.FilePath) 74 | } 75 | 76 | func TestPD_ResponseDelete(t *testing.T) { 77 | rsp := &pd.ResponseDelete{} 78 | rsp.StatusCode = 200 79 | rsp.Success = true 80 | rsp.Value = "test" 81 | rsp.Message = "test message" 82 | 83 | assert.Equal(t, 200, rsp.StatusCode) 84 | assert.Equal(t, true, rsp.Success) 85 | assert.Equal(t, "test", rsp.Value) 86 | assert.Equal(t, "test message", rsp.Message) 87 | } 88 | 89 | func TestPD_ResponseCreateList(t *testing.T) { 90 | rsp := &pd.ResponseCreateList{} 91 | rsp.StatusCode = 200 92 | rsp.Success = true 93 | rsp.Value = "test" 94 | rsp.Message = "test message" 95 | rsp.ID = "123" 96 | 97 | assert.Equal(t, 200, rsp.StatusCode) 98 | assert.Equal(t, true, rsp.Success) 99 | assert.Equal(t, "test", rsp.Value) 100 | assert.Equal(t, "test message", rsp.Message) 101 | assert.Equal(t, "123", rsp.ID) 102 | } 103 | 104 | func TestPD_ResponseGetList(t *testing.T) { 105 | rsp := &pd.ResponseGetList{} 106 | rsp.StatusCode = 200 107 | rsp.Success = true 108 | rsp.Value = "test" 109 | rsp.Message = "test message" 110 | rsp.ID = "123" 111 | rsp.Title = "Test Title" 112 | layout := "2014-09-12T11:45:26.371Z" 113 | timeStr := "2020-02-04T18:34:13.466276Z" 114 | rsp.DateCreated, _ = time.Parse(layout, timeStr) 115 | //@todo 116 | rsp.Files = []pd.FileGetList{{ 117 | DetailHref: "", 118 | Description: "", 119 | Success: false, 120 | ID: "", 121 | Name: "", 122 | Size: 0, 123 | DateCreated: time.Time{}, 124 | DateLastView: time.Time{}, 125 | MimeType: "", 126 | Views: 0, 127 | BandwidthUsed: 0, 128 | ThumbnailHref: "", 129 | }} 130 | 131 | assert.Equal(t, 200, rsp.StatusCode) 132 | assert.Equal(t, true, rsp.Success) 133 | assert.Equal(t, "test", rsp.Value) 134 | assert.Equal(t, "test message", rsp.Message) 135 | assert.Equal(t, "123", rsp.ID) 136 | } 137 | 138 | func TestPD_ResponseGetUserFiles(t *testing.T) { 139 | rsp := &pd.ResponseGetUserFiles{} 140 | rsp.StatusCode = 200 141 | rsp.Success = true 142 | rsp.Value = "test" 143 | rsp.Message = "test message" 144 | 145 | assert.Equal(t, 200, rsp.StatusCode) 146 | assert.Equal(t, true, rsp.Success) 147 | assert.Equal(t, "test", rsp.Value) 148 | assert.Equal(t, "test message", rsp.Message) 149 | } 150 | 151 | func TestPD_ResponseGetUserLists(t *testing.T) { 152 | rsp := &pd.ResponseGetUserLists{} 153 | rsp.StatusCode = 200 154 | rsp.Success = true 155 | rsp.Value = "test" 156 | rsp.Message = "test message" 157 | 158 | assert.Equal(t, 200, rsp.StatusCode) 159 | assert.Equal(t, true, rsp.Success) 160 | assert.Equal(t, "test", rsp.Value) 161 | assert.Equal(t, "test message", rsp.Message) 162 | } 163 | -------------------------------------------------------------------------------- /pkg/pd/testdata/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/pkg/pd/testdata/cat.jpg -------------------------------------------------------------------------------- /pkg/pd/testdata/cat_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ManuelReschke/go-pd/471b70b89daca049586be995aebf24ab5fc94819/pkg/pd/testdata/cat_thumbnail.jpg --------------------------------------------------------------------------------