├── .dockerignore
├── .github
├── dependabot.yml
└── workflows
│ └── docker.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── docker-bake.hcl
├── docs
├── API.md
├── build.md
├── config.md
├── database.md
├── docker.md
├── screenshots.md
└── usage.md
├── exatorrent.go
├── go.mod
├── go.sum
└── internal
├── core
├── auth.go
├── cache.go
├── connection.go
├── engine.go
├── get.go
├── getspec.go
├── init.go
├── routine.go
├── serve.go
├── socket.go
├── statsapi.go
├── storage.go
├── vars.go
└── version.go
├── db
├── db.go
├── sqlite3filestatedb.go
├── sqlite3lockstatedb.go
├── sqlite3pc.go
├── sqlite3torrentdb.go
├── sqlite3torrentuserdb.go
├── sqlite3trackerdb.go
└── sqlite3userdb.go
└── web
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── index.html
├── package-lock.json
├── package.json
├── src
├── App.svelte
├── app.css
├── main.ts
├── partials
│ ├── About.svelte
│ ├── Disconnect.svelte
│ ├── File.svelte
│ ├── Index.svelte
│ ├── Notifications.svelte
│ ├── ProgStat.svelte
│ ├── Settings.svelte
│ ├── Signin.svelte
│ ├── Stats.svelte
│ ├── Top.svelte
│ ├── Torrent.svelte
│ ├── TorrentCard.svelte
│ ├── Torrents.svelte
│ ├── User.svelte
│ ├── Useredit.svelte
│ ├── Users.svelte
│ └── core.ts
└── vite-env.d.ts
├── svelte.config.js
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
├── web.go
└── web_noui.go
/.dockerignore:
--------------------------------------------------------------------------------
1 | .github
2 | docs/
3 | build/
4 | internal/web/build/
5 | internal/web/node_modules/
6 | Dockerfile
7 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "gomod"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 |
8 | - package-ecosystem: "npm"
9 | directory: "/internal/web"
10 | schedule:
11 | interval: "weekly"
12 |
13 | - package-ecosystem: "github-actions"
14 | directory: "/"
15 | schedule:
16 | interval: "weekly"
17 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: Create and publish Container images
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - opened
7 | - reopened
8 | - synchronize
9 | push:
10 | branches:
11 | - "main"
12 |
13 | jobs:
14 | build-container-images:
15 | runs-on: ubuntu-latest
16 | permissions:
17 | contents: read
18 | packages: write
19 |
20 | steps:
21 | - name: Checkout repository
22 | uses: actions/checkout@v4
23 |
24 | - name: Docker meta and tags
25 | id: meta
26 | uses: docker/metadata-action@v5
27 | with:
28 | # list of Docker images to use as base name for tags
29 | images: |
30 | ghcr.io/${{ github.repository_owner }}/exatorrent
31 | # generate Docker tags based on the following events/attributes
32 | tags: |
33 | type=raw,value=amd64
34 | type=raw,value=arm64
35 | type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
36 | type=schedule
37 | type=ref,event=branch
38 | type=ref,event=pr
39 | type=semver,pattern={{version}}
40 | type=semver,pattern={{major}}.{{minor}}
41 | type=semver,pattern={{major}}
42 | type=sha
43 |
44 | - name: Setup Docker Buildx
45 | uses: docker/setup-buildx-action@v3
46 |
47 | - name: Login to GHCR
48 | uses: docker/login-action@v3
49 | with:
50 | registry: ghcr.io
51 | username: ${{ github.repository_owner }}
52 | password: ${{ secrets.GITHUB_TOKEN }}
53 |
54 | - name: Create artifact directory
55 | run: mkdir -p artifact
56 |
57 | - name: Build and push
58 | uses: docker/bake-action@v6
59 | with:
60 | files: |
61 | ./docker-bake.hcl
62 | ${{ steps.meta.outputs.bake-file }}
63 | source: .
64 | push: true
65 | set: |
66 | *.cache-from=type=gha
67 | *.cache-to=type=gha,mode=max
68 |
69 | build-binaries-darwin:
70 | runs-on: macos-13
71 | permissions:
72 | contents: write
73 | steps:
74 | - name: Checkout repository
75 | uses: actions/checkout@v4
76 |
77 | - name: Set up NodeJS
78 | uses: actions/setup-node@v4
79 | with:
80 | node-version: 18
81 | cache: npm
82 | cache-dependency-path: internal/web/package-lock.json
83 |
84 | - name: Install node dependencies
85 | run: npm ci
86 | working-directory: internal/web
87 | - name: Build frontend
88 | run: make web
89 |
90 | - name: Set up Go
91 | uses: actions/setup-go@v5
92 | with:
93 | go-version: 1.23
94 | cache: true
95 |
96 | - name: Build amd64
97 | run: |
98 | make app-no-sl
99 | mv build/exatorrent build/exatorrent-darwin-amd64
100 | env:
101 | GOOS: darwin
102 | GOARCH: amd64
103 |
104 | - name: Build arm64
105 | run: |
106 | make app-no-sl
107 | mv build/exatorrent build/exatorrent-darwin-arm64
108 | env:
109 | GOOS: darwin
110 | GOARCH: arm64
111 |
112 | - name: Upload artifacts
113 | uses: actions/upload-artifact@v4
114 | with:
115 | name: binaries-darwin
116 | path: build/*
117 | if-no-files-found: error
118 |
119 | build-binaries-linux:
120 | runs-on: ubuntu-22.04
121 | permissions:
122 | contents: write
123 | steps:
124 | - name: Checkout repository
125 | uses: actions/checkout@v4
126 |
127 | - name: Set up Docker Buildx
128 | uses: docker/setup-buildx-action@v3
129 |
130 | - name: Create artifacts
131 | uses: docker/bake-action@v6
132 | with:
133 | files: |
134 | ./docker-bake.hcl
135 | targets: artifact-linux
136 | source: .
137 | provenance: false
138 |
139 | - name: Flatten artifact
140 | uses: docker/bake-action@v6
141 | with:
142 | files: |
143 | ./docker-bake.hcl
144 | targets: release
145 | source: .
146 | provenance: false
147 |
148 | - name: Upload artifacts
149 | uses: actions/upload-artifact@v4
150 | with:
151 | name: binaries-linux
152 | path: release/*
153 | if-no-files-found: error
154 |
155 | gh-release:
156 | needs:
157 | - build-binaries-linux
158 | - build-binaries-darwin
159 | runs-on: ubuntu-latest
160 | if: startsWith(github.ref, 'refs/tags/v')
161 | permissions:
162 | contents: write
163 | steps:
164 | - uses: actions/download-artifact@v3
165 | id: binaries
166 |
167 | - name: Display structure of downloaded files
168 | run: ls -R ${{steps.binaries.outputs.download-path}}
169 |
170 | - name: GitHub Release
171 | uses: softprops/action-gh-release@v2
172 | with:
173 | generate_release_notes: true
174 | fail_on_unmatched_files: true
175 | files: |
176 | ${{steps.binaries.outputs.download-path}}/binaries-linux/*
177 | ${{steps.binaries.outputs.download-path}}/binaries-darwin/*
178 | env:
179 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
180 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 |
3 | FROM docker.io/alpine:3.18 AS base
4 |
5 | # Build the web ui from source
6 | FROM --platform=$BUILDPLATFORM docker.io/node:18 AS build-node
7 | WORKDIR /exa
8 | ADD internal/web /exa/internal/web
9 | ADD Makefile /exa/
10 | RUN make web
11 |
12 | # Build the application from source
13 | FROM docker.io/golang:1.24-bookworm AS build-go
14 |
15 | ARG TARGETOS TARGETARCH
16 |
17 | WORKDIR /exa
18 |
19 | COPY go.mod go.sum ./
20 | RUN go mod download
21 |
22 | COPY . ./
23 | COPY --link --from=build-node /exa/internal/web/build /exa/internal/web/build
24 | RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} make app
25 |
26 | # Artifact Target
27 | FROM scratch AS artifact
28 |
29 | ARG TARGETOS TARGETARCH TARGETVARIANT
30 |
31 | COPY --link --from=build-go /exa/build/exatorrent /exatorrent-${TARGETOS}-${TARGETARCH}${TARGETVARIANT}
32 |
33 | # Failover if contexts=artifacts= Screenshots • Releases • Features • Installation • Usage • Docker • Build • License
30 |
17 |
18 | * exatorrent is Elegant [BitTorrent](https://www.bittorrent.org/) Client written in [Go](https://go.dev/).
19 | * It is Simple, easy to use, yet feature rich.
20 | * It can be run locally or be hosted in Remote Server with good resources.
21 | * It is Single Completely Statically Linked Binary with Zero External Dependencies.
22 | * It is lightweight and light on resources.
23 | * It comes with Beautiful Responsive Web Client written in Svelte and Typescript.
24 | * Thanks to documented [WebSocket](https://datatracker.ietf.org/doc/html/rfc6455) [API](docs/API.md) of exatorrent, custom client can be created.
25 | * It supports Single User Mode and Multi User Mode.
26 | * Torrented Files are stored in local disk can be downloaded and streamed via HTTP/Browser/Media Players.
27 |
28 |
29 |
31 |
2 |
3 |
5 |
6 |
8 |
9 |
11 |
12 |
14 |
15 |
17 |
18 |
20 |
21 |
23 |
24 |
26 |
27 |
29 |
30 |
32 |
33 |
35 |
36 |
38 |
39 |
41 |
42 |
44 |
45 |
47 |
48 |
50 |
51 |
53 |
54 |
Not Found
57 |exatorrent is torrent client
16 |License: GPLv3
17 |Version: {$versionstr}
18 |Disconnected
25 | 26 |{$fsfileinfo?.name}
65 |({fileSize($fsfileinfo?.size)})
66 | 67 |No Notifications
38 | {/if} 39 |102 | User Settings 103 | {#if localStorage.getItem('exausertype') === 'admin'} admin{/if} 105 |
106 |Disk Usage
191 |Misc Settings
246 |Engine Settings
360 |Sign in to your account
64 |Device Info
49 |Device Stats
87 |Torrent Client Status
135 |{$torcstatus}166 | {/if} 167 |
{$terrormsg?.msg}
48 | {/if} 49 |User owns no Torrents
35 | {/if} 36 |Created at {timestring}
91 |User Connections
86 |125 | {eachuser?.username} 126 | {#if eachuser?.isadmin === true}(admin){/if} 127 |
128 |146 | Connected at {new Date(eachuser?.contime)?.toLocaleString()} 147 |
148 |Manage Users
160 |Add User
206 |