Upload 13 files
Browse files- .gitattributes +1 -0
- Dockerfile +34 -0
- client/assets/bitplay_logo.png +3 -0
- client/assets/butterup.min.css +1 -0
- client/assets/butterup.min.js +2 -0
- client/assets/favicon.png +0 -0
- client/assets/index.css +487 -0
- client/assets/index.js +1080 -0
- client/assets/output.css +1529 -0
- client/assets/videojs.hotkeys.min.js +2 -0
- client/index.html +545 -0
- go.mod +89 -0
- go.sum +499 -0
- main.go +1434 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
client/assets/bitplay_logo.png filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Build stage
|
| 2 |
+
FROM golang:1.24-alpine AS builder
|
| 3 |
+
|
| 4 |
+
# Set working directory for the build
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Copy module files first to leverage Docker cache
|
| 8 |
+
COPY go.mod go.sum ./
|
| 9 |
+
RUN go mod download
|
| 10 |
+
|
| 11 |
+
# Copy all source files including client directory
|
| 12 |
+
COPY . .
|
| 13 |
+
|
| 14 |
+
# Build the Go app with static linking
|
| 15 |
+
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o main .
|
| 16 |
+
|
| 17 |
+
# Final stage
|
| 18 |
+
FROM alpine:3.18
|
| 19 |
+
RUN apk --no-cache add ca-certificates
|
| 20 |
+
|
| 21 |
+
# Set working directory in final image
|
| 22 |
+
WORKDIR /app
|
| 23 |
+
|
| 24 |
+
# Copy the compiled binary from builder
|
| 25 |
+
COPY --from=builder /app/main .
|
| 26 |
+
|
| 27 |
+
# Copy client directory from builder
|
| 28 |
+
COPY --from=builder /app/client ./client/
|
| 29 |
+
|
| 30 |
+
# Expose the port your app runs on
|
| 31 |
+
EXPOSE 3347
|
| 32 |
+
|
| 33 |
+
# Command to run the application
|
| 34 |
+
CMD ["/app/main"]
|
client/assets/bitplay_logo.png
ADDED
|
Git LFS Details
|
client/assets/butterup.min.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.toaster,ol.rack{list-style:none;margin:0}.toaster{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;box-sizing:border-box;padding:5px;outline:0;z-index:999999999;position:fixed}.butteruptoast,.butteruptoast.brutalist{font-size:13px;display:flex;padding:16px;width:325px}.toaster.bottom-right{bottom:20px;right:20px}.toaster.bottom-left{bottom:20px;left:20px}.toaster.top-right{top:20px;right:20px}.toaster.top-left{top:20px;left:20px}.toaster.bottom-center{bottom:20px;left:50%;transform:translateX(-50%)}.toaster.top-center{top:20px;left:50%;transform:translateX(-50%)}.toaster.top-center ol.rack,.toaster.top-left ol.rack,.toaster.top-right ol.rack{flex-direction:column-reverse}.toaster.bottom-center ol.rack,.toaster.bottom-left ol.rack,.toaster.bottom-right ol.rack{flex-direction:column}ol.rack{padding:0;display:flex}ol.rack li{margin-bottom:16px}ol.rack.upperstack li{margin-bottom:-35px;transition:.3s ease-in-out}ol.rack.upperstack li:hover{margin-bottom:16px;scale:1.03;transition:.3s ease-in-out}ol.rack.lowerstack li{margin-top:-35px}ol.rack.lowerstack{margin-bottom:0}.butteruptoast{border-radius:8px;box-shadow:0 4px 12px #0000001a;border:1px solid #ededed;background-color:#fff;gap:6px;color:#282828}.butteruptoast.dismissable{cursor:pointer}.butteruptoast .icon{display:flex;align-items:start;flex-direction:column}.butteruptoast .icon svg{width:20px;height:20px;fill:#282828}.notif .desc{display:flex;flex-direction:column;gap:2px}.notif .desc .title{font-weight:600;line-height:1.5}.notif .desc .message{font-weight:400;line-height:1.4}.butteruptoast.success{background-color:#ebfef2;color:#00892d;border:1px solid #d2fde4}.butteruptoast.success .icon svg{fill:hsl(140,100%,27%)}.butteruptoast.error .icon svg{fill:hsl(0,100%,27%)}.butteruptoast.warning .icon svg{fill:hsl(50,100%,27%)}.butteruptoast.info .icon svg{fill:hsl(210,100%,27%)}.butteruptoast.error{background-color:#fef0f0;color:#890000;border:1px solid #fdd2d2}.butteruptoast.warning{background-color:#fffdf0;color:#897200;border:1px solid #fdf6d2}.butteruptoast.info{background-color:#f0f8ff;color:#004489;border:1px solid #d2e8fd}.toast-buttons{display:flex;gap:8px;width:100%;align-items:center;flex-direction:row;margin-top:16px}.toast-buttons .toast-button.primary{background-color:#282828;color:#fff;padding:8px 16px;border-radius:4px;cursor:pointer;border:none;width:100%}.toast-buttons .toast-button.secondary{background-color:#f0f8ff;color:#004489;border:1px solid #d2e8fd;padding:8px 16px;border-radius:4px;cursor:pointer;width:100%}.butteruptoast.success .toast-button.primary{background-color:#27ae5f;color:#fff}.butteruptoast.success .toast-button.secondary{background-color:#daf0e3;color:#1e8549;border:1px solid #8ae4b0}.butteruptoast.error .toast-button.primary{background-color:#db3748;color:#fff}.butteruptoast.error .toast-button.secondary{background-color:#eddddf;color:#be2131;border:1px solid #eb8e97}.butteruptoast.warning .toast-button.primary{background-color:#ffc005;color:#4c3900}.butteruptoast.warning .toast-button.secondary{background-color:#fff9ea;color:#9e7600;border:1px solid #ffe084}.butteruptoast.info .toast-button.primary{background-color:#2094f3;color:#fff}.butteruptoast.info .toast-button.secondary{background-color:#e1f1fd;color:#085ea4;border:1px solid #81c2f8}.toastUp{animation:.5s ease-in-out forwards slideUp}.toastDown{animation:.5s ease-in-out forwards slideDown}@keyframes slideDown{0%{opacity:0;transform:translateY(-100%)}100%{opacity:1;transform:translateY(0)}}@keyframes slideUp{0%{opacity:0;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}.fadeOutToast{animation:.3s ease-in-out forwards fadeOut}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.butteruptoast.glass{background-color:rgba(255,255,255,.42)!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:none;box-shadow:0 4px 12px #0000001a;color:#282828}.butteruptoast.glass.success{background-color:rgba(235,254,242,.42)!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:none;box-shadow:0 4px 12px #0000001a;color:#00892d}.butteruptoast.glass.error,.butteruptoast.glass.warning{backdrop-filter:blur(10px);border:none;box-shadow:0 4px 12px #0000001a}.butteruptoast.glass.error{background-color:rgba(254,240,240,.42)!important;-webkit-backdrop-filter:blur(10px);color:#890000}.butteruptoast.glass.warning{background-color:rgba(255,253,240,.42)!important;-webkit-backdrop-filter:blur(10px);color:#897200}.butteruptoast.glass.info{background-color:rgba(240,248,255,.42)!important;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:none;box-shadow:0 4px 12px #0000001a;color:#004489}.butteruptoast.brutalist{border-radius:0;box-shadow:0 4px 12px #0000001a;border:2px solid #282828;align-items:center;background-color:#fff;gap:6px;color:#282828}.butteruptoast.brutalist.success{background-color:#ebfef2;color:#00892d;border:2px solid #00892d}.butteruptoast.brutalist.error{background-color:#fef0f0;color:#890000;border:2px solid #890000}.butteruptoast.brutalist.warning{background-color:#fffdf0;color:#897200;border:2px solid #897200}.butteruptoast.brutalist.info{background-color:#f0f8ff;color:#004489;border:2px solid #004489}
|
client/assets/butterup.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* butterup version 2.0.0 by Nathan Langer * https://github.com/dgtlss/butterup | https://butterup.nlanger.dev * Thankyou for using butterup! Please consider starring the project on GitHub. */
|
| 2 |
+
var butterup={options:{maxToasts:5,toastLife:5e3,currentToasts:0},toast:function({title:t,message:e,type:n,location:s,icon:o,theme:a,customIcon:l,dismissable:c,onClick:i,onRender:u,onTimeout:d,customHTML:r,primaryButton:m,secondaryButton:p}){if(null==document.getElementById("toaster")){const t=document.createElement("div");if(t.id="toaster",t.className=null==s?"toaster top-right":"toaster "+s,document.body.appendChild(t),null==document.getElementById("butterupRack")){const e=document.createElement("ol");e.id="butterupRack",e.className="rack",t.appendChild(e)}}else{const t=document.getElementById("toaster");t.classList.forEach((function(e){(e.includes("top-right")||e.includes("top-center")||e.includes("top-left")||e.includes("bottom-right")||e.includes("bottom-center")||e.includes("bottom-left"))&&t.classList.remove(e)})),t.className=null==s?"toaster top-right":"toaster "+s,document.getElementById("butterupRack")}if(butterup.options.currentToasts>=butterup.options.maxToasts){var v=document.getElementById("butterupRack").firstChild;document.getElementById("butterupRack").removeChild(v),butterup.options.currentToasts--}const f=document.createElement("li");if(butterup.options.currentToasts++,f.className="butteruptoast",(toaster.className.includes("top-right")||toaster.className.includes("top-center")||toaster.className.includes("top-left"))&&(f.className+=" toastDown"),(toaster.className.includes("bottom-right")||toaster.className.includes("bottom-center")||toaster.className.includes("bottom-left"))&&(f.className+=" toastUp"),f.id="butterupToast-"+butterup.options.currentToasts,null!=n&&(f.className+=" "+n),null!=a&&(f.className+=" "+a),document.getElementById("butterupRack").appendChild(f),null!=o&&1==o){const t=document.createElement("div");t.className="icon",f.appendChild(t),l&&(t.innerHTML=l),null!=n&&null==l&&(f.className+=" "+n,"success"==n&&(t.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" /></svg>'),"error"==n&&(t.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" /></svg>'),"warning"==n&&(t.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" /></svg>'),"info"==n&&(t.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" /></svg>'))}const g=document.createElement("div");g.className="notif",f.appendChild(g);const h=document.createElement("div");if(h.className="desc",g.appendChild(h),null!=t){const e=document.createElement("div");e.className="title",e.innerHTML=t,h.appendChild(e)}if(null!=r){const t=document.createElement("div");t.className="message",t.innerHTML=r,h.appendChild(t)}if(null!=e){const t=document.createElement("div");t.className="message",t.innerHTML=e,h.appendChild(t)}if(m||p){const t=document.createElement("div");if(t.className="toast-buttons",g.appendChild(t),m){const e=document.createElement("button");e.className="toast-button primary",e.textContent=m.text,e.onclick=function(t){t.stopPropagation(),m.onClick(t)},t.appendChild(e)}if(p){const e=document.createElement("button");e.className="toast-button secondary",e.textContent=p.text,e.onclick=function(t){t.stopPropagation(),p.onClick(t)},t.appendChild(e)}}i&&"function"==typeof i&&f.addEventListener("click",(function(t){t.stopPropagation(),i(t)})),u&&"function"==typeof u&&u(f),null!=c&&1==c&&(f.className+=" dismissable",f.addEventListener("click",(function(){butterup.despawnToast(f.id)}))),setTimeout((function(){f.className=f.className.replace(" toastDown",""),f.className=f.className.replace(" toastUp","")}),500),setTimeout((function(){d&&"function"==typeof d&&d(f),butterup.despawnToast(f.id)}),butterup.options.toastLife)},despawnToast(t,e){var n=document.getElementById(t);null!=n&&(n.className+=" fadeOutToast",setTimeout((function(){try{n.style.opacity="0",n.parentNode.removeChild(n),butterup.options.currentToasts--,e&&"function"==typeof e&&e(n)}catch(t){}if(0==butterup.options.currentToasts){var t=document.getElementById("toaster");t.parentNode.removeChild(t)}}),500))}};
|
client/assets/favicon.png
ADDED
|
|
client/assets/index.css
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import "tailwindcss";
|
| 2 |
+
|
| 3 |
+
@custom-variant dark (&:is(.dark *));
|
| 4 |
+
|
| 5 |
+
@theme {
|
| 6 |
+
--color-background: var(--background);
|
| 7 |
+
--color-foreground: var(--foreground);
|
| 8 |
+
--color-ring: var(--ring);
|
| 9 |
+
--color-input: var(--input);
|
| 10 |
+
--color-border: var(--border);
|
| 11 |
+
--color-destructive: var(--destructive);
|
| 12 |
+
--color-accent-foreground: var(--accent-foreground);
|
| 13 |
+
--color-accent: var(--accent);
|
| 14 |
+
--color-muted-foreground: var(--muted-foreground);
|
| 15 |
+
--color-muted: var(--muted);
|
| 16 |
+
--color-secondary-foreground: var(--secondary-foreground);
|
| 17 |
+
--color-secondary: var(--secondary);
|
| 18 |
+
--color-primary-foreground: var(--primary-foreground);
|
| 19 |
+
--color-primary: var(--primary);
|
| 20 |
+
--color-popover-foreground: var(--popover-foreground);
|
| 21 |
+
--color-popover: var(--popover);
|
| 22 |
+
--color-card-foreground: var(--card-foreground);
|
| 23 |
+
--color-card: var(--card);
|
| 24 |
+
--radius-sm: calc(var(--radius) - 4px);
|
| 25 |
+
--radius-md: calc(var(--radius) - 2px);
|
| 26 |
+
--radius-lg: var(--radius);
|
| 27 |
+
--radius-xl: calc(var(--radius) + 4px);
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
:root {
|
| 31 |
+
--radius: 0.625rem;
|
| 32 |
+
--background: oklch(1 0 0);
|
| 33 |
+
--foreground: oklch(0.141 0.005 285.823);
|
| 34 |
+
--card: oklch(1 0 0);
|
| 35 |
+
--card-foreground: oklch(0.141 0.005 285.823);
|
| 36 |
+
--popover: oklch(1 0 0);
|
| 37 |
+
--popover-foreground: oklch(0.141 0.005 285.823);
|
| 38 |
+
--primary: oklch(65.24% 0.199188 160.1355);
|
| 39 |
+
--primary-foreground: oklch(0.985 0 0);
|
| 40 |
+
--secondary: oklch(0.967 0.001 286.375);
|
| 41 |
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
| 42 |
+
--muted: oklch(0.967 0.001 286.375);
|
| 43 |
+
--muted-foreground: oklch(0.552 0.016 285.938);
|
| 44 |
+
--accent: oklch(0.967 0.001 286.375);
|
| 45 |
+
--accent-foreground: oklch(0.21 0.006 285.885);
|
| 46 |
+
--destructive: oklch(0.577 0.245 27.325);
|
| 47 |
+
--border: oklch(0.92 0.004 286.32);
|
| 48 |
+
--input: oklch(0.92 0.004 286.32);
|
| 49 |
+
--ring: oklch(0.705 0.015 286.067);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.dark {
|
| 53 |
+
--background: oklch(0.191 0.005 285.823);
|
| 54 |
+
--foreground: oklch(0.985 0 0);
|
| 55 |
+
--card: oklch(0.21 0.006 285.885);
|
| 56 |
+
--card-foreground: oklch(0.985 0 0);
|
| 57 |
+
--popover: oklch(0.21 0.006 285.885);
|
| 58 |
+
--popover-foreground: oklch(0.985 0 0);
|
| 59 |
+
--primary: oklch(88.24% 0.199188 160.1355);
|
| 60 |
+
--primary-foreground: oklch(0.21 0.006 285.885);
|
| 61 |
+
--secondary: oklch(0.244 0.006 286.033);
|
| 62 |
+
--secondary-foreground: oklch(0.985 0 0);
|
| 63 |
+
--muted: oklch(0.274 0.006 286.033);
|
| 64 |
+
--muted-foreground: oklch(0.705 0.015 286.067);
|
| 65 |
+
--accent: oklch(0.274 0.006 286.033);
|
| 66 |
+
--accent-foreground: oklch(0.985 0 0);
|
| 67 |
+
--destructive: oklch(0.704 0.191 22.216);
|
| 68 |
+
--border: oklch(1 0 0 / 10%);
|
| 69 |
+
--input: oklch(1 0 0 / 15%);
|
| 70 |
+
--ring: oklch(0.552 0.016 285.938);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
@layer base {
|
| 74 |
+
* {
|
| 75 |
+
@apply border-border outline-ring/50;
|
| 76 |
+
}
|
| 77 |
+
body {
|
| 78 |
+
@apply bg-background text-foreground;
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
.btn {
|
| 83 |
+
@apply flex gap-1.5 justify-center items-center min-w-[100px] bg-primary text-primary-foreground h-12 rounded-md px-2 py-2 font-semibold text-sm transition-all hover:bg-primary/90;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
.btn.small{
|
| 87 |
+
@apply h-8 text-xs min-w-[70px];
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
svg {
|
| 91 |
+
display: inline;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
#torrent_file_wrapper.drag-over {
|
| 95 |
+
@apply border-primary;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
#search-pagination {
|
| 99 |
+
@apply flex items-center justify-center gap-2 my-4;
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
#search-pagination.hidden {
|
| 103 |
+
display: none !important;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
#search-pagination button {
|
| 107 |
+
@apply min-w-[32px] h-[32px] border border-foreground/20 rounded-md p-1 flex items-center justify-center text-xs font-semibold text-sm transition-all hover:bg-accent;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
#search-pagination button svg {
|
| 111 |
+
font-size: 17px;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
#search-pagination button.disabled {
|
| 115 |
+
@apply cursor-not-allowed hover:bg-transparent;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
#search-pagination button.active {
|
| 119 |
+
@apply bg-primary text-primary-foreground;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
#search-pagination button.active:hover {
|
| 123 |
+
@apply bg-primary text-primary-foreground;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
#video-player {
|
| 127 |
+
@apply hidden;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
.btn.loader:before {
|
| 131 |
+
content: "";
|
| 132 |
+
display: block;
|
| 133 |
+
width: 20px;
|
| 134 |
+
height: 20px;
|
| 135 |
+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBMaWNlbnNlOiBNSVQuIE1hZGUgYnkgTHVjaWRlIENvbnRyaWJ1dG9yczogaHR0cHM6Ly9sdWNpZGUuZGV2LyAtLT4KPHN2ZyAKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgd2lkdGg9IjI0IgogIGhlaWdodD0iMjQiCiAgdmlld0JveD0iMCAwIDI0IDI0IgogIGZpbGw9Im5vbmUiCiAgc3Ryb2tlPSIjZmZmZmZmIgogIHN0cm9rZS13aWR0aD0iMiIKICBzdHJva2UtbGluZWNhcD0icm91bmQiCiAgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIKPgogIDxwYXRoIGQ9Ik0yMSAxMmE5IDkgMCAxMS02LjIxOS04LjU2IiAvPgo8L3N2Zz4=);
|
| 136 |
+
background-size: 20px;
|
| 137 |
+
background-repeat: no-repeat;
|
| 138 |
+
background-position: center;
|
| 139 |
+
animation: spin 0.5s linear infinite;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
.dark .btn.loader:before {
|
| 143 |
+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBMaWNlbnNlOiBNSVQuIE1hZGUgYnkgTHVjaWRlIENvbnRyaWJ1dG9yczogaHR0cHM6Ly9sdWNpZGUuZGV2LyAtLT4KPHN2ZyAKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgd2lkdGg9IjI0IgogIGhlaWdodD0iMjQiCiAgdmlld0JveD0iMCAwIDI0IDI0IgogIGZpbGw9Im5vbmUiCiAgc3Ryb2tlPSIjMDAwMDAwIgogIHN0cm9rZS13aWR0aD0iMiIKICBzdHJva2UtbGluZWNhcD0icm91bmQiCiAgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIKPgogIDxwYXRoIGQ9Ik0yMSAxMmE5IDkgMCAxMS02LjIxOS04LjU2IiAvPgo8L3N2Zz4=);
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
@keyframes spin {
|
| 147 |
+
0% {
|
| 148 |
+
transform: rotate(0deg);
|
| 149 |
+
}
|
| 150 |
+
100% {
|
| 151 |
+
transform: rotate(360deg);
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
#toggle_theme {
|
| 156 |
+
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='1em' height='1em' fill='none' aria-hidden='true' focusable='false'%3E%3Cg%3E%3Cpath d='M21.0672 11.8568L20.4253 11.469L21.0672 11.8568ZM12.1432 2.93276L11.7553 2.29085V2.29085L12.1432 2.93276ZM21.25 12C21.25 17.1086 17.1086 21.25 12 21.25V22.75C17.9371 22.75 22.75 17.9371 22.75 12H21.25ZM12 21.25C6.89137 21.25 2.75 17.1086 2.75 12H1.25C1.25 17.9371 6.06294 22.75 12 22.75V21.25ZM2.75 12C2.75 6.89137 6.89137 2.75 12 2.75V1.25C6.06294 1.25 1.25 6.06294 1.25 12H2.75ZM15.5 14.25C12.3244 14.25 9.75 11.6756 9.75 8.5H8.25C8.25 12.5041 11.4959 15.75 15.5 15.75V14.25ZM20.4253 11.469C19.4172 13.1373 17.5882 14.25 15.5 14.25V15.75C18.1349 15.75 20.4407 14.3439 21.7092 12.2447L20.4253 11.469ZM9.75 8.5C9.75 6.41182 10.8627 4.5828 12.531 3.57467L11.7553 2.29085C9.65609 3.5593 8.25 5.86509 8.25 8.5H9.75ZM12 2.75C11.9115 2.75 11.8077 2.71008 11.7324 2.63168C11.6686 2.56527 11.6538 2.50244 11.6503 2.47703C11.6461 2.44587 11.6482 2.35557 11.7553 2.29085L12.531 3.57467C13.0342 3.27065 13.196 2.71398 13.1368 2.27627C13.0754 1.82126 12.7166 1.25 12 1.25V2.75ZM21.7092 12.2447C21.6444 12.3518 21.5541 12.3539 21.523 12.3497C21.4976 12.3462 21.4347 12.3314 21.3683 12.2676C21.2899 12.1923 21.25 12.0885 21.25 12H22.75C22.75 11.2834 22.1787 10.9246 21.7237 10.8632C21.286 10.804 20.7293 10.9658 20.4253 11.469L21.7092 12.2447Z' fill='%23000' stroke-width='1.5'%3E%3C/path%3E%3C/g%3E%3C/svg%3E")
|
| 157 |
+
center no-repeat;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
.dark #toggle_theme {
|
| 161 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='1em' height='1em' fill='none' aria-hidden='true' focusable='false'%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='5' stroke='%23fff' stroke-width='1.5'%3E%3C/circle%3E%3Cpath d='M12 2V4' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M12 20V22' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M4 12L2 12' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M22 12L20 12' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M19.7778 4.22266L17.5558 6.25424' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M4.22217 4.22266L6.44418 6.25424' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M6.44434 17.5557L4.22211 19.7779' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M19.7778 19.7773L17.5558 17.5551' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
button {
|
| 165 |
+
cursor: pointer;
|
| 166 |
+
transition: all 0.3s ease;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
button:disabled {
|
| 170 |
+
cursor: not-allowed;
|
| 171 |
+
opacity: 0.6;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
#video-player {
|
| 175 |
+
min-height: 200px;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
body .video-js .vjs-big-play-button {
|
| 179 |
+
margin: 0;
|
| 180 |
+
display: flex;
|
| 181 |
+
width: 80px;
|
| 182 |
+
height: 80px;
|
| 183 |
+
transform: translate(-50%, -50%);
|
| 184 |
+
align-items: center;
|
| 185 |
+
justify-content: center;
|
| 186 |
+
border-radius: 50%;
|
| 187 |
+
border-color: var(--color-primary) !important;
|
| 188 |
+
background-color: transparent !important;
|
| 189 |
+
color: var(--color-primary);
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
body .vjs-has-started .vjs-big-play-button {
|
| 193 |
+
display: none;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
body .video-js .vjs-big-play-button .vjs-icon-placeholder {
|
| 197 |
+
display: flex;
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
body .video-js .vjs-big-play-button .vjs-icon-placeholder:before {
|
| 201 |
+
font-size: 47px;
|
| 202 |
+
position: static;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
.vjs-progress-control {
|
| 206 |
+
position: absolute !important;
|
| 207 |
+
bottom: 45px !important;
|
| 208 |
+
width: calc(100% - 20px) !important;
|
| 209 |
+
padding: 1px !important;
|
| 210 |
+
height: 40px !important;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.video-js .vjs-progress-control .vjs-progress-holder,
|
| 214 |
+
.video-js .vjs-play-progress,
|
| 215 |
+
.video-js .vjs-load-progress div {
|
| 216 |
+
border-radius: 50px;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
.video-js .vjs-control-bar {
|
| 220 |
+
background: transparent;
|
| 221 |
+
padding: 0 10px 15px;
|
| 222 |
+
height: auto;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.video-js .vjs-control-bar:before {
|
| 226 |
+
content: "";
|
| 227 |
+
position: absolute;
|
| 228 |
+
width: 100%;
|
| 229 |
+
height: 200%;
|
| 230 |
+
bottom: 0;
|
| 231 |
+
left: 0;
|
| 232 |
+
background: linear-gradient(
|
| 233 |
+
0deg,
|
| 234 |
+
rgba(0, 0, 0, 0.84) 0%,
|
| 235 |
+
rgba(0, 0, 0, 0.59) 50%,
|
| 236 |
+
rgba(0, 0, 0, 0) 100%
|
| 237 |
+
);
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
.vjs-remaining-time {
|
| 241 |
+
display: none;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
.video-js .vjs-current-time,
|
| 245 |
+
.video-js .vjs-duration,
|
| 246 |
+
.vjs-live .vjs-time-control,
|
| 247 |
+
.vjs-time-divider,
|
| 248 |
+
.vjs-live .vjs-time-divider {
|
| 249 |
+
display: block;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
.video-js .vjs-time-control.vjs-duration {
|
| 253 |
+
margin-right: auto;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
.video-js .vjs-time-control {
|
| 257 |
+
padding: 0 5px;
|
| 258 |
+
font-size: 15px;
|
| 259 |
+
line-height: 1.2;
|
| 260 |
+
height: 33px;
|
| 261 |
+
display: flex;
|
| 262 |
+
align-items: center;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
.video-js .vjs-time-control.vjs-time-divider {
|
| 266 |
+
padding: 0;
|
| 267 |
+
min-width: 0;
|
| 268 |
+
font-size: 18px;
|
| 269 |
+
line-height: 1;
|
| 270 |
+
color: white;
|
| 271 |
+
opacity: 1;
|
| 272 |
+
z-index: 1;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
.video-js .vjs-play-control {
|
| 276 |
+
height: 33px;
|
| 277 |
+
width: 33px;
|
| 278 |
+
display: flex;
|
| 279 |
+
align-items: center;
|
| 280 |
+
justify-content: center;
|
| 281 |
+
}
|
| 282 |
+
|
| 283 |
+
.video-js .vjs-play-control .vjs-icon-placeholder {
|
| 284 |
+
display: flex;
|
| 285 |
+
align-items: center;
|
| 286 |
+
justify-content: center;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
.video-js .vjs-play-control .vjs-icon-placeholder:before {
|
| 290 |
+
position: relative;
|
| 291 |
+
line-height: 1;
|
| 292 |
+
font-size: 30px;
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
.video-js .vjs-volume-panel .vjs-volume-control {
|
| 296 |
+
opacity: 1;
|
| 297 |
+
width: 60px !important;
|
| 298 |
+
height: 33px;
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
.video-js .vjs-volume-panel {
|
| 302 |
+
width: 110px !important;
|
| 303 |
+
height: 33px;
|
| 304 |
+
margin-left: 6px;
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
.video-js .vjs-mute-control {
|
| 308 |
+
width: 33px;
|
| 309 |
+
height: 33px;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
.video-js .vjs-mute-control .vjs-icon-placeholder {
|
| 313 |
+
display: flex;
|
| 314 |
+
align-items: center;
|
| 315 |
+
justify-content: center;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.video-js .vjs-mute-control .vjs-icon-placeholder:before {
|
| 319 |
+
position: relative;
|
| 320 |
+
line-height: 1;
|
| 321 |
+
font-size: 25px;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
.video-js .vjs-volume-bar {
|
| 325 |
+
margin: 1.55em 0.45em;
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
.video-js .vjs-time-control.vjs-remaining-time {
|
| 329 |
+
display: none;
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
.video-js .vjs-progress-control .vjs-progress-holder {
|
| 333 |
+
margin: 0 6px;
|
| 334 |
+
}
|
| 335 |
+
|
| 336 |
+
.video-js .vjs-slider {
|
| 337 |
+
background-color: rgb(255 255 255 / 27%);
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
.video-js .vjs-load-progress div {
|
| 341 |
+
background: rgb(255 255 255 / 33%);
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
.video-js .vjs-fullscreen-control,
|
| 345 |
+
.video-js .vjs-picture-in-picture-control,
|
| 346 |
+
.video-js .vjs-subs-caps-button {
|
| 347 |
+
height: 33px;
|
| 348 |
+
width: 33px;
|
| 349 |
+
display: flex;
|
| 350 |
+
align-items: center;
|
| 351 |
+
justify-content: center;
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
.video-js .vjs-fullscreen-control {
|
| 355 |
+
margin-left: 6px;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
.video-js .vjs-picture-in-picture-control {
|
| 359 |
+
margin-left: 10px;
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
.video-js .vjs-fullscreen-control .vjs-icon-placeholder,
|
| 363 |
+
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder,
|
| 364 |
+
.video-js .vjs-subs-caps-button .vjs-icon-placeholder {
|
| 365 |
+
display: flex;
|
| 366 |
+
align-items: center;
|
| 367 |
+
justify-content: center;
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before,
|
| 371 |
+
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before,
|
| 372 |
+
.video-js .vjs-subs-caps-button .vjs-icon-placeholder:before {
|
| 373 |
+
position: relative;
|
| 374 |
+
line-height: 1;
|
| 375 |
+
font-size: 28px;
|
| 376 |
+
}
|
| 377 |
+
|
| 378 |
+
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before {
|
| 379 |
+
font-size: 24px;
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
.video-js .vjs-subs-caps-button .vjs-icon-placeholder:before {
|
| 383 |
+
font-size: 26px;
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
.video-select {
|
| 387 |
+
position: absolute;
|
| 388 |
+
top: 15px;
|
| 389 |
+
left: 13px;
|
| 390 |
+
background: transparent;
|
| 391 |
+
display: none;
|
| 392 |
+
font-size: 15px;
|
| 393 |
+
max-width: calc(100% - 25px);
|
| 394 |
+
overflow: hidden;
|
| 395 |
+
text-overflow: ellipsis;
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
.vjs-has-started.vjs-controls-enabled .video-select {
|
| 399 |
+
display: block;
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
.video-js .vjs-progress-holder {
|
| 403 |
+
height: 6px;
|
| 404 |
+
transition: 0.3s all;
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
.video-js .vjs-play-progress:before {
|
| 408 |
+
font-size: 17px;
|
| 409 |
+
transition: 0.3s all;
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
.video-js .vjs-progress-control:hover .vjs-progress-holder {
|
| 413 |
+
height: 8px;
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
.vjs-progress-control:hover .vjs-play-progress:before {
|
| 417 |
+
font-size: 22px;
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
@media (max-width: 768px) {
|
| 421 |
+
.video-js .vjs-control-bar {
|
| 422 |
+
padding: 0 4px 5px;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
.vjs-progress-control {
|
| 426 |
+
bottom: 31px !important;
|
| 427 |
+
width: calc(100% - 6px) !important;
|
| 428 |
+
padding: 0px !important;
|
| 429 |
+
height: 30px !important;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
.video-js .vjs-play-control .vjs-icon-placeholder:before {
|
| 433 |
+
font-size: 25px;
|
| 434 |
+
}
|
| 435 |
+
|
| 436 |
+
.video-js .vjs-play-control {
|
| 437 |
+
width: 26px;
|
| 438 |
+
height: 26px;
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
.video-js .vjs-volume-bar {
|
| 442 |
+
margin: 1.15em 0.45em;
|
| 443 |
+
}
|
| 444 |
+
|
| 445 |
+
.video-js .vjs-volume-panel .vjs-volume-control {
|
| 446 |
+
width: 45px !important;
|
| 447 |
+
height: 26px;
|
| 448 |
+
}
|
| 449 |
+
|
| 450 |
+
.video-js .vjs-mute-control {
|
| 451 |
+
width: 26px;
|
| 452 |
+
height: 26px;
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
.video-js .vjs-volume-panel {
|
| 456 |
+
width: 80px !important;
|
| 457 |
+
height: 26px;
|
| 458 |
+
margin-left: 4px;
|
| 459 |
+
}
|
| 460 |
+
|
| 461 |
+
.video-js .vjs-time-control {
|
| 462 |
+
padding: 0 4px;
|
| 463 |
+
font-size: 12px;
|
| 464 |
+
height: 26px;
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
.video-js .vjs-time-control.vjs-time-divider {
|
| 468 |
+
font-size: 15px;
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
.video-js .vjs-fullscreen-control,
|
| 472 |
+
.video-js .vjs-picture-in-picture-control,
|
| 473 |
+
.video-js .vjs-subs-caps-button {
|
| 474 |
+
height: 26px;
|
| 475 |
+
width: 26px;
|
| 476 |
+
}
|
| 477 |
+
|
| 478 |
+
.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before,
|
| 479 |
+
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before,
|
| 480 |
+
.video-js .vjs-subs-caps-button .vjs-icon-placeholder:before {
|
| 481 |
+
font-size: 20px;
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before {
|
| 485 |
+
font-size: 18px;
|
| 486 |
+
}
|
| 487 |
+
}
|
client/assets/index.js
ADDED
|
@@ -0,0 +1,1080 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const getLanguage = (code) => {
|
| 2 |
+
const lang = new Intl.DisplayNames(["en"], { type: "language" });
|
| 3 |
+
return lang.of(code);
|
| 4 |
+
};
|
| 5 |
+
|
| 6 |
+
let settings = {
|
| 7 |
+
enableProxy: false,
|
| 8 |
+
proxyUrl: "",
|
| 9 |
+
enableProwlarr: false,
|
| 10 |
+
prowlarrHost: "",
|
| 11 |
+
prowlarrApiKey: "",
|
| 12 |
+
enableJackett: false,
|
| 13 |
+
jackettHost: "",
|
| 14 |
+
jackettApiKey: "",
|
| 15 |
+
};
|
| 16 |
+
|
| 17 |
+
const searchWrapper = document.querySelector("#search-wrapper");
|
| 18 |
+
var player = null;
|
| 19 |
+
|
| 20 |
+
function doubleTapFF(options) {
|
| 21 |
+
var videoElement = this
|
| 22 |
+
var videoElementId = this.id();
|
| 23 |
+
document.getElementById(videoElementId).addEventListener("touchstart", tapHandler);
|
| 24 |
+
var tapedTwice = false;
|
| 25 |
+
function tapHandler(e) {
|
| 26 |
+
if (!videoElement.paused()) {
|
| 27 |
+
|
| 28 |
+
if (!tapedTwice) {
|
| 29 |
+
tapedTwice = true;
|
| 30 |
+
setTimeout(function () {
|
| 31 |
+
tapedTwice = false;
|
| 32 |
+
}, 300);
|
| 33 |
+
return false;
|
| 34 |
+
}
|
| 35 |
+
e.preventDefault();
|
| 36 |
+
var br = document.getElementById(videoElementId).getBoundingClientRect();
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
var x = e.touches[0].clientX - br.left;
|
| 40 |
+
var y = e.touches[0].clientY - br.top;
|
| 41 |
+
|
| 42 |
+
if (x <= br.width / 2) {
|
| 43 |
+
videoElement.currentTime(player.currentTime() - 10)
|
| 44 |
+
} else {
|
| 45 |
+
videoElement.currentTime(player.currentTime() + 10)
|
| 46 |
+
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
videojs.registerPlugin('doubleTapFF', doubleTapFF);
|
| 54 |
+
|
| 55 |
+
(async function ($) {
|
| 56 |
+
// toggle dark mode button
|
| 57 |
+
const toggleDarkMode = () => {
|
| 58 |
+
const html = document.querySelector("html");
|
| 59 |
+
html.classList.toggle("dark");
|
| 60 |
+
localStorage.setItem(
|
| 61 |
+
"theme",
|
| 62 |
+
html.classList.contains("dark") ? "dark" : "light"
|
| 63 |
+
);
|
| 64 |
+
};
|
| 65 |
+
const toggleDarkModeButton = document.querySelector("#toggle_theme");
|
| 66 |
+
toggleDarkModeButton.addEventListener("click", toggleDarkMode);
|
| 67 |
+
|
| 68 |
+
// handle past button
|
| 69 |
+
const pastButton = document.querySelector("#copy_magnet");
|
| 70 |
+
pastButton.addEventListener("click", async () => {
|
| 71 |
+
navigator.clipboard.readText().then((text) => {
|
| 72 |
+
document.getElementById("magnet").value = text;
|
| 73 |
+
});
|
| 74 |
+
});
|
| 75 |
+
|
| 76 |
+
// handle demo button
|
| 77 |
+
const demoButton = document.querySelector("#demo_torrent");
|
| 78 |
+
demoButton.addEventListener("click", async () => {
|
| 79 |
+
document.getElementById("magnet").value =
|
| 80 |
+
"magnet:?xt=urn:btih:08ada5a7a6183aae1e09d831df6748d566095a10&dn=Sintel&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel.torrent";
|
| 81 |
+
|
| 82 |
+
document
|
| 83 |
+
.querySelector("#torrent-form")
|
| 84 |
+
.dispatchEvent(new Event("submit"));
|
| 85 |
+
});
|
| 86 |
+
|
| 87 |
+
const form = document.querySelector("#torrent-form");
|
| 88 |
+
form.addEventListener("submit", async (e) => {
|
| 89 |
+
e.preventDefault();
|
| 90 |
+
const magnet = document.querySelector("#magnet").value;
|
| 91 |
+
|
| 92 |
+
if (!magnet) {
|
| 93 |
+
butterup.toast({
|
| 94 |
+
message: "Please enter a magnet link",
|
| 95 |
+
location: "top-right",
|
| 96 |
+
icon: true,
|
| 97 |
+
dismissable: true,
|
| 98 |
+
type: "error",
|
| 99 |
+
});
|
| 100 |
+
return;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
// clean up previous player
|
| 104 |
+
if (player) {
|
| 105 |
+
player.dispose();
|
| 106 |
+
player = null;
|
| 107 |
+
const vidElm = document.createElement("video");
|
| 108 |
+
vidElm.setAttribute("id", "video-player");
|
| 109 |
+
vidElm.setAttribute("class", "video-js mt-10 w-full");
|
| 110 |
+
|
| 111 |
+
document.querySelector("main").appendChild(vidElm);
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
form
|
| 115 |
+
.querySelector("button[type=submit]")
|
| 116 |
+
.setAttribute("disabled", "disabled");
|
| 117 |
+
form.querySelector("button[type=submit]").innerHTML = "";
|
| 118 |
+
form.querySelector("button[type=submit]").classList.add("loader");
|
| 119 |
+
|
| 120 |
+
const res = await fetch("/api/v1/torrent/add", {
|
| 121 |
+
method: "POST",
|
| 122 |
+
headers: { "Content-Type": "application/json" },
|
| 123 |
+
body: JSON.stringify({ magnet }),
|
| 124 |
+
});
|
| 125 |
+
|
| 126 |
+
if (!res.ok) {
|
| 127 |
+
const err = await res.json();
|
| 128 |
+
butterup.toast({
|
| 129 |
+
message: err.error || "Something went wrong",
|
| 130 |
+
location: "top-right",
|
| 131 |
+
icon: true,
|
| 132 |
+
dismissable: true,
|
| 133 |
+
type: "error",
|
| 134 |
+
});
|
| 135 |
+
form.querySelector("button[type=submit]").removeAttribute("disabled");
|
| 136 |
+
form.querySelector("button[type=submit]").innerHTML = "Play Now";
|
| 137 |
+
form.querySelector("button[type=submit]").classList.remove("loader");
|
| 138 |
+
searchResults.querySelectorAll("#play-torrent").forEach((el) => {
|
| 139 |
+
el.removeAttribute("disabled");
|
| 140 |
+
el.innerHTML = "Watch";
|
| 141 |
+
el.classList.remove("loader");
|
| 142 |
+
});
|
| 143 |
+
return;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
const { sessionId } = await res.json();
|
| 147 |
+
const filesRes = await fetch("/api/v1/torrent/" + sessionId);
|
| 148 |
+
|
| 149 |
+
if (!filesRes.ok) {
|
| 150 |
+
const err = await filesRes.json();
|
| 151 |
+
butterup.toast({
|
| 152 |
+
message: err.error || "Something went wrong",
|
| 153 |
+
location: "top-right",
|
| 154 |
+
icon: true,
|
| 155 |
+
dismissable: true,
|
| 156 |
+
type: "error",
|
| 157 |
+
});
|
| 158 |
+
form.querySelector("button[type=submit]").removeAttribute("disabled");
|
| 159 |
+
form.querySelector("button[type=submit]").innerHTML = "Play Now";
|
| 160 |
+
form.querySelector("button[type=submit]").classList.remove("loader");
|
| 161 |
+
document.querySelectorAll("#play-torrent").forEach((el) => {
|
| 162 |
+
el.removeAttribute("disabled");
|
| 163 |
+
el.innerHTML = "Watch";
|
| 164 |
+
el.classList.remove("loader");
|
| 165 |
+
});
|
| 166 |
+
return;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
const files = await filesRes.json();
|
| 170 |
+
|
| 171 |
+
// Find video file
|
| 172 |
+
const videoFiles = files.filter((f) =>
|
| 173 |
+
f.name.match(/\.(mp4|mkv|webm|avi)$/i)
|
| 174 |
+
);
|
| 175 |
+
|
| 176 |
+
if (!videoFiles.length) {
|
| 177 |
+
butterup.toast({
|
| 178 |
+
message: "No video file found",
|
| 179 |
+
location: "top-right",
|
| 180 |
+
icon: true,
|
| 181 |
+
dismissable: true,
|
| 182 |
+
type: "error",
|
| 183 |
+
});
|
| 184 |
+
form.querySelector("button[type=submit]").removeAttribute("disabled");
|
| 185 |
+
form.querySelector("button[type=submit]").innerHTML = "Play Now";
|
| 186 |
+
form.querySelector("button[type=submit]").classList.remove("loader");
|
| 187 |
+
document.querySelectorAll("#play-torrent").forEach((el) => {
|
| 188 |
+
el.removeAttribute("disabled");
|
| 189 |
+
el.innerHTML = "Watch";
|
| 190 |
+
el.classList.remove("loader");
|
| 191 |
+
});
|
| 192 |
+
return;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
const subtitleFiles = files.filter((f) =>
|
| 196 |
+
f.name.match(/\.(srt|vtt|sub)$/i)
|
| 197 |
+
);
|
| 198 |
+
|
| 199 |
+
const videoUrls = videoFiles.map((file) => {
|
| 200 |
+
return {
|
| 201 |
+
src: "/api/v1/torrent/" + sessionId + "/stream/" + file.index,
|
| 202 |
+
title: file.name,
|
| 203 |
+
type: "video/mp4",
|
| 204 |
+
};
|
| 205 |
+
});
|
| 206 |
+
|
| 207 |
+
let subtitles = [];
|
| 208 |
+
if (subtitleFiles.length) {
|
| 209 |
+
subtitles = subtitleFiles.map((subFile) => {
|
| 210 |
+
let language = "en";
|
| 211 |
+
let langName = "English";
|
| 212 |
+
|
| 213 |
+
// Try to extract language code from filename
|
| 214 |
+
console.log(subFile.name);
|
| 215 |
+
const langMatch = subFile.name.match(/\.([a-z]{2,3})\.(srt|vtt|sub)$/i);
|
| 216 |
+
if (langMatch) {
|
| 217 |
+
language = langMatch[1];
|
| 218 |
+
langName = getLanguage(language);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
return {
|
| 222 |
+
src:
|
| 223 |
+
"/api/v1/torrent/" +
|
| 224 |
+
sessionId +
|
| 225 |
+
"/stream/" +
|
| 226 |
+
subFile.index +
|
| 227 |
+
".vtt?format=vtt",
|
| 228 |
+
srclang: language,
|
| 229 |
+
label: langName,
|
| 230 |
+
kind: "subtitles",
|
| 231 |
+
type: "vtt",
|
| 232 |
+
};
|
| 233 |
+
});
|
| 234 |
+
}
|
| 235 |
+
player = videojs(
|
| 236 |
+
"video-player",
|
| 237 |
+
{
|
| 238 |
+
fluid: true,
|
| 239 |
+
controls: true,
|
| 240 |
+
autoplay: true,
|
| 241 |
+
preload: "auto",
|
| 242 |
+
sources: [{
|
| 243 |
+
src: videoUrls[0].src,
|
| 244 |
+
type: videoUrls[0].type,
|
| 245 |
+
label: videoUrls[0].title,
|
| 246 |
+
}],
|
| 247 |
+
tracks: subtitles,
|
| 248 |
+
html5: {
|
| 249 |
+
nativeTextTracks: false
|
| 250 |
+
},
|
| 251 |
+
plugins: {
|
| 252 |
+
hotkeys: {
|
| 253 |
+
volumeStep: 0.1,
|
| 254 |
+
seekStep: 5,
|
| 255 |
+
enableModifiersForNumbers: false,
|
| 256 |
+
enableVolumeScroll: false,
|
| 257 |
+
},
|
| 258 |
+
},
|
| 259 |
+
},
|
| 260 |
+
function () {
|
| 261 |
+
player = this;
|
| 262 |
+
player.on("error", (e) => {
|
| 263 |
+
console.error(e);
|
| 264 |
+
butterup.toast({
|
| 265 |
+
message: "Something went wrong",
|
| 266 |
+
location: "top-right",
|
| 267 |
+
icon: true,
|
| 268 |
+
dismissable: true,
|
| 269 |
+
type: "error",
|
| 270 |
+
});
|
| 271 |
+
});
|
| 272 |
+
}
|
| 273 |
+
);
|
| 274 |
+
player.doubleTapFF();
|
| 275 |
+
|
| 276 |
+
document.querySelector("#video-player").style.display = "block";
|
| 277 |
+
// scroll to video player
|
| 278 |
+
setTimeout(() => {
|
| 279 |
+
window.scrollTo({
|
| 280 |
+
top: document.body.scrollHeight,
|
| 281 |
+
behavior: "smooth",
|
| 282 |
+
});
|
| 283 |
+
|
| 284 |
+
if (videoUrls.length > 1) {
|
| 285 |
+
const videoSelect = document.createElement("select");
|
| 286 |
+
videoSelect.setAttribute("id", "video-select");
|
| 287 |
+
videoSelect.setAttribute("class", "video-select");
|
| 288 |
+
videoSelect.setAttribute("aria-label", "Select video");
|
| 289 |
+
videoUrls.forEach((video) => {
|
| 290 |
+
const option = document.createElement("option");
|
| 291 |
+
option.setAttribute("value", video.src);
|
| 292 |
+
option.innerHTML = video.title;
|
| 293 |
+
videoSelect.appendChild(option);
|
| 294 |
+
});
|
| 295 |
+
videoSelect.addEventListener("change", (e) => {
|
| 296 |
+
const selectedSrc = e.target.value;
|
| 297 |
+
player.src({
|
| 298 |
+
src: selectedSrc,
|
| 299 |
+
type: "video/mp4",
|
| 300 |
+
});
|
| 301 |
+
player.play();
|
| 302 |
+
});
|
| 303 |
+
document.querySelector("#video-player").appendChild(videoSelect);
|
| 304 |
+
}
|
| 305 |
+
player.play()
|
| 306 |
+
}, 300);
|
| 307 |
+
|
| 308 |
+
form.querySelector("button[type=submit]").removeAttribute("disabled");
|
| 309 |
+
form.querySelector("button[type=submit]").innerHTML = "Play Now";
|
| 310 |
+
form.querySelector("button[type=submit]").classList.remove("loader");
|
| 311 |
+
document.querySelectorAll("#play-torrent").forEach((el) => {
|
| 312 |
+
el.removeAttribute("disabled");
|
| 313 |
+
el.innerHTML = "Watch";
|
| 314 |
+
el.classList.remove("loader");
|
| 315 |
+
});
|
| 316 |
+
});
|
| 317 |
+
|
| 318 |
+
// create switch button
|
| 319 |
+
const switchInputs = document.querySelectorAll("#switchInput");
|
| 320 |
+
switchInputs.forEach((input) => {
|
| 321 |
+
input.querySelector("input").addEventListener("change", (e) => {
|
| 322 |
+
const dot = e.target.parentElement.querySelector(".dot");
|
| 323 |
+
const wrapper = e.target.parentElement.querySelector(".switch-wrapper");
|
| 324 |
+
if (e.target.checked) {
|
| 325 |
+
dot.classList.add("translate-x-full", "!bg-muted");
|
| 326 |
+
wrapper.classList.add("bg-primary");
|
| 327 |
+
} else {
|
| 328 |
+
dot.classList.remove("translate-x-full", "!bg-muted");
|
| 329 |
+
wrapper.classList.remove("bg-primary");
|
| 330 |
+
}
|
| 331 |
+
});
|
| 332 |
+
});
|
| 333 |
+
|
| 334 |
+
document.querySelector("#settings-btn").addEventListener("click", () => {
|
| 335 |
+
document.querySelector("#settings-model").classList.toggle("hidden");
|
| 336 |
+
});
|
| 337 |
+
|
| 338 |
+
document.querySelectorAll("#close-settings").forEach((el) => {
|
| 339 |
+
el.addEventListener("click", () => {
|
| 340 |
+
document.querySelector("#settings-model").classList.toggle("hidden");
|
| 341 |
+
document.querySelector("#proxy-result").classList.remove("flex");
|
| 342 |
+
document.querySelector("#proxy-result").classList.add("hidden");
|
| 343 |
+
});
|
| 344 |
+
});
|
| 345 |
+
|
| 346 |
+
document.querySelectorAll(".tab-btn").forEach((el) => {
|
| 347 |
+
el.addEventListener("click", () => {
|
| 348 |
+
const tabIndex = el.getAttribute("data-index");
|
| 349 |
+
document.querySelectorAll(".tab").forEach((tab) => {
|
| 350 |
+
const index = tab.getAttribute("data-tab");
|
| 351 |
+
if (index === tabIndex) {
|
| 352 |
+
tab.classList.remove("hidden");
|
| 353 |
+
document.querySelectorAll(".tab-btn").forEach((el) => {
|
| 354 |
+
el.classList.remove("bg-primary", "text-primary-foreground");
|
| 355 |
+
el.classList.add("bg-muted");
|
| 356 |
+
});
|
| 357 |
+
el.classList.add("bg-primary", "text-primary-foreground");
|
| 358 |
+
} else {
|
| 359 |
+
tab.classList.add("hidden");
|
| 360 |
+
}
|
| 361 |
+
});
|
| 362 |
+
});
|
| 363 |
+
});
|
| 364 |
+
|
| 365 |
+
function generatePagination(currentPage, pageSize, total, target) {
|
| 366 |
+
const pagination = document.querySelector(target);
|
| 367 |
+
if (!pagination) return;
|
| 368 |
+
pagination.classList.remove("hidden");
|
| 369 |
+
pagination.innerHTML = "";
|
| 370 |
+
const totalPages = Math.ceil(total / pageSize);
|
| 371 |
+
const startPage = Math.max(1, currentPage - 2);
|
| 372 |
+
const endPage = Math.min(totalPages, currentPage + 2);
|
| 373 |
+
|
| 374 |
+
for (let i = startPage; i <= endPage; i++) {
|
| 375 |
+
const pageButton = document.createElement("button");
|
| 376 |
+
pageButton.textContent = i;
|
| 377 |
+
pageButton.classList.add("page-button");
|
| 378 |
+
if (i === currentPage) {
|
| 379 |
+
pageButton.classList.add("active");
|
| 380 |
+
}
|
| 381 |
+
pageButton.addEventListener("click", () => {
|
| 382 |
+
searchPage = i;
|
| 383 |
+
updateSearchResults();
|
| 384 |
+
});
|
| 385 |
+
pagination.appendChild(pageButton);
|
| 386 |
+
}
|
| 387 |
+
const prevButton = document.createElement("button");
|
| 388 |
+
prevButton.innerHTML = `<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 20 20" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.72 9.47a.75.75 0 0 0 0 1.06l4.25 4.25a.75.75 0 1 0 1.06-1.06L6.31 10l3.72-3.72a.75.75 0 1 0-1.06-1.06L4.72 9.47Zm9.25-4.25L9.72 9.47a.75.75 0 0 0 0 1.06l4.25 4.25a.75.75 0 1 0 1.06-1.06L11.31 10l3.72-3.72a.75.75 0 0 0-1.06-1.06Z" clip-rule="evenodd"></path></svg>`;
|
| 389 |
+
prevButton.classList.add("page-button");
|
| 390 |
+
prevButton.disabled = currentPage === 1;
|
| 391 |
+
prevButton.addEventListener("click", () => {
|
| 392 |
+
if (currentPage > 1) {
|
| 393 |
+
searchPage--;
|
| 394 |
+
updateSearchResults();
|
| 395 |
+
}
|
| 396 |
+
});
|
| 397 |
+
pagination.prepend(prevButton);
|
| 398 |
+
const nextButton = document.createElement("button");
|
| 399 |
+
nextButton.innerHTML = `<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 20 20" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.28 9.47a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 1 1-1.06-1.06L13.69 10 9.97 6.28a.75.75 0 0 1 1.06-1.06l4.25 4.25ZM6.03 5.22l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L8.69 10 4.97 6.28a.75.75 0 0 1 1.06-1.06Z" clip-rule="evenodd"></path></svg>`;
|
| 400 |
+
nextButton.classList.add("page-button");
|
| 401 |
+
nextButton.disabled = currentPage === totalPages;
|
| 402 |
+
nextButton.addEventListener("click", () => {
|
| 403 |
+
if (currentPage < totalPages) {
|
| 404 |
+
searchPage++;
|
| 405 |
+
updateSearchResults();
|
| 406 |
+
}
|
| 407 |
+
});
|
| 408 |
+
pagination.appendChild(nextButton);
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
let searchData = [];
|
| 412 |
+
let searchPage = 1;
|
| 413 |
+
let searchPageSize = 5;
|
| 414 |
+
|
| 415 |
+
const updateSearchResults = () => {
|
| 416 |
+
const searchPagination = document.querySelector("#search-pagination");
|
| 417 |
+
const searchResults = document.querySelector("#search-result");
|
| 418 |
+
searchResults.classList.remove("hidden");
|
| 419 |
+
searchResults.querySelector("tbody").innerHTML = "";
|
| 420 |
+
searchResults.querySelector("tfoot").classList.add("hidden");
|
| 421 |
+
if (searchData.length === 0) {
|
| 422 |
+
searchResults.querySelector("tfoot").classList.remove("hidden");
|
| 423 |
+
return;
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
+
const start = (searchPage - 1) * searchPageSize;
|
| 427 |
+
const end = start + searchPageSize;
|
| 428 |
+
const results = searchData.slice(start, end);
|
| 429 |
+
results.forEach((result) => {
|
| 430 |
+
const resultDiv = document.createElement("tr");
|
| 431 |
+
resultDiv.innerHTML = `
|
| 432 |
+
<td>${result.title}</td>
|
| 433 |
+
<td>${result.indexer}</td>
|
| 434 |
+
<td>${result.size}</td>
|
| 435 |
+
<td>${result.leechers}/${result.seeders}</td>
|
| 436 |
+
<td><button id="play-torrent" type="button" class="btn small" data-magnet="${
|
| 437 |
+
result.downloadUrl || result.magnetUrl
|
| 438 |
+
}">Watch</button></td>
|
| 439 |
+
`;
|
| 440 |
+
searchResults.querySelector("tbody").appendChild(resultDiv);
|
| 441 |
+
});
|
| 442 |
+
|
| 443 |
+
// Generate pagination
|
| 444 |
+
const totalResults = searchData.length;
|
| 445 |
+
const totalPages = Math.ceil(totalResults / searchPageSize);
|
| 446 |
+
generatePagination(
|
| 447 |
+
searchPage,
|
| 448 |
+
searchPageSize,
|
| 449 |
+
totalResults,
|
| 450 |
+
"#search-pagination"
|
| 451 |
+
);
|
| 452 |
+
|
| 453 |
+
// Add event listener to each play button
|
| 454 |
+
searchResults.querySelectorAll("#play-torrent").forEach((el) => {
|
| 455 |
+
el.addEventListener("click", async (e) => {
|
| 456 |
+
const magnet = e.target.getAttribute("data-magnet");
|
| 457 |
+
document.querySelector("#magnet").value = magnet;
|
| 458 |
+
document
|
| 459 |
+
.querySelector("#torrent-form")
|
| 460 |
+
.dispatchEvent(new Event("submit"));
|
| 461 |
+
e.target.setAttribute("disabled", "disabled");
|
| 462 |
+
e.target.innerHTML = "";
|
| 463 |
+
e.target.classList.add("loader");
|
| 464 |
+
});
|
| 465 |
+
});
|
| 466 |
+
};
|
| 467 |
+
|
| 468 |
+
document.querySelector("#search-form").addEventListener("submit", (e) => {
|
| 469 |
+
e.preventDefault();
|
| 470 |
+
const query = e.target.querySelector("#search").value;
|
| 471 |
+
if (!query) {
|
| 472 |
+
butterup.toast({
|
| 473 |
+
message: "Please enter a search query",
|
| 474 |
+
location: "top-right",
|
| 475 |
+
icon: true,
|
| 476 |
+
dismissable: true,
|
| 477 |
+
type: "error",
|
| 478 |
+
});
|
| 479 |
+
return;
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
searchData = [];
|
| 483 |
+
searchPage = 1;
|
| 484 |
+
|
| 485 |
+
e.target
|
| 486 |
+
.querySelector("button[type=submit]")
|
| 487 |
+
.setAttribute("disabled", "disabled");
|
| 488 |
+
e.target.querySelector("button[type=submit]").classList.add("loader");
|
| 489 |
+
e.target.querySelector("button[type=submit]").innerHTML = "";
|
| 490 |
+
const searchResults = document.querySelector("#search-result");
|
| 491 |
+
|
| 492 |
+
searchResults.classList.add("hidden");
|
| 493 |
+
document.querySelector("#search-pagination").classList.add("hidden");
|
| 494 |
+
|
| 495 |
+
let apiUrl = "/api/v1/prowlarr/search";
|
| 496 |
+
|
| 497 |
+
if (
|
| 498 |
+
(!settings.prowlarrHost || !settings.prowlarrApiKey) &&
|
| 499 |
+
settings.jackettHost &&
|
| 500 |
+
settings.jackettApiKey
|
| 501 |
+
) {
|
| 502 |
+
apiUrl = "/api/v1/jackett/search";
|
| 503 |
+
}
|
| 504 |
+
|
| 505 |
+
fetch(`${apiUrl}?q=${query}`, {
|
| 506 |
+
method: "POST",
|
| 507 |
+
headers: { "Content-Type": "application/json" },
|
| 508 |
+
})
|
| 509 |
+
.then(async (res) => {
|
| 510 |
+
if (!res.ok) {
|
| 511 |
+
const err = await res.json();
|
| 512 |
+
throw new Error(res.error || "Failed to fetch search results");
|
| 513 |
+
}
|
| 514 |
+
return res.json();
|
| 515 |
+
})
|
| 516 |
+
.then((data) => {
|
| 517 |
+
if (data && typeof data === "object") {
|
| 518 |
+
searchData = data;
|
| 519 |
+
} else {
|
| 520 |
+
searchData = [];
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
updateSearchResults();
|
| 524 |
+
})
|
| 525 |
+
.catch((error) => {
|
| 526 |
+
console.error("There was a problem with the fetch operation:", error);
|
| 527 |
+
butterup.toast({
|
| 528 |
+
message: error.message || "Failed to fetch search results",
|
| 529 |
+
location: "top-right",
|
| 530 |
+
icon: true,
|
| 531 |
+
dismissable: true,
|
| 532 |
+
type: "error",
|
| 533 |
+
});
|
| 534 |
+
})
|
| 535 |
+
.finally(() => {
|
| 536 |
+
e.target
|
| 537 |
+
.querySelector("button[type=submit]")
|
| 538 |
+
.removeAttribute("disabled");
|
| 539 |
+
e.target
|
| 540 |
+
.querySelector("button[type=submit]")
|
| 541 |
+
.classList.remove("loader");
|
| 542 |
+
e.target.querySelector("button[type=submit]").innerHTML = "Search";
|
| 543 |
+
});
|
| 544 |
+
});
|
| 545 |
+
|
| 546 |
+
const testProwlarrConfig = async () => {
|
| 547 |
+
const prowlarrHost = document.querySelector("#prowlarrHost").value;
|
| 548 |
+
const prowlarrApiKey = document.querySelector("#prowlarrApiKey").value;
|
| 549 |
+
const prowlarrTestBtn = document.querySelector("#test-prowlarr");
|
| 550 |
+
|
| 551 |
+
if (!prowlarrHost || !prowlarrApiKey) {
|
| 552 |
+
butterup.toast({
|
| 553 |
+
message: "Please enter Prowlarr host and API key",
|
| 554 |
+
location: "top-right",
|
| 555 |
+
icon: true,
|
| 556 |
+
dismissable: true,
|
| 557 |
+
type: "error",
|
| 558 |
+
});
|
| 559 |
+
return false;
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
prowlarrTestBtn.setAttribute("disabled", "disabled");
|
| 563 |
+
prowlarrTestBtn.querySelector("span").innerHTML = "Testing...";
|
| 564 |
+
|
| 565 |
+
const response = await fetch("/api/v1/prowlarr/test", {
|
| 566 |
+
method: "POST",
|
| 567 |
+
headers: { "Content-Type": "application/json" },
|
| 568 |
+
body: JSON.stringify({ prowlarrHost, prowlarrApiKey }),
|
| 569 |
+
});
|
| 570 |
+
|
| 571 |
+
const data = await response.json();
|
| 572 |
+
if (!response.ok) {
|
| 573 |
+
butterup.toast({
|
| 574 |
+
message: data.error || "Failed to test Prowlarr connection",
|
| 575 |
+
location: "top-right",
|
| 576 |
+
icon: true,
|
| 577 |
+
dismissable: true,
|
| 578 |
+
type: "error",
|
| 579 |
+
});
|
| 580 |
+
prowlarrTestBtn.removeAttribute("disabled");
|
| 581 |
+
prowlarrTestBtn.querySelector("span").innerHTML = "Test Connection";
|
| 582 |
+
return false;
|
| 583 |
+
}
|
| 584 |
+
|
| 585 |
+
butterup.toast({
|
| 586 |
+
message: "Prowlarr settings are valid",
|
| 587 |
+
location: "top-right",
|
| 588 |
+
icon: true,
|
| 589 |
+
dismissable: true,
|
| 590 |
+
type: "success",
|
| 591 |
+
});
|
| 592 |
+
|
| 593 |
+
prowlarrTestBtn.removeAttribute("disabled");
|
| 594 |
+
prowlarrTestBtn.querySelector("span").innerHTML = "Test Connection";
|
| 595 |
+
|
| 596 |
+
return true;
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
document.querySelector("#test-prowlarr").addEventListener("click", (e) => {
|
| 600 |
+
testProwlarrConfig();
|
| 601 |
+
});
|
| 602 |
+
|
| 603 |
+
const testJackettConfig = async () => {
|
| 604 |
+
const jackettHost = document.querySelector("#jackettHost").value;
|
| 605 |
+
const jackettApiKey = document.querySelector("#jackettApiKey").value;
|
| 606 |
+
const jackettTestBtn = document.querySelector("#test-jackett");
|
| 607 |
+
|
| 608 |
+
if (!jackettHost || !jackettApiKey) {
|
| 609 |
+
butterup.toast({
|
| 610 |
+
message: "Please enter Jackett host and API key",
|
| 611 |
+
location: "top-right",
|
| 612 |
+
icon: true,
|
| 613 |
+
dismissable: true,
|
| 614 |
+
type: "error",
|
| 615 |
+
});
|
| 616 |
+
return false;
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
jackettTestBtn.setAttribute("disabled", "disabled");
|
| 620 |
+
jackettTestBtn.querySelector("span").innerHTML = "Testing...";
|
| 621 |
+
|
| 622 |
+
const response = await fetch("/api/v1/jackett/test", {
|
| 623 |
+
method: "POST",
|
| 624 |
+
headers: { "Content-Type": "application/json" },
|
| 625 |
+
body: JSON.stringify({ jackettHost, jackettApiKey }),
|
| 626 |
+
});
|
| 627 |
+
|
| 628 |
+
const data = await response.json();
|
| 629 |
+
if (!response.ok) {
|
| 630 |
+
butterup.toast({
|
| 631 |
+
message: data.error || "Failed to test Jackett connection",
|
| 632 |
+
location: "top-right",
|
| 633 |
+
icon: true,
|
| 634 |
+
dismissable: true,
|
| 635 |
+
type: "error",
|
| 636 |
+
});
|
| 637 |
+
jackettTestBtn.removeAttribute("disabled");
|
| 638 |
+
jackettTestBtn.querySelector("span").innerHTML = "Test Connection";
|
| 639 |
+
return false;
|
| 640 |
+
}
|
| 641 |
+
|
| 642 |
+
butterup.toast({
|
| 643 |
+
message: "Jackett settings are valid",
|
| 644 |
+
location: "top-right",
|
| 645 |
+
icon: true,
|
| 646 |
+
dismissable: true,
|
| 647 |
+
type: "success",
|
| 648 |
+
});
|
| 649 |
+
|
| 650 |
+
jackettTestBtn.removeAttribute("disabled");
|
| 651 |
+
jackettTestBtn.querySelector("span").innerHTML = "Test Connection";
|
| 652 |
+
|
| 653 |
+
return true;
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
document.querySelector("#test-jackett").addEventListener("click", (e) => {
|
| 657 |
+
testJackettConfig();
|
| 658 |
+
});
|
| 659 |
+
|
| 660 |
+
const testProxy = async () => {
|
| 661 |
+
const proxyUrl = document.querySelector("#proxyUrl").value;
|
| 662 |
+
const proxyBtn = document.querySelector("#test-proxy");
|
| 663 |
+
|
| 664 |
+
if (!proxyUrl) {
|
| 665 |
+
butterup.toast({
|
| 666 |
+
message: "Please enter a proxy URL",
|
| 667 |
+
location: "top-right",
|
| 668 |
+
icon: true,
|
| 669 |
+
dismissable: true,
|
| 670 |
+
type: "error",
|
| 671 |
+
});
|
| 672 |
+
return false;
|
| 673 |
+
}
|
| 674 |
+
|
| 675 |
+
proxyBtn.setAttribute("disabled", "disabled");
|
| 676 |
+
proxyBtn.querySelector("span").innerHTML = "Testing...";
|
| 677 |
+
|
| 678 |
+
const response = await fetch("/api/v1/proxy/test", {
|
| 679 |
+
method: "POST",
|
| 680 |
+
headers: { "Content-Type": "application/json" },
|
| 681 |
+
body: JSON.stringify({ proxyUrl }),
|
| 682 |
+
});
|
| 683 |
+
|
| 684 |
+
const data = await response.json();
|
| 685 |
+
|
| 686 |
+
if (!response.ok) {
|
| 687 |
+
butterup.toast({
|
| 688 |
+
message: data.error || "Failed to test Proxy connection",
|
| 689 |
+
location: "top-right",
|
| 690 |
+
icon: true,
|
| 691 |
+
dismissable: true,
|
| 692 |
+
type: "error",
|
| 693 |
+
});
|
| 694 |
+
proxyBtn.removeAttribute("disabled");
|
| 695 |
+
proxyBtn.querySelector("span").innerHTML = "Test Proxy";
|
| 696 |
+
return false;
|
| 697 |
+
}
|
| 698 |
+
|
| 699 |
+
butterup.toast({
|
| 700 |
+
message: "Proxy url is valid",
|
| 701 |
+
location: "top-right",
|
| 702 |
+
icon: true,
|
| 703 |
+
dismissable: true,
|
| 704 |
+
type: "success",
|
| 705 |
+
});
|
| 706 |
+
|
| 707 |
+
proxyBtn.removeAttribute("disabled");
|
| 708 |
+
proxyBtn.querySelector("span").innerHTML = "Test Proxy";
|
| 709 |
+
|
| 710 |
+
if (data?.origin) {
|
| 711 |
+
document.querySelector("#proxy-result").classList.remove("hidden");
|
| 712 |
+
document.querySelector("#proxy-result").classList.add("flex");
|
| 713 |
+
document.querySelector("#proxy-result .output-ip").innerHTML = data?.origin
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
return true;
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
document.querySelector("#test-proxy").addEventListener("click", () => {
|
| 720 |
+
testProxy();
|
| 721 |
+
});
|
| 722 |
+
|
| 723 |
+
document
|
| 724 |
+
.querySelector("#proxy-settings-form")
|
| 725 |
+
.addEventListener("submit", async (e) => {
|
| 726 |
+
e.preventDefault();
|
| 727 |
+
const enableProxy = e.target.querySelector("#enableProxy").checked;
|
| 728 |
+
const proxyUrl = e.target.querySelector("#proxyUrl").value;
|
| 729 |
+
const submitButton = e.target.querySelector("button[type=submit]");
|
| 730 |
+
|
| 731 |
+
submitButton.setAttribute("disabled", "disabled");
|
| 732 |
+
|
| 733 |
+
if (enableProxy) {
|
| 734 |
+
const isValid = await testProxy();
|
| 735 |
+
if (!isValid) {
|
| 736 |
+
submitButton.removeAttribute("disabled");
|
| 737 |
+
return;
|
| 738 |
+
}
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
+
submitButton.classList.add("loader");
|
| 742 |
+
submitButton.innerHTML = "Saving...";
|
| 743 |
+
|
| 744 |
+
const body = {
|
| 745 |
+
enableProxy,
|
| 746 |
+
proxyUrl,
|
| 747 |
+
};
|
| 748 |
+
|
| 749 |
+
const response = await fetch("/api/v1/settings/proxy", {
|
| 750 |
+
method: "POST",
|
| 751 |
+
headers: { "Content-Type": "application/json" },
|
| 752 |
+
body: JSON.stringify(body),
|
| 753 |
+
})
|
| 754 |
+
|
| 755 |
+
const data = await response.json();
|
| 756 |
+
|
| 757 |
+
if (!response.ok) {
|
| 758 |
+
butterup.toast({
|
| 759 |
+
message: data.error || "Failed to save settings",
|
| 760 |
+
location: "top-right",
|
| 761 |
+
icon: true,
|
| 762 |
+
dismissable: true,
|
| 763 |
+
type: "error",
|
| 764 |
+
});
|
| 765 |
+
} else {
|
| 766 |
+
butterup.toast({
|
| 767 |
+
message: "Proxy settings saved successfully",
|
| 768 |
+
location: "top-right",
|
| 769 |
+
icon: true,
|
| 770 |
+
dismissable: true,
|
| 771 |
+
type: "success",
|
| 772 |
+
});
|
| 773 |
+
|
| 774 |
+
settings = {
|
| 775 |
+
...settings,
|
| 776 |
+
enableProxy: body.enableProxy,
|
| 777 |
+
proxyUrl: body.proxyUrl,
|
| 778 |
+
};
|
| 779 |
+
}
|
| 780 |
+
|
| 781 |
+
submitButton.removeAttribute("disabled");
|
| 782 |
+
submitButton.classList.remove("loader");
|
| 783 |
+
submitButton.innerHTML = "Save Settings";
|
| 784 |
+
});
|
| 785 |
+
|
| 786 |
+
document
|
| 787 |
+
.querySelector("#prowlarr-settings-form")
|
| 788 |
+
.addEventListener("submit", async (e) => {
|
| 789 |
+
e.preventDefault();
|
| 790 |
+
const enableProwlarr = e.target.querySelector("#enableProwlarr").checked;
|
| 791 |
+
const prowlarrHost = e.target.querySelector("#prowlarrHost").value;
|
| 792 |
+
const prowlarrApiKey = e.target.querySelector("#prowlarrApiKey").value;
|
| 793 |
+
const submitButton = e.target.querySelector("button[type=submit]");
|
| 794 |
+
|
| 795 |
+
submitButton.setAttribute("disabled", "disabled");
|
| 796 |
+
|
| 797 |
+
if (enableProwlarr) {
|
| 798 |
+
const isValid = await testProwlarrConfig();
|
| 799 |
+
if (!isValid) {
|
| 800 |
+
submitButton.removeAttribute("disabled");
|
| 801 |
+
return;
|
| 802 |
+
}
|
| 803 |
+
}
|
| 804 |
+
|
| 805 |
+
submitButton.classList.add("loader");
|
| 806 |
+
submitButton.innerHTML = "Saving...";
|
| 807 |
+
|
| 808 |
+
const body = {
|
| 809 |
+
enableProwlarr,
|
| 810 |
+
prowlarrHost,
|
| 811 |
+
prowlarrApiKey,
|
| 812 |
+
};
|
| 813 |
+
|
| 814 |
+
const response = await fetch("/api/v1/settings/prowlarr", {
|
| 815 |
+
method: "POST",
|
| 816 |
+
headers: { "Content-Type": "application/json" },
|
| 817 |
+
body: JSON.stringify(body),
|
| 818 |
+
})
|
| 819 |
+
|
| 820 |
+
const data = await response.json();
|
| 821 |
+
if (!response.ok) {
|
| 822 |
+
butterup.toast({
|
| 823 |
+
message: data.error || "Failed to save settings",
|
| 824 |
+
location: "top-right",
|
| 825 |
+
icon: true,
|
| 826 |
+
dismissable: true,
|
| 827 |
+
type: "error",
|
| 828 |
+
});
|
| 829 |
+
} else {
|
| 830 |
+
butterup.toast({
|
| 831 |
+
message: "Prowlarr settings saved successfully",
|
| 832 |
+
location: "top-right",
|
| 833 |
+
icon: true,
|
| 834 |
+
dismissable: true,
|
| 835 |
+
type: "success",
|
| 836 |
+
});
|
| 837 |
+
|
| 838 |
+
settings = {
|
| 839 |
+
...settings,
|
| 840 |
+
enableProwlarr: body.enableProwlarr,
|
| 841 |
+
prowlarrHost: body.prowlarrHost,
|
| 842 |
+
prowlarrApiKey: body.prowlarrApiKey,
|
| 843 |
+
};
|
| 844 |
+
|
| 845 |
+
// Check if Prowlarr or Jackett is enabled
|
| 846 |
+
if (body?.enableProwlarr || settings?.enableJackett) {
|
| 847 |
+
searchWrapper.classList.remove("hidden");
|
| 848 |
+
} else {
|
| 849 |
+
searchWrapper.classList.add("hidden");
|
| 850 |
+
}
|
| 851 |
+
}
|
| 852 |
+
|
| 853 |
+
submitButton.removeAttribute("disabled");
|
| 854 |
+
submitButton.classList.remove("loader");
|
| 855 |
+
submitButton.innerHTML = "Save Settings";
|
| 856 |
+
});
|
| 857 |
+
|
| 858 |
+
document
|
| 859 |
+
.querySelector("#jackett-settings-form")
|
| 860 |
+
.addEventListener("submit", async (e) => {
|
| 861 |
+
e.preventDefault();
|
| 862 |
+
const enableJackett = e.target.querySelector("#enableJackett").checked;
|
| 863 |
+
const jackettHost = e.target.querySelector("#jackettHost").value;
|
| 864 |
+
const jackettApiKey = e.target.querySelector("#jackettApiKey").value;
|
| 865 |
+
const submitButton = e.target.querySelector("button[type=submit]");
|
| 866 |
+
|
| 867 |
+
submitButton.setAttribute("disabled", "disabled");
|
| 868 |
+
|
| 869 |
+
if (enableJackett) {
|
| 870 |
+
const isValid = await testJackettConfig();
|
| 871 |
+
if (!isValid) {
|
| 872 |
+
submitButton.removeAttribute("disabled");
|
| 873 |
+
return;
|
| 874 |
+
}
|
| 875 |
+
}
|
| 876 |
+
|
| 877 |
+
submitButton.classList.add("loader");
|
| 878 |
+
submitButton.innerHTML = "Saving...";
|
| 879 |
+
|
| 880 |
+
const body = {
|
| 881 |
+
enableJackett,
|
| 882 |
+
jackettHost,
|
| 883 |
+
jackettApiKey,
|
| 884 |
+
};
|
| 885 |
+
|
| 886 |
+
const response = await fetch("/api/v1/settings/jackett", {
|
| 887 |
+
method: "POST",
|
| 888 |
+
headers: { "Content-Type": "application/json" },
|
| 889 |
+
body: JSON.stringify(body),
|
| 890 |
+
})
|
| 891 |
+
|
| 892 |
+
const data = await response.json();
|
| 893 |
+
if (!response.ok) {
|
| 894 |
+
butterup.toast({
|
| 895 |
+
message: data.error || "Failed to save settings",
|
| 896 |
+
location: "top-right",
|
| 897 |
+
icon: true,
|
| 898 |
+
dismissable: true,
|
| 899 |
+
type: "error",
|
| 900 |
+
});
|
| 901 |
+
} else {
|
| 902 |
+
butterup.toast({
|
| 903 |
+
message: "Jackett settings saved successfully",
|
| 904 |
+
location: "top-right",
|
| 905 |
+
icon: true,
|
| 906 |
+
dismissable: true,
|
| 907 |
+
type: "success",
|
| 908 |
+
});
|
| 909 |
+
|
| 910 |
+
settings = {
|
| 911 |
+
...settings,
|
| 912 |
+
enableJackett: body.enableJackett,
|
| 913 |
+
jackettHost: body.jackettHost,
|
| 914 |
+
jackettApiKey: body.jackettApiKey,
|
| 915 |
+
};
|
| 916 |
+
|
| 917 |
+
// Check if Jackett or Jackett is enabled
|
| 918 |
+
if (body?.enableJackett || settings?.enableJackett) {
|
| 919 |
+
searchWrapper.classList.remove("hidden");
|
| 920 |
+
} else {
|
| 921 |
+
searchWrapper.classList.add("hidden");
|
| 922 |
+
}
|
| 923 |
+
}
|
| 924 |
+
|
| 925 |
+
submitButton.removeAttribute("disabled");
|
| 926 |
+
submitButton.classList.remove("loader");
|
| 927 |
+
submitButton.innerHTML = "Save Settings";
|
| 928 |
+
});
|
| 929 |
+
|
| 930 |
+
document.querySelector("#torrent_file").addEventListener("change", (e) => {
|
| 931 |
+
const file = e.target.files[0];
|
| 932 |
+
if (file) {
|
| 933 |
+
const formData = new FormData();
|
| 934 |
+
formData.append("torrent", file);
|
| 935 |
+
|
| 936 |
+
fetch("/api/v1/torrent/convert", {
|
| 937 |
+
method: "POST",
|
| 938 |
+
body: formData,
|
| 939 |
+
})
|
| 940 |
+
.then(async (res) => {
|
| 941 |
+
if (!res.ok) {
|
| 942 |
+
const err = await res.json();
|
| 943 |
+
throw new Error(err.error || "Failed to upload torrent file");
|
| 944 |
+
}
|
| 945 |
+
return res.json();
|
| 946 |
+
})
|
| 947 |
+
.then((data) => {
|
| 948 |
+
document.querySelector("#magnet").value = data.magnet;
|
| 949 |
+
document
|
| 950 |
+
.querySelector("#torrent-form")
|
| 951 |
+
.dispatchEvent(new Event("submit"));
|
| 952 |
+
})
|
| 953 |
+
.catch((error) => {
|
| 954 |
+
console.error("There was a problem with the fetch operation:", error);
|
| 955 |
+
butterup.toast({
|
| 956 |
+
message: error.message || "Failed to upload torrent file",
|
| 957 |
+
location: "top-right",
|
| 958 |
+
icon: true,
|
| 959 |
+
dismissable: true,
|
| 960 |
+
type: "error",
|
| 961 |
+
});
|
| 962 |
+
});
|
| 963 |
+
}
|
| 964 |
+
});
|
| 965 |
+
|
| 966 |
+
const torrentFileWrapper = document.querySelector("#torrent_file_wrapper");
|
| 967 |
+
torrentFileWrapper.addEventListener("dragenter", (e) => {
|
| 968 |
+
e.preventDefault();
|
| 969 |
+
e.stopPropagation();
|
| 970 |
+
torrentFileWrapper.classList.add("drag-over");
|
| 971 |
+
});
|
| 972 |
+
torrentFileWrapper.addEventListener("dragover", (e) => {
|
| 973 |
+
e.preventDefault();
|
| 974 |
+
e.stopPropagation();
|
| 975 |
+
});
|
| 976 |
+
torrentFileWrapper.addEventListener("dragleave", (e) => {
|
| 977 |
+
e.preventDefault();
|
| 978 |
+
e.stopPropagation();
|
| 979 |
+
torrentFileWrapper.classList.remove("drag-over");
|
| 980 |
+
});
|
| 981 |
+
torrentFileWrapper.addEventListener("drop", (e) => {
|
| 982 |
+
e.preventDefault();
|
| 983 |
+
e.stopPropagation();
|
| 984 |
+
torrentFileWrapper.classList.remove("drag-over");
|
| 985 |
+
const files = e.dataTransfer.files;
|
| 986 |
+
if (files.length > 0) {
|
| 987 |
+
const file = files[0];
|
| 988 |
+
if (file.name.endsWith(".torrent")) {
|
| 989 |
+
const formData = new FormData();
|
| 990 |
+
formData.append("torrent", file);
|
| 991 |
+
|
| 992 |
+
fetch("/api/v1/torrent/convert", {
|
| 993 |
+
method: "POST",
|
| 994 |
+
body: formData,
|
| 995 |
+
})
|
| 996 |
+
.then(async (res) => {
|
| 997 |
+
if (!res.ok) {
|
| 998 |
+
const err = await res.json();
|
| 999 |
+
throw new Error(err.error || "Failed to upload torrent file");
|
| 1000 |
+
}
|
| 1001 |
+
return res.json();
|
| 1002 |
+
})
|
| 1003 |
+
.then((data) => {
|
| 1004 |
+
document.querySelector("#magnet").value = data.magnet;
|
| 1005 |
+
document
|
| 1006 |
+
.querySelector("#torrent-form")
|
| 1007 |
+
.dispatchEvent(new Event("submit"));
|
| 1008 |
+
})
|
| 1009 |
+
.catch((error) => {
|
| 1010 |
+
console.error(
|
| 1011 |
+
"There was a problem with the fetch operation:",
|
| 1012 |
+
error
|
| 1013 |
+
);
|
| 1014 |
+
butterup.toast({
|
| 1015 |
+
message: error.message || "Failed to upload torrent file",
|
| 1016 |
+
location: "top-right",
|
| 1017 |
+
icon: true,
|
| 1018 |
+
dismissable: true,
|
| 1019 |
+
type: "error",
|
| 1020 |
+
});
|
| 1021 |
+
});
|
| 1022 |
+
} else {
|
| 1023 |
+
butterup.toast({
|
| 1024 |
+
message: "Please drop a valid torrent file",
|
| 1025 |
+
location: "top-right",
|
| 1026 |
+
icon: true,
|
| 1027 |
+
dismissable: true,
|
| 1028 |
+
type: "error",
|
| 1029 |
+
});
|
| 1030 |
+
}
|
| 1031 |
+
}
|
| 1032 |
+
});
|
| 1033 |
+
|
| 1034 |
+
// fetch settings
|
| 1035 |
+
fetch("/api/v1/settings")
|
| 1036 |
+
.then((res) => {
|
| 1037 |
+
if (!res.ok) {
|
| 1038 |
+
throw new Error("Network response was not ok");
|
| 1039 |
+
}
|
| 1040 |
+
return res.json();
|
| 1041 |
+
})
|
| 1042 |
+
.then((data) => {
|
| 1043 |
+
settings = data;
|
| 1044 |
+
document.querySelector("#enableProxy").checked = data.enableProxy;
|
| 1045 |
+
document.querySelector("#proxyUrl").value = data.proxyUrl || "";
|
| 1046 |
+
document.querySelector("#enableProwlarr").checked =
|
| 1047 |
+
data.enableProwlarr || false;
|
| 1048 |
+
document.querySelector("#prowlarrHost").value = data.prowlarrHost || "";
|
| 1049 |
+
document.querySelector("#prowlarrApiKey").value =
|
| 1050 |
+
data.prowlarrApiKey || "";
|
| 1051 |
+
document.querySelector("#enableJackett").checked =
|
| 1052 |
+
data.enableJackett || false;
|
| 1053 |
+
document.querySelector("#jackettHost").value = data.jackettHost || "";
|
| 1054 |
+
document.querySelector("#jackettApiKey").value = data.jackettApiKey || "";
|
| 1055 |
+
|
| 1056 |
+
// Set switch button state
|
| 1057 |
+
const switchInputs = document.querySelectorAll("#switchInput");
|
| 1058 |
+
switchInputs.forEach((input) => {
|
| 1059 |
+
const dot = input.querySelector(".dot");
|
| 1060 |
+
const wrapper = input.querySelector(".switch-wrapper");
|
| 1061 |
+
if (input.querySelector("input").checked) {
|
| 1062 |
+
dot.classList.add("translate-x-full", "!bg-muted");
|
| 1063 |
+
wrapper.classList.add("bg-primary");
|
| 1064 |
+
} else {
|
| 1065 |
+
dot.classList.remove("translate-x-full", "!bg-muted");
|
| 1066 |
+
wrapper.classList.remove("bg-primary");
|
| 1067 |
+
}
|
| 1068 |
+
});
|
| 1069 |
+
|
| 1070 |
+
// Check if Prowlarr or Jackett is enabled
|
| 1071 |
+
if (data?.enableProwlarr || data?.enableJackett) {
|
| 1072 |
+
searchWrapper.classList.remove("hidden");
|
| 1073 |
+
} else {
|
| 1074 |
+
searchWrapper.classList.add("hidden");
|
| 1075 |
+
}
|
| 1076 |
+
})
|
| 1077 |
+
.catch((error) => {
|
| 1078 |
+
console.error("There was a problem with the fetch operation:", error);
|
| 1079 |
+
});
|
| 1080 |
+
})();
|
client/assets/output.css
ADDED
|
@@ -0,0 +1,1529 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*! tailwindcss v4.1.4 | MIT License | https://tailwindcss.com */
|
| 2 |
+
@layer properties;
|
| 3 |
+
@layer theme, base, components, utilities;
|
| 4 |
+
@layer theme {
|
| 5 |
+
:root, :host {
|
| 6 |
+
--font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
|
| 7 |
+
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
| 8 |
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
| 9 |
+
"Courier New", monospace;
|
| 10 |
+
--color-black: #000;
|
| 11 |
+
--color-white: #fff;
|
| 12 |
+
--spacing: 0.25rem;
|
| 13 |
+
--container-lg: 32rem;
|
| 14 |
+
--container-xl: 36rem;
|
| 15 |
+
--container-4xl: 56rem;
|
| 16 |
+
--text-xs: 0.75rem;
|
| 17 |
+
--text-xs--line-height: calc(1 / 0.75);
|
| 18 |
+
--text-sm: 0.875rem;
|
| 19 |
+
--text-sm--line-height: calc(1.25 / 0.875);
|
| 20 |
+
--text-base: 1rem;
|
| 21 |
+
--text-base--line-height: calc(1.5 / 1);
|
| 22 |
+
--text-xl: 1.25rem;
|
| 23 |
+
--text-xl--line-height: calc(1.75 / 1.25);
|
| 24 |
+
--text-3xl: 1.875rem;
|
| 25 |
+
--text-3xl--line-height: calc(2.25 / 1.875);
|
| 26 |
+
--text-5xl: 3rem;
|
| 27 |
+
--text-5xl--line-height: 1;
|
| 28 |
+
--font-weight-medium: 500;
|
| 29 |
+
--font-weight-semibold: 600;
|
| 30 |
+
--font-weight-bold: 700;
|
| 31 |
+
--radius-md: calc(var(--radius) - 2px);
|
| 32 |
+
--radius-lg: var(--radius);
|
| 33 |
+
--radius-xl: calc(var(--radius) + 4px);
|
| 34 |
+
--default-transition-duration: 150ms;
|
| 35 |
+
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
| 36 |
+
--default-font-family: var(--font-sans);
|
| 37 |
+
--default-mono-font-family: var(--font-mono);
|
| 38 |
+
--color-background: var(--background);
|
| 39 |
+
--color-foreground: var(--foreground);
|
| 40 |
+
--color-ring: var(--ring);
|
| 41 |
+
--color-input: var(--input);
|
| 42 |
+
--color-border: var(--border);
|
| 43 |
+
--color-destructive: var(--destructive);
|
| 44 |
+
--color-accent-foreground: var(--accent-foreground);
|
| 45 |
+
--color-accent: var(--accent);
|
| 46 |
+
--color-muted-foreground: var(--muted-foreground);
|
| 47 |
+
--color-muted: var(--muted);
|
| 48 |
+
--color-secondary: var(--secondary);
|
| 49 |
+
--color-primary-foreground: var(--primary-foreground);
|
| 50 |
+
--color-primary: var(--primary);
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
@layer base {
|
| 54 |
+
*, ::after, ::before, ::backdrop, ::file-selector-button {
|
| 55 |
+
box-sizing: border-box;
|
| 56 |
+
margin: 0;
|
| 57 |
+
padding: 0;
|
| 58 |
+
border: 0 solid;
|
| 59 |
+
}
|
| 60 |
+
html, :host {
|
| 61 |
+
line-height: 1.5;
|
| 62 |
+
-webkit-text-size-adjust: 100%;
|
| 63 |
+
tab-size: 4;
|
| 64 |
+
font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
|
| 65 |
+
font-feature-settings: var(--default-font-feature-settings, normal);
|
| 66 |
+
font-variation-settings: var(--default-font-variation-settings, normal);
|
| 67 |
+
-webkit-tap-highlight-color: transparent;
|
| 68 |
+
}
|
| 69 |
+
hr {
|
| 70 |
+
height: 0;
|
| 71 |
+
color: inherit;
|
| 72 |
+
border-top-width: 1px;
|
| 73 |
+
}
|
| 74 |
+
abbr:where([title]) {
|
| 75 |
+
-webkit-text-decoration: underline dotted;
|
| 76 |
+
text-decoration: underline dotted;
|
| 77 |
+
}
|
| 78 |
+
h1, h2, h3, h4, h5, h6 {
|
| 79 |
+
font-size: inherit;
|
| 80 |
+
font-weight: inherit;
|
| 81 |
+
}
|
| 82 |
+
a {
|
| 83 |
+
color: inherit;
|
| 84 |
+
-webkit-text-decoration: inherit;
|
| 85 |
+
text-decoration: inherit;
|
| 86 |
+
}
|
| 87 |
+
b, strong {
|
| 88 |
+
font-weight: bolder;
|
| 89 |
+
}
|
| 90 |
+
code, kbd, samp, pre {
|
| 91 |
+
font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
|
| 92 |
+
font-feature-settings: var(--default-mono-font-feature-settings, normal);
|
| 93 |
+
font-variation-settings: var(--default-mono-font-variation-settings, normal);
|
| 94 |
+
font-size: 1em;
|
| 95 |
+
}
|
| 96 |
+
small {
|
| 97 |
+
font-size: 80%;
|
| 98 |
+
}
|
| 99 |
+
sub, sup {
|
| 100 |
+
font-size: 75%;
|
| 101 |
+
line-height: 0;
|
| 102 |
+
position: relative;
|
| 103 |
+
vertical-align: baseline;
|
| 104 |
+
}
|
| 105 |
+
sub {
|
| 106 |
+
bottom: -0.25em;
|
| 107 |
+
}
|
| 108 |
+
sup {
|
| 109 |
+
top: -0.5em;
|
| 110 |
+
}
|
| 111 |
+
table {
|
| 112 |
+
text-indent: 0;
|
| 113 |
+
border-color: inherit;
|
| 114 |
+
border-collapse: collapse;
|
| 115 |
+
}
|
| 116 |
+
:-moz-focusring {
|
| 117 |
+
outline: auto;
|
| 118 |
+
}
|
| 119 |
+
progress {
|
| 120 |
+
vertical-align: baseline;
|
| 121 |
+
}
|
| 122 |
+
summary {
|
| 123 |
+
display: list-item;
|
| 124 |
+
}
|
| 125 |
+
ol, ul, menu {
|
| 126 |
+
list-style: none;
|
| 127 |
+
}
|
| 128 |
+
img, svg, video, canvas, audio, iframe, embed, object {
|
| 129 |
+
display: block;
|
| 130 |
+
vertical-align: middle;
|
| 131 |
+
}
|
| 132 |
+
img, video {
|
| 133 |
+
max-width: 100%;
|
| 134 |
+
height: auto;
|
| 135 |
+
}
|
| 136 |
+
button, input, select, optgroup, textarea, ::file-selector-button {
|
| 137 |
+
font: inherit;
|
| 138 |
+
font-feature-settings: inherit;
|
| 139 |
+
font-variation-settings: inherit;
|
| 140 |
+
letter-spacing: inherit;
|
| 141 |
+
color: inherit;
|
| 142 |
+
border-radius: 0;
|
| 143 |
+
background-color: transparent;
|
| 144 |
+
opacity: 1;
|
| 145 |
+
}
|
| 146 |
+
:where(select:is([multiple], [size])) optgroup {
|
| 147 |
+
font-weight: bolder;
|
| 148 |
+
}
|
| 149 |
+
:where(select:is([multiple], [size])) optgroup option {
|
| 150 |
+
padding-inline-start: 20px;
|
| 151 |
+
}
|
| 152 |
+
::file-selector-button {
|
| 153 |
+
margin-inline-end: 4px;
|
| 154 |
+
}
|
| 155 |
+
::placeholder {
|
| 156 |
+
opacity: 1;
|
| 157 |
+
}
|
| 158 |
+
@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
|
| 159 |
+
::placeholder {
|
| 160 |
+
color: currentcolor;
|
| 161 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 162 |
+
color: color-mix(in oklab, currentcolor 50%, transparent);
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
textarea {
|
| 167 |
+
resize: vertical;
|
| 168 |
+
}
|
| 169 |
+
::-webkit-search-decoration {
|
| 170 |
+
-webkit-appearance: none;
|
| 171 |
+
}
|
| 172 |
+
::-webkit-date-and-time-value {
|
| 173 |
+
min-height: 1lh;
|
| 174 |
+
text-align: inherit;
|
| 175 |
+
}
|
| 176 |
+
::-webkit-datetime-edit {
|
| 177 |
+
display: inline-flex;
|
| 178 |
+
}
|
| 179 |
+
::-webkit-datetime-edit-fields-wrapper {
|
| 180 |
+
padding: 0;
|
| 181 |
+
}
|
| 182 |
+
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
|
| 183 |
+
padding-block: 0;
|
| 184 |
+
}
|
| 185 |
+
:-moz-ui-invalid {
|
| 186 |
+
box-shadow: none;
|
| 187 |
+
}
|
| 188 |
+
button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
|
| 189 |
+
appearance: button;
|
| 190 |
+
}
|
| 191 |
+
::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
|
| 192 |
+
height: auto;
|
| 193 |
+
}
|
| 194 |
+
[hidden]:where(:not([hidden="until-found"])) {
|
| 195 |
+
display: none !important;
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
@layer utilities {
|
| 199 |
+
.sr-only {
|
| 200 |
+
position: absolute;
|
| 201 |
+
width: 1px;
|
| 202 |
+
height: 1px;
|
| 203 |
+
padding: 0;
|
| 204 |
+
margin: -1px;
|
| 205 |
+
overflow: hidden;
|
| 206 |
+
clip: rect(0, 0, 0, 0);
|
| 207 |
+
white-space: nowrap;
|
| 208 |
+
border-width: 0;
|
| 209 |
+
}
|
| 210 |
+
.absolute {
|
| 211 |
+
position: absolute;
|
| 212 |
+
}
|
| 213 |
+
.fixed {
|
| 214 |
+
position: fixed;
|
| 215 |
+
}
|
| 216 |
+
.relative {
|
| 217 |
+
position: relative;
|
| 218 |
+
}
|
| 219 |
+
.static {
|
| 220 |
+
position: static;
|
| 221 |
+
}
|
| 222 |
+
.inset-0 {
|
| 223 |
+
inset: calc(var(--spacing) * 0);
|
| 224 |
+
}
|
| 225 |
+
.top-0 {
|
| 226 |
+
top: calc(var(--spacing) * 0);
|
| 227 |
+
}
|
| 228 |
+
.top-\[1px\] {
|
| 229 |
+
top: 1px;
|
| 230 |
+
}
|
| 231 |
+
.right-0 {
|
| 232 |
+
right: calc(var(--spacing) * 0);
|
| 233 |
+
}
|
| 234 |
+
.left-\[1px\] {
|
| 235 |
+
left: 1px;
|
| 236 |
+
}
|
| 237 |
+
.z-\[1\] {
|
| 238 |
+
z-index: 1;
|
| 239 |
+
}
|
| 240 |
+
.z-\[2\] {
|
| 241 |
+
z-index: 2;
|
| 242 |
+
}
|
| 243 |
+
.z-\[999\] {
|
| 244 |
+
z-index: 999;
|
| 245 |
+
}
|
| 246 |
+
.container {
|
| 247 |
+
width: 100%;
|
| 248 |
+
@media (width >= 40rem) {
|
| 249 |
+
max-width: 40rem;
|
| 250 |
+
}
|
| 251 |
+
@media (width >= 48rem) {
|
| 252 |
+
max-width: 48rem;
|
| 253 |
+
}
|
| 254 |
+
@media (width >= 64rem) {
|
| 255 |
+
max-width: 64rem;
|
| 256 |
+
}
|
| 257 |
+
@media (width >= 80rem) {
|
| 258 |
+
max-width: 80rem;
|
| 259 |
+
}
|
| 260 |
+
@media (width >= 96rem) {
|
| 261 |
+
max-width: 96rem;
|
| 262 |
+
}
|
| 263 |
+
}
|
| 264 |
+
.m-auto {
|
| 265 |
+
margin: auto;
|
| 266 |
+
}
|
| 267 |
+
.mx-auto {
|
| 268 |
+
margin-inline: auto;
|
| 269 |
+
}
|
| 270 |
+
.my-4 {
|
| 271 |
+
margin-block: calc(var(--spacing) * 4);
|
| 272 |
+
}
|
| 273 |
+
.mt-4 {
|
| 274 |
+
margin-top: calc(var(--spacing) * 4);
|
| 275 |
+
}
|
| 276 |
+
.mt-7 {
|
| 277 |
+
margin-top: calc(var(--spacing) * 7);
|
| 278 |
+
}
|
| 279 |
+
.mt-10 {
|
| 280 |
+
margin-top: calc(var(--spacing) * 10);
|
| 281 |
+
}
|
| 282 |
+
.mb-6 {
|
| 283 |
+
margin-bottom: calc(var(--spacing) * 6);
|
| 284 |
+
}
|
| 285 |
+
.mb-7 {
|
| 286 |
+
margin-bottom: calc(var(--spacing) * 7);
|
| 287 |
+
}
|
| 288 |
+
.mb-8 {
|
| 289 |
+
margin-bottom: calc(var(--spacing) * 8);
|
| 290 |
+
}
|
| 291 |
+
.mb-12 {
|
| 292 |
+
margin-bottom: calc(var(--spacing) * 12);
|
| 293 |
+
}
|
| 294 |
+
.ml-3 {
|
| 295 |
+
margin-left: calc(var(--spacing) * 3);
|
| 296 |
+
}
|
| 297 |
+
.block {
|
| 298 |
+
display: block;
|
| 299 |
+
}
|
| 300 |
+
.contents {
|
| 301 |
+
display: contents;
|
| 302 |
+
}
|
| 303 |
+
.flex {
|
| 304 |
+
display: flex;
|
| 305 |
+
}
|
| 306 |
+
.hidden {
|
| 307 |
+
display: none;
|
| 308 |
+
}
|
| 309 |
+
.inline-block {
|
| 310 |
+
display: inline-block;
|
| 311 |
+
}
|
| 312 |
+
.inline-flex {
|
| 313 |
+
display: inline-flex;
|
| 314 |
+
}
|
| 315 |
+
.size-5 {
|
| 316 |
+
width: calc(var(--spacing) * 5);
|
| 317 |
+
height: calc(var(--spacing) * 5);
|
| 318 |
+
}
|
| 319 |
+
.size-10 {
|
| 320 |
+
width: calc(var(--spacing) * 10);
|
| 321 |
+
height: calc(var(--spacing) * 10);
|
| 322 |
+
}
|
| 323 |
+
.size-12 {
|
| 324 |
+
width: calc(var(--spacing) * 12);
|
| 325 |
+
height: calc(var(--spacing) * 12);
|
| 326 |
+
}
|
| 327 |
+
.size-full {
|
| 328 |
+
width: 100%;
|
| 329 |
+
height: 100%;
|
| 330 |
+
}
|
| 331 |
+
.\!h-11 {
|
| 332 |
+
height: calc(var(--spacing) * 11) !important;
|
| 333 |
+
}
|
| 334 |
+
.h-4 {
|
| 335 |
+
height: calc(var(--spacing) * 4);
|
| 336 |
+
}
|
| 337 |
+
.h-5 {
|
| 338 |
+
height: calc(var(--spacing) * 5);
|
| 339 |
+
}
|
| 340 |
+
.h-6 {
|
| 341 |
+
height: calc(var(--spacing) * 6);
|
| 342 |
+
}
|
| 343 |
+
.h-10 {
|
| 344 |
+
height: calc(var(--spacing) * 10);
|
| 345 |
+
}
|
| 346 |
+
.h-12 {
|
| 347 |
+
height: calc(var(--spacing) * 12);
|
| 348 |
+
}
|
| 349 |
+
.h-full {
|
| 350 |
+
height: 100%;
|
| 351 |
+
}
|
| 352 |
+
.max-h-\[500px\] {
|
| 353 |
+
max-height: 500px;
|
| 354 |
+
}
|
| 355 |
+
.w-4 {
|
| 356 |
+
width: calc(var(--spacing) * 4);
|
| 357 |
+
}
|
| 358 |
+
.w-5 {
|
| 359 |
+
width: calc(var(--spacing) * 5);
|
| 360 |
+
}
|
| 361 |
+
.w-6 {
|
| 362 |
+
width: calc(var(--spacing) * 6);
|
| 363 |
+
}
|
| 364 |
+
.w-11 {
|
| 365 |
+
width: calc(var(--spacing) * 11);
|
| 366 |
+
}
|
| 367 |
+
.w-12 {
|
| 368 |
+
width: calc(var(--spacing) * 12);
|
| 369 |
+
}
|
| 370 |
+
.w-\[10px\] {
|
| 371 |
+
width: 10px;
|
| 372 |
+
}
|
| 373 |
+
.w-auto {
|
| 374 |
+
width: auto;
|
| 375 |
+
}
|
| 376 |
+
.w-full {
|
| 377 |
+
width: 100%;
|
| 378 |
+
}
|
| 379 |
+
.max-w-4xl {
|
| 380 |
+
max-width: var(--container-4xl);
|
| 381 |
+
}
|
| 382 |
+
.max-w-lg {
|
| 383 |
+
max-width: var(--container-lg);
|
| 384 |
+
}
|
| 385 |
+
.max-w-xl {
|
| 386 |
+
max-width: var(--container-xl);
|
| 387 |
+
}
|
| 388 |
+
.min-w-0 {
|
| 389 |
+
min-width: calc(var(--spacing) * 0);
|
| 390 |
+
}
|
| 391 |
+
.flex-1 {
|
| 392 |
+
flex: 1;
|
| 393 |
+
}
|
| 394 |
+
.shrink-0 {
|
| 395 |
+
flex-shrink: 0;
|
| 396 |
+
}
|
| 397 |
+
.caption-bottom {
|
| 398 |
+
caption-side: bottom;
|
| 399 |
+
}
|
| 400 |
+
.translate-x-full {
|
| 401 |
+
--tw-translate-x: 100%;
|
| 402 |
+
translate: var(--tw-translate-x) var(--tw-translate-y);
|
| 403 |
+
}
|
| 404 |
+
.cursor-pointer {
|
| 405 |
+
cursor: pointer;
|
| 406 |
+
}
|
| 407 |
+
.flex-col {
|
| 408 |
+
flex-direction: column;
|
| 409 |
+
}
|
| 410 |
+
.items-center {
|
| 411 |
+
align-items: center;
|
| 412 |
+
}
|
| 413 |
+
.justify-between {
|
| 414 |
+
justify-content: space-between;
|
| 415 |
+
}
|
| 416 |
+
.justify-center {
|
| 417 |
+
justify-content: center;
|
| 418 |
+
}
|
| 419 |
+
.gap-2 {
|
| 420 |
+
gap: calc(var(--spacing) * 2);
|
| 421 |
+
}
|
| 422 |
+
.gap-3 {
|
| 423 |
+
gap: calc(var(--spacing) * 3);
|
| 424 |
+
}
|
| 425 |
+
.gap-4 {
|
| 426 |
+
gap: calc(var(--spacing) * 4);
|
| 427 |
+
}
|
| 428 |
+
.gap-6 {
|
| 429 |
+
gap: calc(var(--spacing) * 6);
|
| 430 |
+
}
|
| 431 |
+
.divide-y {
|
| 432 |
+
:where(& > :not(:last-child)) {
|
| 433 |
+
--tw-divide-y-reverse: 0;
|
| 434 |
+
border-bottom-style: var(--tw-border-style);
|
| 435 |
+
border-top-style: var(--tw-border-style);
|
| 436 |
+
border-top-width: calc(1px * var(--tw-divide-y-reverse));
|
| 437 |
+
border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
| 438 |
+
}
|
| 439 |
+
}
|
| 440 |
+
.overflow-auto {
|
| 441 |
+
overflow: auto;
|
| 442 |
+
}
|
| 443 |
+
.overflow-hidden {
|
| 444 |
+
overflow: hidden;
|
| 445 |
+
}
|
| 446 |
+
.rounded-full {
|
| 447 |
+
border-radius: calc(infinity * 1px);
|
| 448 |
+
}
|
| 449 |
+
.rounded-lg {
|
| 450 |
+
border-radius: var(--radius-lg);
|
| 451 |
+
}
|
| 452 |
+
.rounded-md {
|
| 453 |
+
border-radius: var(--radius-md);
|
| 454 |
+
}
|
| 455 |
+
.rounded-xl {
|
| 456 |
+
border-radius: var(--radius-xl);
|
| 457 |
+
}
|
| 458 |
+
.border {
|
| 459 |
+
border-style: var(--tw-border-style);
|
| 460 |
+
border-width: 1px;
|
| 461 |
+
}
|
| 462 |
+
.border-2 {
|
| 463 |
+
border-style: var(--tw-border-style);
|
| 464 |
+
border-width: 2px;
|
| 465 |
+
}
|
| 466 |
+
.border-b {
|
| 467 |
+
border-bottom-style: var(--tw-border-style);
|
| 468 |
+
border-bottom-width: 1px;
|
| 469 |
+
}
|
| 470 |
+
.border-dashed {
|
| 471 |
+
--tw-border-style: dashed;
|
| 472 |
+
border-style: dashed;
|
| 473 |
+
}
|
| 474 |
+
.border-foreground\/30 {
|
| 475 |
+
border-color: var(--color-foreground);
|
| 476 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 477 |
+
border-color: color-mix(in oklab, var(--color-foreground) 30%, transparent);
|
| 478 |
+
}
|
| 479 |
+
}
|
| 480 |
+
.border-input {
|
| 481 |
+
border-color: var(--color-input);
|
| 482 |
+
}
|
| 483 |
+
.border-primary {
|
| 484 |
+
border-color: var(--color-primary);
|
| 485 |
+
}
|
| 486 |
+
.\!bg-muted {
|
| 487 |
+
background-color: var(--color-muted) !important;
|
| 488 |
+
}
|
| 489 |
+
.bg-accent {
|
| 490 |
+
background-color: var(--color-accent);
|
| 491 |
+
}
|
| 492 |
+
.bg-accent\/50 {
|
| 493 |
+
background-color: var(--color-accent);
|
| 494 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 495 |
+
background-color: color-mix(in oklab, var(--color-accent) 50%, transparent);
|
| 496 |
+
}
|
| 497 |
+
}
|
| 498 |
+
.bg-background {
|
| 499 |
+
background-color: var(--color-background);
|
| 500 |
+
}
|
| 501 |
+
.bg-black\/70 {
|
| 502 |
+
background-color: color-mix(in srgb, #000 70%, transparent);
|
| 503 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 504 |
+
background-color: color-mix(in oklab, var(--color-black) 70%, transparent);
|
| 505 |
+
}
|
| 506 |
+
}
|
| 507 |
+
.bg-muted {
|
| 508 |
+
background-color: var(--color-muted);
|
| 509 |
+
}
|
| 510 |
+
.bg-primary {
|
| 511 |
+
background-color: var(--color-primary);
|
| 512 |
+
}
|
| 513 |
+
.bg-secondary\/50 {
|
| 514 |
+
background-color: var(--color-secondary);
|
| 515 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 516 |
+
background-color: color-mix(in oklab, var(--color-secondary) 50%, transparent);
|
| 517 |
+
}
|
| 518 |
+
}
|
| 519 |
+
.bg-transparent {
|
| 520 |
+
background-color: transparent;
|
| 521 |
+
}
|
| 522 |
+
.bg-white {
|
| 523 |
+
background-color: var(--color-white);
|
| 524 |
+
}
|
| 525 |
+
.bg-gradient-to-r {
|
| 526 |
+
--tw-gradient-position: to right in oklab;
|
| 527 |
+
background-image: linear-gradient(var(--tw-gradient-stops));
|
| 528 |
+
}
|
| 529 |
+
.from-primary {
|
| 530 |
+
--tw-gradient-from: var(--color-primary);
|
| 531 |
+
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
| 532 |
+
}
|
| 533 |
+
.to-\[\#00CCFF\] {
|
| 534 |
+
--tw-gradient-to: #00CCFF;
|
| 535 |
+
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
| 536 |
+
}
|
| 537 |
+
.bg-clip-text {
|
| 538 |
+
background-clip: text;
|
| 539 |
+
}
|
| 540 |
+
.p-3 {
|
| 541 |
+
padding: calc(var(--spacing) * 3);
|
| 542 |
+
}
|
| 543 |
+
.p-6 {
|
| 544 |
+
padding: calc(var(--spacing) * 6);
|
| 545 |
+
}
|
| 546 |
+
.px-3 {
|
| 547 |
+
padding-inline: calc(var(--spacing) * 3);
|
| 548 |
+
}
|
| 549 |
+
.px-4 {
|
| 550 |
+
padding-inline: calc(var(--spacing) * 4);
|
| 551 |
+
}
|
| 552 |
+
.px-6 {
|
| 553 |
+
padding-inline: calc(var(--spacing) * 6);
|
| 554 |
+
}
|
| 555 |
+
.py-1 {
|
| 556 |
+
padding-block: calc(var(--spacing) * 1);
|
| 557 |
+
}
|
| 558 |
+
.py-2 {
|
| 559 |
+
padding-block: calc(var(--spacing) * 2);
|
| 560 |
+
}
|
| 561 |
+
.py-10 {
|
| 562 |
+
padding-block: calc(var(--spacing) * 10);
|
| 563 |
+
}
|
| 564 |
+
.pr-12 {
|
| 565 |
+
padding-right: calc(var(--spacing) * 12);
|
| 566 |
+
}
|
| 567 |
+
.text-center {
|
| 568 |
+
text-align: center;
|
| 569 |
+
}
|
| 570 |
+
.text-left {
|
| 571 |
+
text-align: left;
|
| 572 |
+
}
|
| 573 |
+
.text-3xl {
|
| 574 |
+
font-size: var(--text-3xl);
|
| 575 |
+
line-height: var(--tw-leading, var(--text-3xl--line-height));
|
| 576 |
+
}
|
| 577 |
+
.text-base {
|
| 578 |
+
font-size: var(--text-base);
|
| 579 |
+
line-height: var(--tw-leading, var(--text-base--line-height));
|
| 580 |
+
}
|
| 581 |
+
.text-sm {
|
| 582 |
+
font-size: var(--text-sm);
|
| 583 |
+
line-height: var(--tw-leading, var(--text-sm--line-height));
|
| 584 |
+
}
|
| 585 |
+
.text-xl {
|
| 586 |
+
font-size: var(--text-xl);
|
| 587 |
+
line-height: var(--tw-leading, var(--text-xl--line-height));
|
| 588 |
+
}
|
| 589 |
+
.text-\[20px\] {
|
| 590 |
+
font-size: 20px;
|
| 591 |
+
}
|
| 592 |
+
.text-\[24px\] {
|
| 593 |
+
font-size: 24px;
|
| 594 |
+
}
|
| 595 |
+
.leading-\[1\.2\] {
|
| 596 |
+
--tw-leading: 1.2;
|
| 597 |
+
line-height: 1.2;
|
| 598 |
+
}
|
| 599 |
+
.font-bold {
|
| 600 |
+
--tw-font-weight: var(--font-weight-bold);
|
| 601 |
+
font-weight: var(--font-weight-bold);
|
| 602 |
+
}
|
| 603 |
+
.font-medium {
|
| 604 |
+
--tw-font-weight: var(--font-weight-medium);
|
| 605 |
+
font-weight: var(--font-weight-medium);
|
| 606 |
+
}
|
| 607 |
+
.font-semibold {
|
| 608 |
+
--tw-font-weight: var(--font-weight-semibold);
|
| 609 |
+
font-weight: var(--font-weight-semibold);
|
| 610 |
+
}
|
| 611 |
+
.overflow-ellipsis {
|
| 612 |
+
text-overflow: ellipsis;
|
| 613 |
+
}
|
| 614 |
+
.whitespace-nowrap {
|
| 615 |
+
white-space: nowrap;
|
| 616 |
+
}
|
| 617 |
+
.text-accent-foreground {
|
| 618 |
+
color: var(--color-accent-foreground);
|
| 619 |
+
}
|
| 620 |
+
.text-muted-foreground {
|
| 621 |
+
color: var(--color-muted-foreground);
|
| 622 |
+
}
|
| 623 |
+
.text-primary {
|
| 624 |
+
color: var(--color-primary);
|
| 625 |
+
}
|
| 626 |
+
.text-primary-foreground {
|
| 627 |
+
color: var(--color-primary-foreground);
|
| 628 |
+
}
|
| 629 |
+
.text-transparent {
|
| 630 |
+
color: transparent;
|
| 631 |
+
}
|
| 632 |
+
.opacity-0 {
|
| 633 |
+
opacity: 0%;
|
| 634 |
+
}
|
| 635 |
+
.shadow {
|
| 636 |
+
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
| 637 |
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
| 638 |
+
}
|
| 639 |
+
.shadow-inner {
|
| 640 |
+
--tw-shadow: inset 0 2px 4px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
|
| 641 |
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
| 642 |
+
}
|
| 643 |
+
.shadow-lg {
|
| 644 |
+
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
| 645 |
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
| 646 |
+
}
|
| 647 |
+
.shadow-xs {
|
| 648 |
+
--tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
|
| 649 |
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
| 650 |
+
}
|
| 651 |
+
.outline {
|
| 652 |
+
outline-style: var(--tw-outline-style);
|
| 653 |
+
outline-width: 1px;
|
| 654 |
+
}
|
| 655 |
+
.transition {
|
| 656 |
+
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
| 657 |
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
| 658 |
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
| 659 |
+
}
|
| 660 |
+
.transition-\[color\,box-shadow\] {
|
| 661 |
+
transition-property: color,box-shadow;
|
| 662 |
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
| 663 |
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
| 664 |
+
}
|
| 665 |
+
.transition-all {
|
| 666 |
+
transition-property: all;
|
| 667 |
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
| 668 |
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
| 669 |
+
}
|
| 670 |
+
.transition-colors {
|
| 671 |
+
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
|
| 672 |
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
| 673 |
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
| 674 |
+
}
|
| 675 |
+
.outline-none {
|
| 676 |
+
--tw-outline-style: none;
|
| 677 |
+
outline-style: none;
|
| 678 |
+
}
|
| 679 |
+
.selection\:bg-primary {
|
| 680 |
+
& *::selection {
|
| 681 |
+
background-color: var(--color-primary);
|
| 682 |
+
}
|
| 683 |
+
&::selection {
|
| 684 |
+
background-color: var(--color-primary);
|
| 685 |
+
}
|
| 686 |
+
}
|
| 687 |
+
.selection\:text-primary-foreground {
|
| 688 |
+
& *::selection {
|
| 689 |
+
color: var(--color-primary-foreground);
|
| 690 |
+
}
|
| 691 |
+
&::selection {
|
| 692 |
+
color: var(--color-primary-foreground);
|
| 693 |
+
}
|
| 694 |
+
}
|
| 695 |
+
.file\:inline-flex {
|
| 696 |
+
&::file-selector-button {
|
| 697 |
+
display: inline-flex;
|
| 698 |
+
}
|
| 699 |
+
}
|
| 700 |
+
.file\:h-7 {
|
| 701 |
+
&::file-selector-button {
|
| 702 |
+
height: calc(var(--spacing) * 7);
|
| 703 |
+
}
|
| 704 |
+
}
|
| 705 |
+
.file\:border-0 {
|
| 706 |
+
&::file-selector-button {
|
| 707 |
+
border-style: var(--tw-border-style);
|
| 708 |
+
border-width: 0px;
|
| 709 |
+
}
|
| 710 |
+
}
|
| 711 |
+
.file\:bg-transparent {
|
| 712 |
+
&::file-selector-button {
|
| 713 |
+
background-color: transparent;
|
| 714 |
+
}
|
| 715 |
+
}
|
| 716 |
+
.file\:text-sm {
|
| 717 |
+
&::file-selector-button {
|
| 718 |
+
font-size: var(--text-sm);
|
| 719 |
+
line-height: var(--tw-leading, var(--text-sm--line-height));
|
| 720 |
+
}
|
| 721 |
+
}
|
| 722 |
+
.file\:font-medium {
|
| 723 |
+
&::file-selector-button {
|
| 724 |
+
--tw-font-weight: var(--font-weight-medium);
|
| 725 |
+
font-weight: var(--font-weight-medium);
|
| 726 |
+
}
|
| 727 |
+
}
|
| 728 |
+
.file\:text-foreground {
|
| 729 |
+
&::file-selector-button {
|
| 730 |
+
color: var(--color-foreground);
|
| 731 |
+
}
|
| 732 |
+
}
|
| 733 |
+
.placeholder\:text-muted-foreground {
|
| 734 |
+
&::placeholder {
|
| 735 |
+
color: var(--color-muted-foreground);
|
| 736 |
+
}
|
| 737 |
+
}
|
| 738 |
+
.hover\:bg-accent {
|
| 739 |
+
&:hover {
|
| 740 |
+
@media (hover: hover) {
|
| 741 |
+
background-color: var(--color-accent);
|
| 742 |
+
}
|
| 743 |
+
}
|
| 744 |
+
}
|
| 745 |
+
.hover\:bg-transparent {
|
| 746 |
+
&:hover {
|
| 747 |
+
@media (hover: hover) {
|
| 748 |
+
background-color: transparent;
|
| 749 |
+
}
|
| 750 |
+
}
|
| 751 |
+
}
|
| 752 |
+
.hover\:text-accent-foreground {
|
| 753 |
+
&:hover {
|
| 754 |
+
@media (hover: hover) {
|
| 755 |
+
color: var(--color-accent-foreground);
|
| 756 |
+
}
|
| 757 |
+
}
|
| 758 |
+
}
|
| 759 |
+
.focus-visible\:border-ring {
|
| 760 |
+
&:focus-visible {
|
| 761 |
+
border-color: var(--color-ring);
|
| 762 |
+
}
|
| 763 |
+
}
|
| 764 |
+
.focus-visible\:ring-\[3px\] {
|
| 765 |
+
&:focus-visible {
|
| 766 |
+
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
| 767 |
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
| 768 |
+
}
|
| 769 |
+
}
|
| 770 |
+
.focus-visible\:ring-ring\/50 {
|
| 771 |
+
&:focus-visible {
|
| 772 |
+
--tw-ring-color: var(--color-ring);
|
| 773 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 774 |
+
--tw-ring-color: color-mix(in oklab, var(--color-ring) 50%, transparent);
|
| 775 |
+
}
|
| 776 |
+
}
|
| 777 |
+
}
|
| 778 |
+
.disabled\:pointer-events-none {
|
| 779 |
+
&:disabled {
|
| 780 |
+
pointer-events: none;
|
| 781 |
+
}
|
| 782 |
+
}
|
| 783 |
+
.disabled\:cursor-not-allowed {
|
| 784 |
+
&:disabled {
|
| 785 |
+
cursor: not-allowed;
|
| 786 |
+
}
|
| 787 |
+
}
|
| 788 |
+
.disabled\:opacity-50 {
|
| 789 |
+
&:disabled {
|
| 790 |
+
opacity: 50%;
|
| 791 |
+
}
|
| 792 |
+
}
|
| 793 |
+
.aria-invalid\:border-destructive {
|
| 794 |
+
&[aria-invalid="true"] {
|
| 795 |
+
border-color: var(--color-destructive);
|
| 796 |
+
}
|
| 797 |
+
}
|
| 798 |
+
.aria-invalid\:ring-destructive\/20 {
|
| 799 |
+
&[aria-invalid="true"] {
|
| 800 |
+
--tw-ring-color: var(--color-destructive);
|
| 801 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 802 |
+
--tw-ring-color: color-mix(in oklab, var(--color-destructive) 20%, transparent);
|
| 803 |
+
}
|
| 804 |
+
}
|
| 805 |
+
}
|
| 806 |
+
.sm\:px-6 {
|
| 807 |
+
@media (width >= 40rem) {
|
| 808 |
+
padding-inline: calc(var(--spacing) * 6);
|
| 809 |
+
}
|
| 810 |
+
}
|
| 811 |
+
.md\:mb-15 {
|
| 812 |
+
@media (width >= 48rem) {
|
| 813 |
+
margin-bottom: calc(var(--spacing) * 15);
|
| 814 |
+
}
|
| 815 |
+
}
|
| 816 |
+
.md\:text-5xl {
|
| 817 |
+
@media (width >= 48rem) {
|
| 818 |
+
font-size: var(--text-5xl);
|
| 819 |
+
line-height: var(--tw-leading, var(--text-5xl--line-height));
|
| 820 |
+
}
|
| 821 |
+
}
|
| 822 |
+
.md\:text-sm {
|
| 823 |
+
@media (width >= 48rem) {
|
| 824 |
+
font-size: var(--text-sm);
|
| 825 |
+
line-height: var(--tw-leading, var(--text-sm--line-height));
|
| 826 |
+
}
|
| 827 |
+
}
|
| 828 |
+
.lg\:px-8 {
|
| 829 |
+
@media (width >= 64rem) {
|
| 830 |
+
padding-inline: calc(var(--spacing) * 8);
|
| 831 |
+
}
|
| 832 |
+
}
|
| 833 |
+
.dark\:border-input {
|
| 834 |
+
&:is(.dark *) {
|
| 835 |
+
border-color: var(--color-input);
|
| 836 |
+
}
|
| 837 |
+
}
|
| 838 |
+
.dark\:bg-input\/30 {
|
| 839 |
+
&:is(.dark *) {
|
| 840 |
+
background-color: var(--color-input);
|
| 841 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 842 |
+
background-color: color-mix(in oklab, var(--color-input) 30%, transparent);
|
| 843 |
+
}
|
| 844 |
+
}
|
| 845 |
+
}
|
| 846 |
+
.dark\:hover\:bg-accent\/50 {
|
| 847 |
+
&:is(.dark *) {
|
| 848 |
+
&:hover {
|
| 849 |
+
@media (hover: hover) {
|
| 850 |
+
background-color: var(--color-accent);
|
| 851 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 852 |
+
background-color: color-mix(in oklab, var(--color-accent) 50%, transparent);
|
| 853 |
+
}
|
| 854 |
+
}
|
| 855 |
+
}
|
| 856 |
+
}
|
| 857 |
+
}
|
| 858 |
+
.dark\:hover\:bg-input\/50 {
|
| 859 |
+
&:is(.dark *) {
|
| 860 |
+
&:hover {
|
| 861 |
+
@media (hover: hover) {
|
| 862 |
+
background-color: var(--color-input);
|
| 863 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 864 |
+
background-color: color-mix(in oklab, var(--color-input) 50%, transparent);
|
| 865 |
+
}
|
| 866 |
+
}
|
| 867 |
+
}
|
| 868 |
+
}
|
| 869 |
+
}
|
| 870 |
+
.dark\:aria-invalid\:ring-destructive\/40 {
|
| 871 |
+
&:is(.dark *) {
|
| 872 |
+
&[aria-invalid="true"] {
|
| 873 |
+
--tw-ring-color: var(--color-destructive);
|
| 874 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 875 |
+
--tw-ring-color: color-mix(in oklab, var(--color-destructive) 40%, transparent);
|
| 876 |
+
}
|
| 877 |
+
}
|
| 878 |
+
}
|
| 879 |
+
}
|
| 880 |
+
.\[\&_td\]\:max-w-\[200px\] {
|
| 881 |
+
& td {
|
| 882 |
+
max-width: 200px;
|
| 883 |
+
}
|
| 884 |
+
}
|
| 885 |
+
.\[\&_td\]\:overflow-hidden {
|
| 886 |
+
& td {
|
| 887 |
+
overflow: hidden;
|
| 888 |
+
}
|
| 889 |
+
}
|
| 890 |
+
.\[\&_td\]\:px-3 {
|
| 891 |
+
& td {
|
| 892 |
+
padding-inline: calc(var(--spacing) * 3);
|
| 893 |
+
}
|
| 894 |
+
}
|
| 895 |
+
.\[\&_td\]\:py-2 {
|
| 896 |
+
& td {
|
| 897 |
+
padding-block: calc(var(--spacing) * 2);
|
| 898 |
+
}
|
| 899 |
+
}
|
| 900 |
+
.\[\&_td\]\:text-ellipsis {
|
| 901 |
+
& td {
|
| 902 |
+
text-overflow: ellipsis;
|
| 903 |
+
}
|
| 904 |
+
}
|
| 905 |
+
.\[\&_td\]\:whitespace-nowrap {
|
| 906 |
+
& td {
|
| 907 |
+
white-space: nowrap;
|
| 908 |
+
}
|
| 909 |
+
}
|
| 910 |
+
}
|
| 911 |
+
:root {
|
| 912 |
+
--radius: 0.625rem;
|
| 913 |
+
--background: oklch(1 0 0);
|
| 914 |
+
--foreground: oklch(0.141 0.005 285.823);
|
| 915 |
+
--card: oklch(1 0 0);
|
| 916 |
+
--card-foreground: oklch(0.141 0.005 285.823);
|
| 917 |
+
--popover: oklch(1 0 0);
|
| 918 |
+
--popover-foreground: oklch(0.141 0.005 285.823);
|
| 919 |
+
--primary: oklch(65.24% 0.199188 160.1355);
|
| 920 |
+
--primary-foreground: oklch(0.985 0 0);
|
| 921 |
+
--secondary: oklch(0.967 0.001 286.375);
|
| 922 |
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
| 923 |
+
--muted: oklch(0.967 0.001 286.375);
|
| 924 |
+
--muted-foreground: oklch(0.552 0.016 285.938);
|
| 925 |
+
--accent: oklch(0.967 0.001 286.375);
|
| 926 |
+
--accent-foreground: oklch(0.21 0.006 285.885);
|
| 927 |
+
--destructive: oklch(0.577 0.245 27.325);
|
| 928 |
+
--border: oklch(0.92 0.004 286.32);
|
| 929 |
+
--input: oklch(0.92 0.004 286.32);
|
| 930 |
+
--ring: oklch(0.705 0.015 286.067);
|
| 931 |
+
}
|
| 932 |
+
.dark {
|
| 933 |
+
--background: oklch(0.191 0.005 285.823);
|
| 934 |
+
--foreground: oklch(0.985 0 0);
|
| 935 |
+
--card: oklch(0.21 0.006 285.885);
|
| 936 |
+
--card-foreground: oklch(0.985 0 0);
|
| 937 |
+
--popover: oklch(0.21 0.006 285.885);
|
| 938 |
+
--popover-foreground: oklch(0.985 0 0);
|
| 939 |
+
--primary: oklch(88.24% 0.199188 160.1355);
|
| 940 |
+
--primary-foreground: oklch(0.21 0.006 285.885);
|
| 941 |
+
--secondary: oklch(0.244 0.006 286.033);
|
| 942 |
+
--secondary-foreground: oklch(0.985 0 0);
|
| 943 |
+
--muted: oklch(0.274 0.006 286.033);
|
| 944 |
+
--muted-foreground: oklch(0.705 0.015 286.067);
|
| 945 |
+
--accent: oklch(0.274 0.006 286.033);
|
| 946 |
+
--accent-foreground: oklch(0.985 0 0);
|
| 947 |
+
--destructive: oklch(0.704 0.191 22.216);
|
| 948 |
+
--border: oklch(1 0 0 / 10%);
|
| 949 |
+
--input: oklch(1 0 0 / 15%);
|
| 950 |
+
--ring: oklch(0.552 0.016 285.938);
|
| 951 |
+
}
|
| 952 |
+
@layer base {
|
| 953 |
+
* {
|
| 954 |
+
border-color: var(--color-border);
|
| 955 |
+
outline-color: var(--color-ring);
|
| 956 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 957 |
+
outline-color: color-mix(in oklab, var(--color-ring) 50%, transparent);
|
| 958 |
+
}
|
| 959 |
+
}
|
| 960 |
+
body {
|
| 961 |
+
background-color: var(--color-background);
|
| 962 |
+
color: var(--color-foreground);
|
| 963 |
+
}
|
| 964 |
+
}
|
| 965 |
+
.btn {
|
| 966 |
+
display: flex;
|
| 967 |
+
height: calc(var(--spacing) * 12);
|
| 968 |
+
min-width: 100px;
|
| 969 |
+
align-items: center;
|
| 970 |
+
justify-content: center;
|
| 971 |
+
gap: calc(var(--spacing) * 1.5);
|
| 972 |
+
border-radius: var(--radius-md);
|
| 973 |
+
background-color: var(--color-primary);
|
| 974 |
+
padding-inline: calc(var(--spacing) * 2);
|
| 975 |
+
padding-block: calc(var(--spacing) * 2);
|
| 976 |
+
font-size: var(--text-sm);
|
| 977 |
+
line-height: var(--tw-leading, var(--text-sm--line-height));
|
| 978 |
+
--tw-font-weight: var(--font-weight-semibold);
|
| 979 |
+
font-weight: var(--font-weight-semibold);
|
| 980 |
+
color: var(--color-primary-foreground);
|
| 981 |
+
transition-property: all;
|
| 982 |
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
| 983 |
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
| 984 |
+
&:hover {
|
| 985 |
+
@media (hover: hover) {
|
| 986 |
+
background-color: var(--color-primary);
|
| 987 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 988 |
+
background-color: color-mix(in oklab, var(--color-primary) 90%, transparent);
|
| 989 |
+
}
|
| 990 |
+
}
|
| 991 |
+
}
|
| 992 |
+
}
|
| 993 |
+
.btn.small {
|
| 994 |
+
height: calc(var(--spacing) * 8);
|
| 995 |
+
min-width: 70px;
|
| 996 |
+
font-size: var(--text-xs);
|
| 997 |
+
line-height: var(--tw-leading, var(--text-xs--line-height));
|
| 998 |
+
}
|
| 999 |
+
svg {
|
| 1000 |
+
display: inline;
|
| 1001 |
+
}
|
| 1002 |
+
#torrent_file_wrapper.drag-over {
|
| 1003 |
+
border-color: var(--color-primary);
|
| 1004 |
+
}
|
| 1005 |
+
#search-pagination {
|
| 1006 |
+
margin-block: calc(var(--spacing) * 4);
|
| 1007 |
+
display: flex;
|
| 1008 |
+
align-items: center;
|
| 1009 |
+
justify-content: center;
|
| 1010 |
+
gap: calc(var(--spacing) * 2);
|
| 1011 |
+
}
|
| 1012 |
+
#search-pagination.hidden {
|
| 1013 |
+
display: none !important;
|
| 1014 |
+
}
|
| 1015 |
+
#search-pagination button {
|
| 1016 |
+
display: flex;
|
| 1017 |
+
height: 32px;
|
| 1018 |
+
min-width: 32px;
|
| 1019 |
+
align-items: center;
|
| 1020 |
+
justify-content: center;
|
| 1021 |
+
border-radius: var(--radius-md);
|
| 1022 |
+
border-style: var(--tw-border-style);
|
| 1023 |
+
border-width: 1px;
|
| 1024 |
+
border-color: var(--color-foreground);
|
| 1025 |
+
@supports (color: color-mix(in lab, red, red)) {
|
| 1026 |
+
border-color: color-mix(in oklab, var(--color-foreground) 20%, transparent);
|
| 1027 |
+
}
|
| 1028 |
+
padding: calc(var(--spacing) * 1);
|
| 1029 |
+
font-size: var(--text-sm);
|
| 1030 |
+
line-height: var(--tw-leading, var(--text-sm--line-height));
|
| 1031 |
+
font-size: var(--text-xs);
|
| 1032 |
+
line-height: var(--tw-leading, var(--text-xs--line-height));
|
| 1033 |
+
--tw-font-weight: var(--font-weight-semibold);
|
| 1034 |
+
font-weight: var(--font-weight-semibold);
|
| 1035 |
+
transition-property: all;
|
| 1036 |
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
| 1037 |
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
| 1038 |
+
&:hover {
|
| 1039 |
+
@media (hover: hover) {
|
| 1040 |
+
background-color: var(--color-accent);
|
| 1041 |
+
}
|
| 1042 |
+
}
|
| 1043 |
+
}
|
| 1044 |
+
#search-pagination button svg {
|
| 1045 |
+
font-size: 17px;
|
| 1046 |
+
}
|
| 1047 |
+
#search-pagination button.disabled {
|
| 1048 |
+
cursor: not-allowed;
|
| 1049 |
+
&:hover {
|
| 1050 |
+
@media (hover: hover) {
|
| 1051 |
+
background-color: transparent;
|
| 1052 |
+
}
|
| 1053 |
+
}
|
| 1054 |
+
}
|
| 1055 |
+
#search-pagination button.active {
|
| 1056 |
+
background-color: var(--color-primary);
|
| 1057 |
+
color: var(--color-primary-foreground);
|
| 1058 |
+
}
|
| 1059 |
+
#search-pagination button.active:hover {
|
| 1060 |
+
background-color: var(--color-primary);
|
| 1061 |
+
color: var(--color-primary-foreground);
|
| 1062 |
+
}
|
| 1063 |
+
#video-player {
|
| 1064 |
+
display: none;
|
| 1065 |
+
}
|
| 1066 |
+
.btn.loader:before {
|
| 1067 |
+
content: "";
|
| 1068 |
+
display: block;
|
| 1069 |
+
width: 20px;
|
| 1070 |
+
height: 20px;
|
| 1071 |
+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBMaWNlbnNlOiBNSVQuIE1hZGUgYnkgTHVjaWRlIENvbnRyaWJ1dG9yczogaHR0cHM6Ly9sdWNpZGUuZGV2LyAtLT4KPHN2ZyAKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgd2lkdGg9IjI0IgogIGhlaWdodD0iMjQiCiAgdmlld0JveD0iMCAwIDI0IDI0IgogIGZpbGw9Im5vbmUiCiAgc3Ryb2tlPSIjZmZmZmZmIgogIHN0cm9rZS13aWR0aD0iMiIKICBzdHJva2UtbGluZWNhcD0icm91bmQiCiAgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIKPgogIDxwYXRoIGQ9Ik0yMSAxMmE5IDkgMCAxMS02LjIxOS04LjU2IiAvPgo8L3N2Zz4=);
|
| 1072 |
+
background-size: 20px;
|
| 1073 |
+
background-repeat: no-repeat;
|
| 1074 |
+
background-position: center;
|
| 1075 |
+
animation: spin 0.5s linear infinite;
|
| 1076 |
+
}
|
| 1077 |
+
.dark .btn.loader:before {
|
| 1078 |
+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBMaWNlbnNlOiBNSVQuIE1hZGUgYnkgTHVjaWRlIENvbnRyaWJ1dG9yczogaHR0cHM6Ly9sdWNpZGUuZGV2LyAtLT4KPHN2ZyAKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgd2lkdGg9IjI0IgogIGhlaWdodD0iMjQiCiAgdmlld0JveD0iMCAwIDI0IDI0IgogIGZpbGw9Im5vbmUiCiAgc3Ryb2tlPSIjMDAwMDAwIgogIHN0cm9rZS13aWR0aD0iMiIKICBzdHJva2UtbGluZWNhcD0icm91bmQiCiAgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIKPgogIDxwYXRoIGQ9Ik0yMSAxMmE5IDkgMCAxMS02LjIxOS04LjU2IiAvPgo8L3N2Zz4=);
|
| 1079 |
+
}
|
| 1080 |
+
@keyframes spin {
|
| 1081 |
+
0% {
|
| 1082 |
+
transform: rotate(0deg);
|
| 1083 |
+
}
|
| 1084 |
+
100% {
|
| 1085 |
+
transform: rotate(360deg);
|
| 1086 |
+
}
|
| 1087 |
+
}
|
| 1088 |
+
#toggle_theme {
|
| 1089 |
+
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='1em' height='1em' fill='none' aria-hidden='true' focusable='false'%3E%3Cg%3E%3Cpath d='M21.0672 11.8568L20.4253 11.469L21.0672 11.8568ZM12.1432 2.93276L11.7553 2.29085V2.29085L12.1432 2.93276ZM21.25 12C21.25 17.1086 17.1086 21.25 12 21.25V22.75C17.9371 22.75 22.75 17.9371 22.75 12H21.25ZM12 21.25C6.89137 21.25 2.75 17.1086 2.75 12H1.25C1.25 17.9371 6.06294 22.75 12 22.75V21.25ZM2.75 12C2.75 6.89137 6.89137 2.75 12 2.75V1.25C6.06294 1.25 1.25 6.06294 1.25 12H2.75ZM15.5 14.25C12.3244 14.25 9.75 11.6756 9.75 8.5H8.25C8.25 12.5041 11.4959 15.75 15.5 15.75V14.25ZM20.4253 11.469C19.4172 13.1373 17.5882 14.25 15.5 14.25V15.75C18.1349 15.75 20.4407 14.3439 21.7092 12.2447L20.4253 11.469ZM9.75 8.5C9.75 6.41182 10.8627 4.5828 12.531 3.57467L11.7553 2.29085C9.65609 3.5593 8.25 5.86509 8.25 8.5H9.75ZM12 2.75C11.9115 2.75 11.8077 2.71008 11.7324 2.63168C11.6686 2.56527 11.6538 2.50244 11.6503 2.47703C11.6461 2.44587 11.6482 2.35557 11.7553 2.29085L12.531 3.57467C13.0342 3.27065 13.196 2.71398 13.1368 2.27627C13.0754 1.82126 12.7166 1.25 12 1.25V2.75ZM21.7092 12.2447C21.6444 12.3518 21.5541 12.3539 21.523 12.3497C21.4976 12.3462 21.4347 12.3314 21.3683 12.2676C21.2899 12.1923 21.25 12.0885 21.25 12H22.75C22.75 11.2834 22.1787 10.9246 21.7237 10.8632C21.286 10.804 20.7293 10.9658 20.4253 11.469L21.7092 12.2447Z' fill='%23000' stroke-width='1.5'%3E%3C/path%3E%3C/g%3E%3C/svg%3E") center no-repeat;
|
| 1090 |
+
}
|
| 1091 |
+
.dark #toggle_theme {
|
| 1092 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='1em' height='1em' fill='none' aria-hidden='true' focusable='false'%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='5' stroke='%23fff' stroke-width='1.5'%3E%3C/circle%3E%3Cpath d='M12 2V4' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M12 20V22' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M4 12L2 12' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M22 12L20 12' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M19.7778 4.22266L17.5558 6.25424' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M4.22217 4.22266L6.44418 6.25424' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M6.44434 17.5557L4.22211 19.7779' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3Cpath d='M19.7778 19.7773L17.5558 17.5551' stroke='%23fff' stroke-width='1.5' stroke-linecap='round'%3E%3C/path%3E%3C/g%3E%3C/svg%3E");
|
| 1093 |
+
}
|
| 1094 |
+
button {
|
| 1095 |
+
cursor: pointer;
|
| 1096 |
+
transition: all 0.3s ease;
|
| 1097 |
+
}
|
| 1098 |
+
button:disabled {
|
| 1099 |
+
cursor: not-allowed;
|
| 1100 |
+
opacity: 0.6;
|
| 1101 |
+
}
|
| 1102 |
+
#video-player {
|
| 1103 |
+
min-height: 200px;
|
| 1104 |
+
}
|
| 1105 |
+
body .video-js .vjs-big-play-button {
|
| 1106 |
+
margin: 0;
|
| 1107 |
+
display: flex;
|
| 1108 |
+
width: 80px;
|
| 1109 |
+
height: 80px;
|
| 1110 |
+
transform: translate(-50%, -50%);
|
| 1111 |
+
align-items: center;
|
| 1112 |
+
justify-content: center;
|
| 1113 |
+
border-radius: 50%;
|
| 1114 |
+
border-color: var(--color-primary) !important;
|
| 1115 |
+
background-color: transparent !important;
|
| 1116 |
+
color: var(--color-primary);
|
| 1117 |
+
}
|
| 1118 |
+
body .vjs-has-started .vjs-big-play-button {
|
| 1119 |
+
display: none;
|
| 1120 |
+
}
|
| 1121 |
+
body .video-js .vjs-big-play-button .vjs-icon-placeholder {
|
| 1122 |
+
display: flex;
|
| 1123 |
+
}
|
| 1124 |
+
body .video-js .vjs-big-play-button .vjs-icon-placeholder:before {
|
| 1125 |
+
font-size: 47px;
|
| 1126 |
+
position: static;
|
| 1127 |
+
}
|
| 1128 |
+
.vjs-progress-control {
|
| 1129 |
+
position: absolute !important;
|
| 1130 |
+
bottom: 45px !important;
|
| 1131 |
+
width: calc(100% - 20px) !important;
|
| 1132 |
+
padding: 1px !important;
|
| 1133 |
+
height: 40px !important;
|
| 1134 |
+
}
|
| 1135 |
+
.video-js .vjs-progress-control .vjs-progress-holder, .video-js .vjs-play-progress, .video-js .vjs-load-progress div {
|
| 1136 |
+
border-radius: 50px;
|
| 1137 |
+
}
|
| 1138 |
+
.video-js .vjs-control-bar {
|
| 1139 |
+
background: transparent;
|
| 1140 |
+
padding: 0 10px 15px;
|
| 1141 |
+
height: auto;
|
| 1142 |
+
}
|
| 1143 |
+
.video-js .vjs-control-bar:before {
|
| 1144 |
+
content: "";
|
| 1145 |
+
position: absolute;
|
| 1146 |
+
width: 100%;
|
| 1147 |
+
height: 200%;
|
| 1148 |
+
bottom: 0;
|
| 1149 |
+
left: 0;
|
| 1150 |
+
background: linear-gradient( 0deg, rgba(0, 0, 0, 0.84) 0%, rgba(0, 0, 0, 0.59) 50%, rgba(0, 0, 0, 0) 100% );
|
| 1151 |
+
}
|
| 1152 |
+
.vjs-remaining-time {
|
| 1153 |
+
display: none;
|
| 1154 |
+
}
|
| 1155 |
+
.video-js .vjs-current-time, .video-js .vjs-duration, .vjs-live .vjs-time-control, .vjs-time-divider, .vjs-live .vjs-time-divider {
|
| 1156 |
+
display: block;
|
| 1157 |
+
}
|
| 1158 |
+
.video-js .vjs-time-control.vjs-duration {
|
| 1159 |
+
margin-right: auto;
|
| 1160 |
+
}
|
| 1161 |
+
.video-js .vjs-time-control {
|
| 1162 |
+
padding: 0 5px;
|
| 1163 |
+
font-size: 15px;
|
| 1164 |
+
line-height: 1.2;
|
| 1165 |
+
height: 33px;
|
| 1166 |
+
display: flex;
|
| 1167 |
+
align-items: center;
|
| 1168 |
+
}
|
| 1169 |
+
.video-js .vjs-time-control.vjs-time-divider {
|
| 1170 |
+
padding: 0;
|
| 1171 |
+
min-width: 0;
|
| 1172 |
+
font-size: 18px;
|
| 1173 |
+
line-height: 1;
|
| 1174 |
+
color: white;
|
| 1175 |
+
opacity: 1;
|
| 1176 |
+
z-index: 1;
|
| 1177 |
+
}
|
| 1178 |
+
.video-js .vjs-play-control {
|
| 1179 |
+
height: 33px;
|
| 1180 |
+
width: 33px;
|
| 1181 |
+
display: flex;
|
| 1182 |
+
align-items: center;
|
| 1183 |
+
justify-content: center;
|
| 1184 |
+
}
|
| 1185 |
+
.video-js .vjs-play-control .vjs-icon-placeholder {
|
| 1186 |
+
display: flex;
|
| 1187 |
+
align-items: center;
|
| 1188 |
+
justify-content: center;
|
| 1189 |
+
}
|
| 1190 |
+
.video-js .vjs-play-control .vjs-icon-placeholder:before {
|
| 1191 |
+
position: relative;
|
| 1192 |
+
line-height: 1;
|
| 1193 |
+
font-size: 30px;
|
| 1194 |
+
}
|
| 1195 |
+
.video-js .vjs-volume-panel .vjs-volume-control {
|
| 1196 |
+
opacity: 1;
|
| 1197 |
+
width: 60px !important;
|
| 1198 |
+
height: 33px;
|
| 1199 |
+
}
|
| 1200 |
+
.video-js .vjs-volume-panel {
|
| 1201 |
+
width: 110px !important;
|
| 1202 |
+
height: 33px;
|
| 1203 |
+
margin-left: 6px;
|
| 1204 |
+
}
|
| 1205 |
+
.video-js .vjs-mute-control {
|
| 1206 |
+
width: 33px;
|
| 1207 |
+
height: 33px;
|
| 1208 |
+
}
|
| 1209 |
+
.video-js .vjs-mute-control .vjs-icon-placeholder {
|
| 1210 |
+
display: flex;
|
| 1211 |
+
align-items: center;
|
| 1212 |
+
justify-content: center;
|
| 1213 |
+
}
|
| 1214 |
+
.video-js .vjs-mute-control .vjs-icon-placeholder:before {
|
| 1215 |
+
position: relative;
|
| 1216 |
+
line-height: 1;
|
| 1217 |
+
font-size: 25px;
|
| 1218 |
+
}
|
| 1219 |
+
.video-js .vjs-volume-bar {
|
| 1220 |
+
margin: 1.55em 0.45em;
|
| 1221 |
+
}
|
| 1222 |
+
.video-js .vjs-time-control.vjs-remaining-time {
|
| 1223 |
+
display: none;
|
| 1224 |
+
}
|
| 1225 |
+
.video-js .vjs-progress-control .vjs-progress-holder {
|
| 1226 |
+
margin: 0 6px;
|
| 1227 |
+
}
|
| 1228 |
+
.video-js .vjs-slider {
|
| 1229 |
+
background-color: rgb(255 255 255 / 27%);
|
| 1230 |
+
}
|
| 1231 |
+
.video-js .vjs-load-progress div {
|
| 1232 |
+
background: rgb(255 255 255 / 33%);
|
| 1233 |
+
}
|
| 1234 |
+
.video-js .vjs-fullscreen-control, .video-js .vjs-picture-in-picture-control, .video-js .vjs-subs-caps-button {
|
| 1235 |
+
height: 33px;
|
| 1236 |
+
width: 33px;
|
| 1237 |
+
display: flex;
|
| 1238 |
+
align-items: center;
|
| 1239 |
+
justify-content: center;
|
| 1240 |
+
}
|
| 1241 |
+
.video-js .vjs-fullscreen-control {
|
| 1242 |
+
margin-left: 6px;
|
| 1243 |
+
}
|
| 1244 |
+
.video-js .vjs-picture-in-picture-control {
|
| 1245 |
+
margin-left: 10px;
|
| 1246 |
+
}
|
| 1247 |
+
.video-js .vjs-fullscreen-control .vjs-icon-placeholder, .video-js .vjs-picture-in-picture-control .vjs-icon-placeholder, .video-js .vjs-subs-caps-button .vjs-icon-placeholder {
|
| 1248 |
+
display: flex;
|
| 1249 |
+
align-items: center;
|
| 1250 |
+
justify-content: center;
|
| 1251 |
+
}
|
| 1252 |
+
.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before, .video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before, .video-js .vjs-subs-caps-button .vjs-icon-placeholder:before {
|
| 1253 |
+
position: relative;
|
| 1254 |
+
line-height: 1;
|
| 1255 |
+
font-size: 28px;
|
| 1256 |
+
}
|
| 1257 |
+
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before {
|
| 1258 |
+
font-size: 24px;
|
| 1259 |
+
}
|
| 1260 |
+
.video-js .vjs-subs-caps-button .vjs-icon-placeholder:before {
|
| 1261 |
+
font-size: 26px;
|
| 1262 |
+
}
|
| 1263 |
+
.video-select {
|
| 1264 |
+
position: absolute;
|
| 1265 |
+
top: 15px;
|
| 1266 |
+
left: 13px;
|
| 1267 |
+
background: transparent;
|
| 1268 |
+
display: none;
|
| 1269 |
+
font-size: 15px;
|
| 1270 |
+
max-width: calc(100% - 25px);
|
| 1271 |
+
overflow: hidden;
|
| 1272 |
+
text-overflow: ellipsis;
|
| 1273 |
+
}
|
| 1274 |
+
.vjs-has-started.vjs-controls-enabled .video-select {
|
| 1275 |
+
display: block;
|
| 1276 |
+
}
|
| 1277 |
+
.video-js .vjs-progress-holder {
|
| 1278 |
+
height: 6px;
|
| 1279 |
+
transition: 0.3s all;
|
| 1280 |
+
}
|
| 1281 |
+
.video-js .vjs-play-progress:before {
|
| 1282 |
+
font-size: 17px;
|
| 1283 |
+
transition: 0.3s all;
|
| 1284 |
+
}
|
| 1285 |
+
.video-js .vjs-progress-control:hover .vjs-progress-holder {
|
| 1286 |
+
height: 8px;
|
| 1287 |
+
}
|
| 1288 |
+
.vjs-progress-control:hover .vjs-play-progress:before {
|
| 1289 |
+
font-size: 22px;
|
| 1290 |
+
}
|
| 1291 |
+
@media (max-width: 768px) {
|
| 1292 |
+
.video-js .vjs-control-bar {
|
| 1293 |
+
padding: 0 4px 5px;
|
| 1294 |
+
}
|
| 1295 |
+
.vjs-progress-control {
|
| 1296 |
+
bottom: 31px !important;
|
| 1297 |
+
width: calc(100% - 6px) !important;
|
| 1298 |
+
padding: 0px !important;
|
| 1299 |
+
height: 30px !important;
|
| 1300 |
+
}
|
| 1301 |
+
.video-js .vjs-play-control .vjs-icon-placeholder:before {
|
| 1302 |
+
font-size: 25px;
|
| 1303 |
+
}
|
| 1304 |
+
.video-js .vjs-play-control {
|
| 1305 |
+
width: 26px;
|
| 1306 |
+
height: 26px;
|
| 1307 |
+
}
|
| 1308 |
+
.video-js .vjs-volume-bar {
|
| 1309 |
+
margin: 1.15em 0.45em;
|
| 1310 |
+
}
|
| 1311 |
+
.video-js .vjs-volume-panel .vjs-volume-control {
|
| 1312 |
+
width: 45px !important;
|
| 1313 |
+
height: 26px;
|
| 1314 |
+
}
|
| 1315 |
+
.video-js .vjs-mute-control {
|
| 1316 |
+
width: 26px;
|
| 1317 |
+
height: 26px;
|
| 1318 |
+
}
|
| 1319 |
+
.video-js .vjs-volume-panel {
|
| 1320 |
+
width: 80px !important;
|
| 1321 |
+
height: 26px;
|
| 1322 |
+
margin-left: 4px;
|
| 1323 |
+
}
|
| 1324 |
+
.video-js .vjs-time-control {
|
| 1325 |
+
padding: 0 4px;
|
| 1326 |
+
font-size: 12px;
|
| 1327 |
+
height: 26px;
|
| 1328 |
+
}
|
| 1329 |
+
.video-js .vjs-time-control.vjs-time-divider {
|
| 1330 |
+
font-size: 15px;
|
| 1331 |
+
}
|
| 1332 |
+
.video-js .vjs-fullscreen-control, .video-js .vjs-picture-in-picture-control, .video-js .vjs-subs-caps-button {
|
| 1333 |
+
height: 26px;
|
| 1334 |
+
width: 26px;
|
| 1335 |
+
}
|
| 1336 |
+
.video-js .vjs-fullscreen-control .vjs-icon-placeholder:before, .video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before, .video-js .vjs-subs-caps-button .vjs-icon-placeholder:before {
|
| 1337 |
+
font-size: 20px;
|
| 1338 |
+
}
|
| 1339 |
+
.video-js .vjs-picture-in-picture-control .vjs-icon-placeholder:before {
|
| 1340 |
+
font-size: 18px;
|
| 1341 |
+
}
|
| 1342 |
+
}
|
| 1343 |
+
@property --tw-translate-x {
|
| 1344 |
+
syntax: "*";
|
| 1345 |
+
inherits: false;
|
| 1346 |
+
initial-value: 0;
|
| 1347 |
+
}
|
| 1348 |
+
@property --tw-translate-y {
|
| 1349 |
+
syntax: "*";
|
| 1350 |
+
inherits: false;
|
| 1351 |
+
initial-value: 0;
|
| 1352 |
+
}
|
| 1353 |
+
@property --tw-translate-z {
|
| 1354 |
+
syntax: "*";
|
| 1355 |
+
inherits: false;
|
| 1356 |
+
initial-value: 0;
|
| 1357 |
+
}
|
| 1358 |
+
@property --tw-divide-y-reverse {
|
| 1359 |
+
syntax: "*";
|
| 1360 |
+
inherits: false;
|
| 1361 |
+
initial-value: 0;
|
| 1362 |
+
}
|
| 1363 |
+
@property --tw-border-style {
|
| 1364 |
+
syntax: "*";
|
| 1365 |
+
inherits: false;
|
| 1366 |
+
initial-value: solid;
|
| 1367 |
+
}
|
| 1368 |
+
@property --tw-gradient-position {
|
| 1369 |
+
syntax: "*";
|
| 1370 |
+
inherits: false;
|
| 1371 |
+
}
|
| 1372 |
+
@property --tw-gradient-from {
|
| 1373 |
+
syntax: "<color>";
|
| 1374 |
+
inherits: false;
|
| 1375 |
+
initial-value: #0000;
|
| 1376 |
+
}
|
| 1377 |
+
@property --tw-gradient-via {
|
| 1378 |
+
syntax: "<color>";
|
| 1379 |
+
inherits: false;
|
| 1380 |
+
initial-value: #0000;
|
| 1381 |
+
}
|
| 1382 |
+
@property --tw-gradient-to {
|
| 1383 |
+
syntax: "<color>";
|
| 1384 |
+
inherits: false;
|
| 1385 |
+
initial-value: #0000;
|
| 1386 |
+
}
|
| 1387 |
+
@property --tw-gradient-stops {
|
| 1388 |
+
syntax: "*";
|
| 1389 |
+
inherits: false;
|
| 1390 |
+
}
|
| 1391 |
+
@property --tw-gradient-via-stops {
|
| 1392 |
+
syntax: "*";
|
| 1393 |
+
inherits: false;
|
| 1394 |
+
}
|
| 1395 |
+
@property --tw-gradient-from-position {
|
| 1396 |
+
syntax: "<length-percentage>";
|
| 1397 |
+
inherits: false;
|
| 1398 |
+
initial-value: 0%;
|
| 1399 |
+
}
|
| 1400 |
+
@property --tw-gradient-via-position {
|
| 1401 |
+
syntax: "<length-percentage>";
|
| 1402 |
+
inherits: false;
|
| 1403 |
+
initial-value: 50%;
|
| 1404 |
+
}
|
| 1405 |
+
@property --tw-gradient-to-position {
|
| 1406 |
+
syntax: "<length-percentage>";
|
| 1407 |
+
inherits: false;
|
| 1408 |
+
initial-value: 100%;
|
| 1409 |
+
}
|
| 1410 |
+
@property --tw-leading {
|
| 1411 |
+
syntax: "*";
|
| 1412 |
+
inherits: false;
|
| 1413 |
+
}
|
| 1414 |
+
@property --tw-font-weight {
|
| 1415 |
+
syntax: "*";
|
| 1416 |
+
inherits: false;
|
| 1417 |
+
}
|
| 1418 |
+
@property --tw-shadow {
|
| 1419 |
+
syntax: "*";
|
| 1420 |
+
inherits: false;
|
| 1421 |
+
initial-value: 0 0 #0000;
|
| 1422 |
+
}
|
| 1423 |
+
@property --tw-shadow-color {
|
| 1424 |
+
syntax: "*";
|
| 1425 |
+
inherits: false;
|
| 1426 |
+
}
|
| 1427 |
+
@property --tw-shadow-alpha {
|
| 1428 |
+
syntax: "<percentage>";
|
| 1429 |
+
inherits: false;
|
| 1430 |
+
initial-value: 100%;
|
| 1431 |
+
}
|
| 1432 |
+
@property --tw-inset-shadow {
|
| 1433 |
+
syntax: "*";
|
| 1434 |
+
inherits: false;
|
| 1435 |
+
initial-value: 0 0 #0000;
|
| 1436 |
+
}
|
| 1437 |
+
@property --tw-inset-shadow-color {
|
| 1438 |
+
syntax: "*";
|
| 1439 |
+
inherits: false;
|
| 1440 |
+
}
|
| 1441 |
+
@property --tw-inset-shadow-alpha {
|
| 1442 |
+
syntax: "<percentage>";
|
| 1443 |
+
inherits: false;
|
| 1444 |
+
initial-value: 100%;
|
| 1445 |
+
}
|
| 1446 |
+
@property --tw-ring-color {
|
| 1447 |
+
syntax: "*";
|
| 1448 |
+
inherits: false;
|
| 1449 |
+
}
|
| 1450 |
+
@property --tw-ring-shadow {
|
| 1451 |
+
syntax: "*";
|
| 1452 |
+
inherits: false;
|
| 1453 |
+
initial-value: 0 0 #0000;
|
| 1454 |
+
}
|
| 1455 |
+
@property --tw-inset-ring-color {
|
| 1456 |
+
syntax: "*";
|
| 1457 |
+
inherits: false;
|
| 1458 |
+
}
|
| 1459 |
+
@property --tw-inset-ring-shadow {
|
| 1460 |
+
syntax: "*";
|
| 1461 |
+
inherits: false;
|
| 1462 |
+
initial-value: 0 0 #0000;
|
| 1463 |
+
}
|
| 1464 |
+
@property --tw-ring-inset {
|
| 1465 |
+
syntax: "*";
|
| 1466 |
+
inherits: false;
|
| 1467 |
+
}
|
| 1468 |
+
@property --tw-ring-offset-width {
|
| 1469 |
+
syntax: "<length>";
|
| 1470 |
+
inherits: false;
|
| 1471 |
+
initial-value: 0px;
|
| 1472 |
+
}
|
| 1473 |
+
@property --tw-ring-offset-color {
|
| 1474 |
+
syntax: "*";
|
| 1475 |
+
inherits: false;
|
| 1476 |
+
initial-value: #fff;
|
| 1477 |
+
}
|
| 1478 |
+
@property --tw-ring-offset-shadow {
|
| 1479 |
+
syntax: "*";
|
| 1480 |
+
inherits: false;
|
| 1481 |
+
initial-value: 0 0 #0000;
|
| 1482 |
+
}
|
| 1483 |
+
@property --tw-outline-style {
|
| 1484 |
+
syntax: "*";
|
| 1485 |
+
inherits: false;
|
| 1486 |
+
initial-value: solid;
|
| 1487 |
+
}
|
| 1488 |
+
@keyframes spin {
|
| 1489 |
+
to {
|
| 1490 |
+
transform: rotate(360deg);
|
| 1491 |
+
}
|
| 1492 |
+
}
|
| 1493 |
+
@layer properties {
|
| 1494 |
+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
| 1495 |
+
*, ::before, ::after, ::backdrop {
|
| 1496 |
+
--tw-translate-x: 0;
|
| 1497 |
+
--tw-translate-y: 0;
|
| 1498 |
+
--tw-translate-z: 0;
|
| 1499 |
+
--tw-divide-y-reverse: 0;
|
| 1500 |
+
--tw-border-style: solid;
|
| 1501 |
+
--tw-gradient-position: initial;
|
| 1502 |
+
--tw-gradient-from: #0000;
|
| 1503 |
+
--tw-gradient-via: #0000;
|
| 1504 |
+
--tw-gradient-to: #0000;
|
| 1505 |
+
--tw-gradient-stops: initial;
|
| 1506 |
+
--tw-gradient-via-stops: initial;
|
| 1507 |
+
--tw-gradient-from-position: 0%;
|
| 1508 |
+
--tw-gradient-via-position: 50%;
|
| 1509 |
+
--tw-gradient-to-position: 100%;
|
| 1510 |
+
--tw-leading: initial;
|
| 1511 |
+
--tw-font-weight: initial;
|
| 1512 |
+
--tw-shadow: 0 0 #0000;
|
| 1513 |
+
--tw-shadow-color: initial;
|
| 1514 |
+
--tw-shadow-alpha: 100%;
|
| 1515 |
+
--tw-inset-shadow: 0 0 #0000;
|
| 1516 |
+
--tw-inset-shadow-color: initial;
|
| 1517 |
+
--tw-inset-shadow-alpha: 100%;
|
| 1518 |
+
--tw-ring-color: initial;
|
| 1519 |
+
--tw-ring-shadow: 0 0 #0000;
|
| 1520 |
+
--tw-inset-ring-color: initial;
|
| 1521 |
+
--tw-inset-ring-shadow: 0 0 #0000;
|
| 1522 |
+
--tw-ring-inset: initial;
|
| 1523 |
+
--tw-ring-offset-width: 0px;
|
| 1524 |
+
--tw-ring-offset-color: #fff;
|
| 1525 |
+
--tw-ring-offset-shadow: 0 0 #0000;
|
| 1526 |
+
--tw-outline-style: solid;
|
| 1527 |
+
}
|
| 1528 |
+
}
|
| 1529 |
+
}
|
client/assets/videojs.hotkeys.min.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* videojs-hotkeys v0.2.30 - https://github.com/ctd1500/videojs-hotkeys */
|
| 2 |
+
(t=>{var e;"undefined"!=typeof window&&window.videojs?t(window.videojs):"function"==typeof define&&define.amd?define("videojs-hotkeys",["video.js"],function(e){return t(e.default||e)}):"undefined"!=typeof module&&module.exports&&(e=require("video.js"),module.exports=t(e.default||e))})(function(I){"undefined"!=typeof window&&(window.videojs_hotkeys={version:"0.2.30"});(I.registerPlugin||I.plugin)("hotkeys",function(a){function e(e){var t;t=u?0:y.activeElement,s.controls()&&(q||t==m||t==m.querySelector(".vjs-tech")||t==m.querySelector(".iframeblocker")||t==m.querySelector(".vjs-control-bar")||x)&&r&&(e=window.event||e,t=Math.max(-1,Math.min(1,e.wheelDelta||-e.detail)),e.preventDefault(),1==t?s.volume(s.volume()+k):-1==t&&s.volume(s.volume()-k))}var t,s=this,m=s.el(),y=document,d=1,v=2,f=3,p=4,b=5,h=6,w=7,n=I.obj?.merge||I.mergeOptions||I.util.mergeOptions,k=(a=n({volumeStep:.1,seekStep:5,enableMute:!0,enableVolumeScroll:!0,enableHoverScroll:!1,enableFullscreen:!0,enableNumbers:!0,enableJogStyle:!1,alwaysCaptureHotkeys:!1,captureDocumentHotkeys:!1,documentHotkeysFocusElementFilter:()=>!1,enableModifiersForNumbers:!0,enableInactiveFocus:!0,skipInitialFocus:!1,playPauseKey:function(e){return 32===e.which||179===e.which},rewindKey:function(e){return 37===e.which||177===e.which},forwardKey:function(e){return 39===e.which||176===e.which},volumeUpKey:function(e){return 38===e.which},volumeDownKey:function(e){return 40===e.which},muteKey:function(e){return 77===e.which},fullscreenKey:function(e){return 70===e.which},customKeys:{}},a||{})).volumeStep,o=a.seekStep,S=a.enableMute,r=a.enableVolumeScroll,u=a.enableHoverScroll,K=a.enableFullscreen,F=a.enableNumbers,j=a.enableJogStyle,q=a.alwaysCaptureHotkeys,E=a.captureDocumentHotkeys,T=a.documentHotkeysFocusElementFilter,g=a.enableModifiersForNumbers,n=a.enableInactiveFocus,l=a.skipInitialFocus,i=I.VERSION,c=(m.hasAttribute("tabIndex")||m.setAttribute("tabIndex","-1"),m.style.outline="none",!q&&s.autoplay()||l||s.one("play",()=>{m.focus()}),n&&s.on("userinactive",()=>{var o=()=>{clearTimeout(e)},e=setTimeout(()=>{s.off("useractive",o);var e=y.activeElement,t=e.parentElement,n=m.querySelector(".vjs-control-bar");!e||n!=t&&n!=t.parentElement||m.focus()},10);s.one("useractive",o)}),s.on("play",()=>{var e=m.querySelector(".iframeblocker");e&&""===e.style.display&&(e.style.display="block",e.style.bottom="39px")}),function(e){var t=e.which,n=e.preventDefault.bind(e),o=s.duration();if(s.controls()){var r,u,l,i=y.activeElement;if(q||E&&T(i)||i==m||i==m.querySelector(".vjs-tech")||i==m.querySelector(".vjs-control-bar")||i==m.querySelector(".iframeblocker"))switch(D(e,s)){case d:n(),(q||E)&&e.stopPropagation(),s.paused()?M(s.play()):s.pause();break;case v:r=!s.paused(),n(),r&&s.pause(),u=s.currentTime()-H(e),s.currentTime(u=u<=0?0:u),r&&M(s.play());break;case f:r=!s.paused(),n(),r&&s.pause(),u=s.currentTime()+H(e),s.currentTime(u=o<=u?r?o-.001:o:u),r&&M(s.play());break;case b:n(),j?(u=s.currentTime()-1,s.currentTime()<=1&&(u=0),s.currentTime(u)):s.volume(s.volume()-k);break;case p:n(),j?(u=s.currentTime()+1,s.currentTime(u=o<=u?o:u)):s.volume(s.volume()+k);break;case h:S&&s.muted(!s.muted());break;case w:K&&(s.isFullscreen()?s.exitFullscreen():s.requestFullscreen());break;default:for(l in!(47<t&&t<59||95<t&&t<106)||!g&&(e.metaKey||e.ctrlKey||e.altKey)||F&&(r=48,u=t-(r=95<t?96:r),n(),s.currentTime(s.duration()*u*.1)),a.customKeys){var c=a.customKeys[l];c&&c.key&&c.handler&&c.key(e)&&(n(),c.handler(s,a,e))}}}}),x=!1,l=m.querySelector(".vjs-volume-menu-button")||m.querySelector(".vjs-volume-panel"),D=(null!=l&&(l.addEventListener("mouseover",()=>{x=!0}),l.addEventListener("mouseout",()=>{x=!1})),function(e,t){return a.playPauseKey(e,t)?d:a.rewindKey(e,t)?v:a.forwardKey(e,t)?f:a.volumeUpKey(e,t)?p:a.volumeDownKey(e,t)?b:a.muteKey(e,t)?h:a.fullscreenKey(e,t)?w:void 0});function H(e){return"function"==typeof o?o(e):o}function M(e){null!=e&&"function"==typeof e.then&&e.then(null,function(e){})}return E?(t=function(e){c(e)},document.addEventListener("keydown",t),this.dispose=()=>{document.removeEventListener("keydown",t)}):s.on("keydown",c),s.on("dblclick",function(e){null!=i&&i<="7.1.0"&&(!s.controls()||(e=e.relatedTarget||e.toElement||y.activeElement)!=m&&e!=m.querySelector(".vjs-tech")&&e!=m.querySelector(".iframeblocker")||K&&(s.isFullscreen()?s.exitFullscreen():s.requestFullscreen()))}),s.on("mousewheel",e),s.on("DOMMouseScroll",e),this})});
|
client/index.html
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>BitPlay - Stream Torrents Inside the Browser</title>
|
| 7 |
+
<meta
|
| 8 |
+
name="description"
|
| 9 |
+
content="BitPlay is a web-based torrent streaming application that allows you to stream torrents directly in your browser."
|
| 10 |
+
/>
|
| 11 |
+
<link rel="icon" href="assets/favicon.png" type="image/png" />
|
| 12 |
+
<link
|
| 13 |
+
href="https://vjs.zencdn.net/8.10.0/video-js.min.css"
|
| 14 |
+
rel="stylesheet"
|
| 15 |
+
/>
|
| 16 |
+
<link rel="stylesheet" href="assets/butterup.min.css" />
|
| 17 |
+
<link rel="stylesheet" href="assets/output.css" />
|
| 18 |
+
<script>
|
| 19 |
+
// set theme mode
|
| 20 |
+
if (localStorage.getItem("theme") === "dark") {
|
| 21 |
+
document.documentElement.classList.add("dark");
|
| 22 |
+
} else {
|
| 23 |
+
document.documentElement.classList.add("light");
|
| 24 |
+
}
|
| 25 |
+
</script>
|
| 26 |
+
</head>
|
| 27 |
+
|
| 28 |
+
<body>
|
| 29 |
+
<main
|
| 30 |
+
class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-10 flex flex-col items-center"
|
| 31 |
+
>
|
| 32 |
+
<div class="flex justify-between w-full gap-4 mb-8 md:mb-15">
|
| 33 |
+
<div class="flex items-center gap-3">
|
| 34 |
+
<img src="assets/bitplay_logo.png" alt="logo" class="h-12 w-auto" />
|
| 35 |
+
<span class="text-[24px] font-semibold">bitplay</span>
|
| 36 |
+
</div>
|
| 37 |
+
<div class="flex items-center gap-4">
|
| 38 |
+
<button
|
| 39 |
+
class="inline-flex items-center cursor-pointer justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 size-10"
|
| 40 |
+
type="button"
|
| 41 |
+
id="toggle_theme"
|
| 42 |
+
></button>
|
| 43 |
+
<button
|
| 44 |
+
class="inline-flex h-10 px-3 items-center cursor-pointer justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
|
| 45 |
+
type="button"
|
| 46 |
+
id="settings-btn"
|
| 47 |
+
>
|
| 48 |
+
<svg
|
| 49 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 50 |
+
width="24"
|
| 51 |
+
height="24"
|
| 52 |
+
viewBox="0 0 24 24"
|
| 53 |
+
fill="none"
|
| 54 |
+
stroke="currentColor"
|
| 55 |
+
stroke-width="2"
|
| 56 |
+
stroke-linecap="round"
|
| 57 |
+
stroke-linejoin="round"
|
| 58 |
+
class="lucide lucide-settings2 w-4 h-4"
|
| 59 |
+
>
|
| 60 |
+
<path d="M20 7h-9"></path>
|
| 61 |
+
<path d="M14 17H5"></path>
|
| 62 |
+
<circle cx="17" cy="17" r="3"></circle>
|
| 63 |
+
<circle cx="7" cy="7" r="3"></circle>
|
| 64 |
+
</svg>
|
| 65 |
+
Settings
|
| 66 |
+
</button>
|
| 67 |
+
</div>
|
| 68 |
+
</div>
|
| 69 |
+
<h1
|
| 70 |
+
class="mb-8 text-3xl leading-[1.2] md:text-5xl max-w-xl text-center font-semibold bg-gradient-to-r from-primary to-[#00CCFF] text-transparent bg-clip-text"
|
| 71 |
+
>
|
| 72 |
+
Stream torrents with ease
|
| 73 |
+
</h1>
|
| 74 |
+
<div id="search-wrapper" class="hidden w-full">
|
| 75 |
+
<form id="search-form" class="w-full flex items-center gap-3">
|
| 76 |
+
<input
|
| 77 |
+
class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-12 overflow-ellipsis overflow-hidden"
|
| 78 |
+
placeholder="Search..."
|
| 79 |
+
id="search"
|
| 80 |
+
name="search"
|
| 81 |
+
type="search"
|
| 82 |
+
/>
|
| 83 |
+
<button class="btn" type="submit">Search</button>
|
| 84 |
+
</form>
|
| 85 |
+
<div
|
| 86 |
+
id="search-result"
|
| 87 |
+
class="mt-4 border rounded-lg overflow-auto max-h-[500px] bg-accent/50 hidden"
|
| 88 |
+
>
|
| 89 |
+
<table class="w-full caption-bottom text-sm">
|
| 90 |
+
<thead>
|
| 91 |
+
<tr class="bg-muted text-muted-foreground border-b">
|
| 92 |
+
<th class="p-3 text-left">Name</th>
|
| 93 |
+
<th class="p-3 text-left">Indexer</th>
|
| 94 |
+
<th class="p-3 text-left">Size</th>
|
| 95 |
+
<th class="p-3 text-left">Seeders</th>
|
| 96 |
+
<th class="p-3 text-left w-[10px]"></th>
|
| 97 |
+
</tr>
|
| 98 |
+
</thead>
|
| 99 |
+
<tbody
|
| 100 |
+
id="search-result-body"
|
| 101 |
+
class="divide-y [&_td]:px-3 [&_td]:py-2 [&_td]:whitespace-nowrap [&_td]:max-w-[200px] [&_td]:overflow-hidden [&_td]:text-ellipsis"
|
| 102 |
+
>
|
| 103 |
+
<tr>
|
| 104 |
+
<td colspan="5" class="py-10 px-6 text-center">
|
| 105 |
+
No results found
|
| 106 |
+
</td>
|
| 107 |
+
</tr>
|
| 108 |
+
</tbody>
|
| 109 |
+
<tfoot class="hidden">
|
| 110 |
+
<tr>
|
| 111 |
+
<td colspan="5" class="py-10 px-6 text-center">
|
| 112 |
+
No results found
|
| 113 |
+
</td>
|
| 114 |
+
</tr>
|
| 115 |
+
</tfoot>
|
| 116 |
+
</table>
|
| 117 |
+
</div>
|
| 118 |
+
<div id="search-pagination" class="hidden"></div>
|
| 119 |
+
<div
|
| 120 |
+
class="w-full my-4 text-center text-sm font-semibold text-accent-foreground"
|
| 121 |
+
>
|
| 122 |
+
OR
|
| 123 |
+
</div>
|
| 124 |
+
</div>
|
| 125 |
+
<form id="torrent-form" class="w-full flex items-center gap-3">
|
| 126 |
+
<div class="relative w-full">
|
| 127 |
+
<input
|
| 128 |
+
class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-12 pr-12 overflow-ellipsis overflow-hidden"
|
| 129 |
+
placeholder="Magnet link"
|
| 130 |
+
id="magnet"
|
| 131 |
+
name="magnet"
|
| 132 |
+
/>
|
| 133 |
+
<button
|
| 134 |
+
id="copy_magnet"
|
| 135 |
+
class="inline-flex items-center cursor-pointer justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 size-10 absolute right-0 top-0 h-full w-12"
|
| 136 |
+
type="button"
|
| 137 |
+
>
|
| 138 |
+
<svg
|
| 139 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 140 |
+
width="24"
|
| 141 |
+
height="24"
|
| 142 |
+
viewBox="0 0 24 24"
|
| 143 |
+
fill="none"
|
| 144 |
+
stroke="currentColor"
|
| 145 |
+
stroke-width="2"
|
| 146 |
+
stroke-linecap="round"
|
| 147 |
+
stroke-linejoin="round"
|
| 148 |
+
class="lucide lucide-clipboard-paste size-5"
|
| 149 |
+
>
|
| 150 |
+
<path
|
| 151 |
+
d="M15 2H9a1 1 0 0 0-1 1v2c0 .6.4 1 1 1h6c.6 0 1-.4 1-1V3c0-.6-.4-1-1-1Z"
|
| 152 |
+
></path>
|
| 153 |
+
<path
|
| 154 |
+
d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2M16 4h2a2 2 0 0 1 2 2v2M11 14h10"
|
| 155 |
+
></path>
|
| 156 |
+
<path d="m17 10 4 4-4 4"></path>
|
| 157 |
+
</svg>
|
| 158 |
+
</button>
|
| 159 |
+
</div>
|
| 160 |
+
<button class="btn" type="submit">Play Now</button>
|
| 161 |
+
</form>
|
| 162 |
+
<div
|
| 163 |
+
id="demo_torrent"
|
| 164 |
+
class="w-full rounded-lg p-3 text-center flex items-center gap-2 justify-center text-sm font-semibold text-accent-foreground border-2 mt-4 border-primary bg-secondary/50 cursor-pointer hover:bg-transparent transition-all"
|
| 165 |
+
>
|
| 166 |
+
<svg
|
| 167 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 168 |
+
width="24"
|
| 169 |
+
height="24"
|
| 170 |
+
viewBox="0 0 24 24"
|
| 171 |
+
fill="none"
|
| 172 |
+
stroke="currentColor"
|
| 173 |
+
stroke-width="2"
|
| 174 |
+
stroke-linecap="round"
|
| 175 |
+
stroke-linejoin="round"
|
| 176 |
+
class="lucide lucide-video w-6 h-6 inline-block"
|
| 177 |
+
>
|
| 178 |
+
<path
|
| 179 |
+
d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5"
|
| 180 |
+
></path>
|
| 181 |
+
<rect x="2" y="6" width="14" height="12" rx="2"></rect>
|
| 182 |
+
</svg>
|
| 183 |
+
Try Demo with Sintel (CC Movie)
|
| 184 |
+
</div>
|
| 185 |
+
<div
|
| 186 |
+
id="torrent_file_wrapper"
|
| 187 |
+
class="w-full relative rounded-lg p-3 py-10 text-center flex flex-col items-center gap-4 justify-center text-sm font-semibold text-accent-foreground mt-7 border-2 border-foreground/30 border-dashed bg-secondary/50 cursor-pointer hover:bg-transparent transition-all"
|
| 188 |
+
>
|
| 189 |
+
<svg
|
| 190 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 191 |
+
width="24"
|
| 192 |
+
height="24"
|
| 193 |
+
viewBox="0 0 24 24"
|
| 194 |
+
fill="none"
|
| 195 |
+
stroke="currentColor"
|
| 196 |
+
stroke-width="2"
|
| 197 |
+
stroke-linecap="round"
|
| 198 |
+
stroke-linejoin="round"
|
| 199 |
+
class="lucide lucide-upload size-12 text-primary"
|
| 200 |
+
>
|
| 201 |
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
| 202 |
+
<polyline points="17 8 12 3 7 8"></polyline>
|
| 203 |
+
<line x1="12" x2="12" y1="3" y2="15"></line>
|
| 204 |
+
</svg>
|
| 205 |
+
<span class="text-sm text-accent-foreground font-bold"
|
| 206 |
+
>Drag and drop your torrent file here, or
|
| 207 |
+
<span class="text-primary">Browse</span>
|
| 208 |
+
</span>
|
| 209 |
+
<input
|
| 210 |
+
type="file"
|
| 211 |
+
class="opacity-0 size-full z-[1] absolute cursor-pointer"
|
| 212 |
+
accept=".torrent"
|
| 213 |
+
id="torrent_file"
|
| 214 |
+
/>
|
| 215 |
+
</div>
|
| 216 |
+
<video id="video-player" class="video-js mt-10 w-full"></video>
|
| 217 |
+
</main>
|
| 218 |
+
<div id="settings-model" class="fixed inset-0 z-[999] flex flex-col hidden">
|
| 219 |
+
<!-- model overlay -->
|
| 220 |
+
<div id="close-settings" class="absolute inset-0 bg-black/70"></div>
|
| 221 |
+
<!-- model content -->
|
| 222 |
+
<div
|
| 223 |
+
class="p-6 bg-background rounded-xl shadow-lg w-full max-w-lg m-auto border z-[2]"
|
| 224 |
+
>
|
| 225 |
+
<div class="flex items-center justify-between mb-6">
|
| 226 |
+
<h1 class="text-xl font-semibold">Settings</h1>
|
| 227 |
+
<button id="close-settings" type="button">
|
| 228 |
+
<svg
|
| 229 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 230 |
+
width="24"
|
| 231 |
+
height="24"
|
| 232 |
+
viewBox="0 0 24 24"
|
| 233 |
+
fill="none"
|
| 234 |
+
stroke="currentColor"
|
| 235 |
+
stroke-width="2"
|
| 236 |
+
stroke-linecap="round"
|
| 237 |
+
stroke-linejoin="round"
|
| 238 |
+
class="lucide lucide-x w-5 h-5"
|
| 239 |
+
>
|
| 240 |
+
<path d="M18 6 6 18"></path>
|
| 241 |
+
<path d="m6 6 12 12"></path>
|
| 242 |
+
</svg>
|
| 243 |
+
</button>
|
| 244 |
+
</div>
|
| 245 |
+
<div class="flex gap-2 mb-7">
|
| 246 |
+
<button
|
| 247 |
+
class="tab-btn flex items-center gap-2 px-4 py-2 rounded-lg transition-colors border bg-primary text-primary-foreground"
|
| 248 |
+
data-index="0"
|
| 249 |
+
type="button"
|
| 250 |
+
>
|
| 251 |
+
<svg
|
| 252 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 253 |
+
width="24"
|
| 254 |
+
height="24"
|
| 255 |
+
viewBox="0 0 24 24"
|
| 256 |
+
fill="none"
|
| 257 |
+
stroke="currentColor"
|
| 258 |
+
stroke-width="2"
|
| 259 |
+
stroke-linecap="round"
|
| 260 |
+
stroke-linejoin="round"
|
| 261 |
+
class="lucide lucide-globe w-4 h-4"
|
| 262 |
+
>
|
| 263 |
+
<circle cx="12" cy="12" r="10"></circle>
|
| 264 |
+
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"></path>
|
| 265 |
+
<path d="M2 12h20"></path>
|
| 266 |
+
</svg>
|
| 267 |
+
<span>Proxy</span>
|
| 268 |
+
</button>
|
| 269 |
+
<button
|
| 270 |
+
class="tab-btn flex items-center gap-2 px-4 py-2 rounded-lg transition-colors bg-muted border"
|
| 271 |
+
data-index="1"
|
| 272 |
+
type="button"
|
| 273 |
+
>
|
| 274 |
+
<svg
|
| 275 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 276 |
+
width="24"
|
| 277 |
+
height="24"
|
| 278 |
+
viewBox="0 0 24 24"
|
| 279 |
+
fill="none"
|
| 280 |
+
stroke="currentColor"
|
| 281 |
+
stroke-width="2"
|
| 282 |
+
stroke-linecap="round"
|
| 283 |
+
stroke-linejoin="round"
|
| 284 |
+
class="lucide lucide-search w-4 h-4"
|
| 285 |
+
>
|
| 286 |
+
<circle cx="11" cy="11" r="8"></circle>
|
| 287 |
+
<path d="m21 21-4.3-4.3"></path>
|
| 288 |
+
</svg>
|
| 289 |
+
<span>Prowlarr</span>
|
| 290 |
+
</button>
|
| 291 |
+
<button
|
| 292 |
+
class="tab-btn flex items-center gap-2 px-4 py-2 rounded-lg transition-colors bg-muted border"
|
| 293 |
+
data-index="2"
|
| 294 |
+
type="button"
|
| 295 |
+
>
|
| 296 |
+
<svg
|
| 297 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 298 |
+
width="24"
|
| 299 |
+
height="24"
|
| 300 |
+
viewBox="0 0 24 24"
|
| 301 |
+
fill="none"
|
| 302 |
+
stroke="currentColor"
|
| 303 |
+
stroke-width="2"
|
| 304 |
+
stroke-linecap="round"
|
| 305 |
+
stroke-linejoin="round"
|
| 306 |
+
class="lucide lucide-server w-4 h-4"
|
| 307 |
+
>
|
| 308 |
+
<rect width="20" height="8" x="2" y="2" rx="2" ry="2"></rect>
|
| 309 |
+
<rect width="20" height="8" x="2" y="14" rx="2" ry="2"></rect>
|
| 310 |
+
<line x1="6" x2="6.01" y1="6" y2="6"></line>
|
| 311 |
+
<line x1="6" x2="6.01" y1="18" y2="18"></line>
|
| 312 |
+
</svg>
|
| 313 |
+
<span>Jackett</span>
|
| 314 |
+
</button>
|
| 315 |
+
</div>
|
| 316 |
+
<form
|
| 317 |
+
id="proxy-settings-form"
|
| 318 |
+
data-tab="0"
|
| 319 |
+
class="tab flex flex-col gap-6"
|
| 320 |
+
>
|
| 321 |
+
<label
|
| 322 |
+
id="switchInput"
|
| 323 |
+
for="enableProxy"
|
| 324 |
+
class="flex items-center cursor-pointer"
|
| 325 |
+
>
|
| 326 |
+
<input id="enableProxy" type="checkbox" class="sr-only" />
|
| 327 |
+
<div
|
| 328 |
+
class="w-11 switch-wrapper h-6 border bg-accent rounded-full shadow-inner relative transition"
|
| 329 |
+
>
|
| 330 |
+
<div
|
| 331 |
+
class="dot absolute size-5 bg-white rounded-full shadow left-[1px] top-[1px] transition"
|
| 332 |
+
></div>
|
| 333 |
+
</div>
|
| 334 |
+
<span class="ml-3 text-muted-foreground"> Enable Proxy </span>
|
| 335 |
+
</label>
|
| 336 |
+
<div class="flex flex-col gap-2">
|
| 337 |
+
<label
|
| 338 |
+
for="proxy"
|
| 339 |
+
class="text-muted-foreground text-sm font-semibold"
|
| 340 |
+
>Enter a SOCKS5 Proxy URL
|
| 341 |
+
</label>
|
| 342 |
+
<input
|
| 343 |
+
type="text"
|
| 344 |
+
id="proxyUrl"
|
| 345 |
+
placeholder="socks5://username:password@host:port"
|
| 346 |
+
class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-10 pr-12 overflow-ellipsis overflow-hidden"
|
| 347 |
+
/>
|
| 348 |
+
</div>
|
| 349 |
+
<div id="proxy-result" class="hidden items-center gap-2">
|
| 350 |
+
<span class="text-muted-foreground">IP Address:</span
|
| 351 |
+
><span class="output-ip text-primary"></span>
|
| 352 |
+
</div>
|
| 353 |
+
<div class="flex gap-2">
|
| 354 |
+
<button
|
| 355 |
+
type="button"
|
| 356 |
+
id="test-proxy"
|
| 357 |
+
class="flex items-center justify-center gap-2 px-4 py-2 flex-1 rounded-lg transition-colors bg-muted border font-medium text-sm"
|
| 358 |
+
>
|
| 359 |
+
<svg
|
| 360 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 361 |
+
width="24"
|
| 362 |
+
height="24"
|
| 363 |
+
viewBox="0 0 24 24"
|
| 364 |
+
fill="none"
|
| 365 |
+
stroke="currentColor"
|
| 366 |
+
stroke-width="2"
|
| 367 |
+
stroke-linecap="round"
|
| 368 |
+
stroke-linejoin="round"
|
| 369 |
+
class="lucide lucide-test-tube2 w-4 h-4"
|
| 370 |
+
>
|
| 371 |
+
<path
|
| 372 |
+
d="M21 7 6.82 21.18a2.83 2.83 0 0 1-3.99-.01v0a2.83 2.83 0 0 1 0-4L17 3"
|
| 373 |
+
></path>
|
| 374 |
+
<path d="m16 2 6 6"></path>
|
| 375 |
+
<path d="M12 16H4"></path>
|
| 376 |
+
</svg>
|
| 377 |
+
<span>Test Proxy</span>
|
| 378 |
+
</button>
|
| 379 |
+
<button type="submit" class="btn flex-1 !h-11">
|
| 380 |
+
Save Settings
|
| 381 |
+
</button>
|
| 382 |
+
</div>
|
| 383 |
+
</form>
|
| 384 |
+
<form
|
| 385 |
+
id="prowlarr-settings-form"
|
| 386 |
+
data-tab="1"
|
| 387 |
+
class="tab flex flex-col gap-6 hidden"
|
| 388 |
+
>
|
| 389 |
+
<label
|
| 390 |
+
id="switchInput"
|
| 391 |
+
for="enableProwlarr"
|
| 392 |
+
class="flex items-center cursor-pointer"
|
| 393 |
+
>
|
| 394 |
+
<input id="enableProwlarr" type="checkbox" class="sr-only" />
|
| 395 |
+
<div
|
| 396 |
+
class="w-11 switch-wrapper h-6 border bg-accent rounded-full shadow-inner relative transition"
|
| 397 |
+
>
|
| 398 |
+
<div
|
| 399 |
+
class="dot absolute size-5 bg-white rounded-full shadow left-[1px] top-[1px] transition"
|
| 400 |
+
></div>
|
| 401 |
+
</div>
|
| 402 |
+
<span class="ml-3 text-muted-foreground"> Enable Prowlarr </span>
|
| 403 |
+
</label>
|
| 404 |
+
<div class="flex flex-col gap-2">
|
| 405 |
+
<label
|
| 406 |
+
for="prowlarrHost"
|
| 407 |
+
class="text-muted-foreground text-sm font-semibold"
|
| 408 |
+
>Prowlarr Host</label
|
| 409 |
+
>
|
| 410 |
+
<input
|
| 411 |
+
type="text"
|
| 412 |
+
id="prowlarrHost"
|
| 413 |
+
placeholder="http://localhost:9117"
|
| 414 |
+
class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-10 pr-12 overflow-ellipsis overflow-hidden"
|
| 415 |
+
/>
|
| 416 |
+
</div>
|
| 417 |
+
<div class="flex flex-col gap-2">
|
| 418 |
+
<label
|
| 419 |
+
for="prowlarrApiKey"
|
| 420 |
+
class="text-muted-foreground text-sm font-semibold"
|
| 421 |
+
>API Key</label
|
| 422 |
+
>
|
| 423 |
+
<input
|
| 424 |
+
type="text"
|
| 425 |
+
id="prowlarrApiKey"
|
| 426 |
+
placeholder="Your Prowlarr API key"
|
| 427 |
+
class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-10 pr-12 overflow-ellipsis overflow-hidden"
|
| 428 |
+
/>
|
| 429 |
+
</div>
|
| 430 |
+
<div class="flex gap-2">
|
| 431 |
+
<button
|
| 432 |
+
type="button"
|
| 433 |
+
id="test-prowlarr"
|
| 434 |
+
class="flex items-center justify-center gap-2 px-4 py-2 flex-1 rounded-lg transition-colors bg-muted border font-medium text-sm"
|
| 435 |
+
>
|
| 436 |
+
<svg
|
| 437 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 438 |
+
width="24"
|
| 439 |
+
height="24"
|
| 440 |
+
viewBox="0 0 24 24"
|
| 441 |
+
fill="none"
|
| 442 |
+
stroke="currentColor"
|
| 443 |
+
stroke-width="2"
|
| 444 |
+
stroke-linecap="round"
|
| 445 |
+
stroke-linejoin="round"
|
| 446 |
+
class="lucide lucide-test-tube2 w-4 h-4"
|
| 447 |
+
>
|
| 448 |
+
<path
|
| 449 |
+
d="M21 7 6.82 21.18a2.83 2.83 0 0 1-3.99-.01v0a2.83 2.83 0 0 1 0-4L17 3"
|
| 450 |
+
></path>
|
| 451 |
+
<path d="m16 2 6 6"></path>
|
| 452 |
+
<path d="M12 16H4"></path>
|
| 453 |
+
</svg>
|
| 454 |
+
<span>Test Connection</span>
|
| 455 |
+
</button>
|
| 456 |
+
<button type="submit" class="btn flex-1 !h-11">
|
| 457 |
+
Save Settings
|
| 458 |
+
</button>
|
| 459 |
+
</div>
|
| 460 |
+
</form>
|
| 461 |
+
<form
|
| 462 |
+
id="jackett-settings-form"
|
| 463 |
+
data-tab="2"
|
| 464 |
+
class="tab flex flex-col gap-6 hidden"
|
| 465 |
+
>
|
| 466 |
+
<label
|
| 467 |
+
id="switchInput"
|
| 468 |
+
for="enableJackett"
|
| 469 |
+
class="flex items-center cursor-pointer"
|
| 470 |
+
>
|
| 471 |
+
<input id="enableJackett" type="checkbox" class="sr-only" />
|
| 472 |
+
<div
|
| 473 |
+
class="w-11 switch-wrapper h-6 border bg-accent rounded-full shadow-inner relative transition"
|
| 474 |
+
>
|
| 475 |
+
<div
|
| 476 |
+
class="dot absolute size-5 bg-white rounded-full shadow left-[1px] top-[1px] transition"
|
| 477 |
+
></div>
|
| 478 |
+
</div>
|
| 479 |
+
<span class="ml-3 text-muted-foreground"> Enable Jackett </span>
|
| 480 |
+
</label>
|
| 481 |
+
<div class="flex flex-col gap-2">
|
| 482 |
+
<label
|
| 483 |
+
for="jackettHost"
|
| 484 |
+
class="text-muted-foreground text-sm font-semibold"
|
| 485 |
+
>Jackett Host</label
|
| 486 |
+
>
|
| 487 |
+
<input
|
| 488 |
+
type="text"
|
| 489 |
+
id="jackettHost"
|
| 490 |
+
placeholder="http://localhost:9117"
|
| 491 |
+
class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-10 pr-12 overflow-ellipsis overflow-hidden"
|
| 492 |
+
/>
|
| 493 |
+
</div>
|
| 494 |
+
<div class="flex flex-col gap-2">
|
| 495 |
+
<label
|
| 496 |
+
for="jackettApiKey"
|
| 497 |
+
class="text-muted-foreground text-sm font-semibold"
|
| 498 |
+
>API Key</label
|
| 499 |
+
>
|
| 500 |
+
<input
|
| 501 |
+
type="text"
|
| 502 |
+
id="jackettApiKey"
|
| 503 |
+
placeholder="Your Jackett API key"
|
| 504 |
+
class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive h-10 pr-12 overflow-ellipsis overflow-hidden"
|
| 505 |
+
/>
|
| 506 |
+
</div>
|
| 507 |
+
<div class="flex gap-2">
|
| 508 |
+
<button
|
| 509 |
+
type="button"
|
| 510 |
+
id="test-jackett"
|
| 511 |
+
class="flex items-center justify-center gap-2 px-4 py-2 flex-1 rounded-lg transition-colors bg-muted border font-medium text-sm"
|
| 512 |
+
>
|
| 513 |
+
<svg
|
| 514 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 515 |
+
width="24"
|
| 516 |
+
height="24"
|
| 517 |
+
viewBox="0 0 24 24"
|
| 518 |
+
fill="none"
|
| 519 |
+
stroke="currentColor"
|
| 520 |
+
stroke-width="2"
|
| 521 |
+
stroke-linecap="round"
|
| 522 |
+
stroke-linejoin="round"
|
| 523 |
+
class="lucide lucide-test-tube2 w-4 h-4"
|
| 524 |
+
>
|
| 525 |
+
<path
|
| 526 |
+
d="M21 7 6.82 21.18a2.83 2.83 0 0 1-3.99-.01v0a2.83 2.83 0 0 1 0-4L17 3"
|
| 527 |
+
></path>
|
| 528 |
+
<path d="m16 2 6 6"></path>
|
| 529 |
+
<path d="M12 16H4"></path>
|
| 530 |
+
</svg>
|
| 531 |
+
<span>Test Connection</span>
|
| 532 |
+
</button>
|
| 533 |
+
<button type="submit" class="btn flex-1 !h-11">
|
| 534 |
+
Save Settings
|
| 535 |
+
</button>
|
| 536 |
+
</div>
|
| 537 |
+
</form>
|
| 538 |
+
</div>
|
| 539 |
+
</div>
|
| 540 |
+
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
|
| 541 |
+
<script src="assets/videojs.hotkeys.min.js"></script>
|
| 542 |
+
<script src="assets/butterup.min.js"></script>
|
| 543 |
+
<script type="module" src="assets/index.js"></script>
|
| 544 |
+
</body>
|
| 545 |
+
</html>
|
go.mod
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
module torrent-stream
|
| 2 |
+
|
| 3 |
+
go 1.24.0
|
| 4 |
+
|
| 5 |
+
require (
|
| 6 |
+
github.com/anacrolix/torrent v1.58.1
|
| 7 |
+
golang.org/x/net v0.38.0
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
require (
|
| 11 |
+
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
| 12 |
+
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
|
| 13 |
+
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
|
| 14 |
+
github.com/anacrolix/chansync v0.4.1-0.20240627045151-1aa1ac392fe8 // indirect
|
| 15 |
+
github.com/anacrolix/dht/v2 v2.19.2-0.20221121215055-066ad8494444 // indirect
|
| 16 |
+
github.com/anacrolix/envpprof v1.3.0 // indirect
|
| 17 |
+
github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca // indirect
|
| 18 |
+
github.com/anacrolix/go-libutp v1.3.2 // indirect
|
| 19 |
+
github.com/anacrolix/log v0.15.3-0.20240627045001-cd912c641d83 // indirect
|
| 20 |
+
github.com/anacrolix/missinggo v1.3.0 // indirect
|
| 21 |
+
github.com/anacrolix/missinggo/perf v1.0.0 // indirect
|
| 22 |
+
github.com/anacrolix/missinggo/v2 v2.7.4 // indirect
|
| 23 |
+
github.com/anacrolix/mmsg v1.0.1 // indirect
|
| 24 |
+
github.com/anacrolix/multiless v0.4.0 // indirect
|
| 25 |
+
github.com/anacrolix/stm v0.4.0 // indirect
|
| 26 |
+
github.com/anacrolix/sync v0.5.1 // indirect
|
| 27 |
+
github.com/anacrolix/upnp v0.1.4 // indirect
|
| 28 |
+
github.com/anacrolix/utp v0.1.0 // indirect
|
| 29 |
+
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
| 30 |
+
github.com/benbjohnson/immutable v0.3.0 // indirect
|
| 31 |
+
github.com/bits-and-blooms/bitset v1.2.2 // indirect
|
| 32 |
+
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect
|
| 33 |
+
github.com/cespare/xxhash v1.1.0 // indirect
|
| 34 |
+
github.com/davecgh/go-spew v1.1.1 // indirect
|
| 35 |
+
github.com/dustin/go-humanize v1.0.0 // indirect
|
| 36 |
+
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
| 37 |
+
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 // indirect
|
| 38 |
+
github.com/go-llsqlite/crawshaw v0.5.2-0.20240425034140-f30eb7704568 // indirect
|
| 39 |
+
github.com/go-logr/logr v1.2.3 // indirect
|
| 40 |
+
github.com/go-logr/stdr v1.2.2 // indirect
|
| 41 |
+
github.com/google/btree v1.1.2 // indirect
|
| 42 |
+
github.com/google/uuid v1.6.0 // indirect
|
| 43 |
+
github.com/gorilla/websocket v1.5.0 // indirect
|
| 44 |
+
github.com/huandu/xstrings v1.3.2 // indirect
|
| 45 |
+
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
| 46 |
+
github.com/mattn/go-isatty v0.0.16 // indirect
|
| 47 |
+
github.com/minio/sha256-simd v1.0.0 // indirect
|
| 48 |
+
github.com/mr-tron/base58 v1.2.0 // indirect
|
| 49 |
+
github.com/mschoch/smat v0.2.0 // indirect
|
| 50 |
+
github.com/multiformats/go-multihash v0.2.3 // indirect
|
| 51 |
+
github.com/multiformats/go-varint v0.0.6 // indirect
|
| 52 |
+
github.com/pion/datachannel v1.5.9 // indirect
|
| 53 |
+
github.com/pion/dtls/v3 v3.0.3 // indirect
|
| 54 |
+
github.com/pion/ice/v4 v4.0.2 // indirect
|
| 55 |
+
github.com/pion/interceptor v0.1.37 // indirect
|
| 56 |
+
github.com/pion/logging v0.2.2 // indirect
|
| 57 |
+
github.com/pion/mdns/v2 v2.0.7 // indirect
|
| 58 |
+
github.com/pion/randutil v0.1.0 // indirect
|
| 59 |
+
github.com/pion/rtcp v1.2.14 // indirect
|
| 60 |
+
github.com/pion/rtp v1.8.9 // indirect
|
| 61 |
+
github.com/pion/sctp v1.8.33 // indirect
|
| 62 |
+
github.com/pion/sdp/v3 v3.0.9 // indirect
|
| 63 |
+
github.com/pion/srtp/v3 v3.0.4 // indirect
|
| 64 |
+
github.com/pion/stun/v3 v3.0.0 // indirect
|
| 65 |
+
github.com/pion/transport/v3 v3.0.7 // indirect
|
| 66 |
+
github.com/pion/turn/v4 v4.0.0 // indirect
|
| 67 |
+
github.com/pion/webrtc/v4 v4.0.0 // indirect
|
| 68 |
+
github.com/pkg/errors v0.9.1 // indirect
|
| 69 |
+
github.com/protolambda/ctxlock v0.1.0 // indirect
|
| 70 |
+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
| 71 |
+
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect
|
| 72 |
+
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
| 73 |
+
github.com/tidwall/btree v1.6.0 // indirect
|
| 74 |
+
github.com/wlynxg/anet v0.0.3 // indirect
|
| 75 |
+
go.etcd.io/bbolt v1.3.6 // indirect
|
| 76 |
+
go.opentelemetry.io/otel v1.11.1 // indirect
|
| 77 |
+
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
| 78 |
+
golang.org/x/crypto v0.36.0 // indirect
|
| 79 |
+
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
|
| 80 |
+
golang.org/x/sync v0.8.0 // indirect
|
| 81 |
+
golang.org/x/sys v0.31.0 // indirect
|
| 82 |
+
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
|
| 83 |
+
lukechampine.com/blake3 v1.1.6 // indirect
|
| 84 |
+
modernc.org/libc v1.22.3 // indirect
|
| 85 |
+
modernc.org/mathutil v1.5.0 // indirect
|
| 86 |
+
modernc.org/memory v1.5.0 // indirect
|
| 87 |
+
modernc.org/sqlite v1.21.1 // indirect
|
| 88 |
+
zombiezen.com/go/sqlite v0.13.1 // indirect
|
| 89 |
+
)
|
go.sum
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
| 2 |
+
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
| 3 |
+
crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk=
|
| 4 |
+
crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
|
| 5 |
+
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
|
| 6 |
+
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
| 7 |
+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
| 8 |
+
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
| 9 |
+
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
| 10 |
+
github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
|
| 11 |
+
github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI=
|
| 12 |
+
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
| 13 |
+
github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVOVmhWBY=
|
| 14 |
+
github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
|
| 15 |
+
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
| 16 |
+
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
| 17 |
+
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 h1:byYvvbfSo3+9efR4IeReh77gVs4PnNDR3AMOE9NJ7a0=
|
| 18 |
+
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0/go.mod h1:q37NoqncT41qKc048STsifIt69LfUJ8SrWWcz/yam5k=
|
| 19 |
+
github.com/alecthomas/assert/v2 v2.0.0-alpha3 h1:pcHeMvQ3OMstAWgaeaXIAL8uzB9xMm2zlxt+/4ml8lk=
|
| 20 |
+
github.com/alecthomas/assert/v2 v2.0.0-alpha3/go.mod h1:+zD0lmDXTeQj7TgDgCt0ePWxb0hMC1G+PGTsTCv1B9o=
|
| 21 |
+
github.com/alecthomas/atomic v0.1.0-alpha2 h1:dqwXmax66gXvHhsOS4pGPZKqYOlTkapELkLb3MNdlH8=
|
| 22 |
+
github.com/alecthomas/atomic v0.1.0-alpha2/go.mod h1:zD6QGEyw49HIq19caJDc2NMXAy8rNi9ROrxtMXATfyI=
|
| 23 |
+
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 h1:8Uy0oSf5co/NZXje7U1z8Mpep++QJOldL2hs/sBQf48=
|
| 24 |
+
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
| 25 |
+
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
| 26 |
+
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
| 27 |
+
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
| 28 |
+
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
| 29 |
+
github.com/anacrolix/chansync v0.4.1-0.20240627045151-1aa1ac392fe8 h1:eyb0bBaQKMOh5Se/Qg54shijc8K4zpQiOjEhKFADkQM=
|
| 30 |
+
github.com/anacrolix/chansync v0.4.1-0.20240627045151-1aa1ac392fe8/go.mod h1:DZsatdsdXxD0WiwcGl0nJVwyjCKMDv+knl1q2iBjA2k=
|
| 31 |
+
github.com/anacrolix/dht/v2 v2.19.2-0.20221121215055-066ad8494444 h1:8V0K09lrGoeT2KRJNOtspA7q+OMxGwQqK/Ug0IiaaRE=
|
| 32 |
+
github.com/anacrolix/dht/v2 v2.19.2-0.20221121215055-066ad8494444/go.mod h1:MctKM1HS5YYDb3F30NGJxLE+QPuqWoT5ReW/4jt8xew=
|
| 33 |
+
github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
|
| 34 |
+
github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
|
| 35 |
+
github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
|
| 36 |
+
github.com/anacrolix/envpprof v1.3.0 h1:WJt9bpuT7A/CDCxPOv/eeZqHWlle/Y0keJUvc6tcJDk=
|
| 37 |
+
github.com/anacrolix/envpprof v1.3.0/go.mod h1:7QIG4CaX1uexQ3tqd5+BRa/9e2D02Wcertl6Yh0jCB0=
|
| 38 |
+
github.com/anacrolix/generics v0.0.0-20230113004304-d6428d516633/go.mod h1:ff2rHB/joTV03aMSSn/AZNnaIpUw0h3njetGsaXcMy8=
|
| 39 |
+
github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca h1:aiiGqSQWjtVNdi8zUMfA//IrM8fPkv2bWwZVPbDe0wg=
|
| 40 |
+
github.com/anacrolix/generics v0.0.3-0.20240902042256-7fb2702ef0ca/go.mod h1:MN3ve08Z3zSV/rTuX/ouI4lNdlfTxgdafQJiLzyNRB8=
|
| 41 |
+
github.com/anacrolix/go-libutp v1.3.2 h1:WswiaxTIogchbkzNgGHuHRfbrYLpv4o290mlvcx+++M=
|
| 42 |
+
github.com/anacrolix/go-libutp v1.3.2/go.mod h1:fCUiEnXJSe3jsPG554A200Qv+45ZzIIyGEvE56SHmyA=
|
| 43 |
+
github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
|
| 44 |
+
github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
|
| 45 |
+
github.com/anacrolix/log v0.13.1/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68=
|
| 46 |
+
github.com/anacrolix/log v0.14.2/go.mod h1:1OmJESOtxQGNMlUO5rcv96Vpp9mfMqXXbe2RdinFLdY=
|
| 47 |
+
github.com/anacrolix/log v0.15.3-0.20240627045001-cd912c641d83 h1:9o/yVzzLzYaBDFx8B27yhkvBLhNnRAuSTK7Y+yZKVtU=
|
| 48 |
+
github.com/anacrolix/log v0.15.3-0.20240627045001-cd912c641d83/go.mod h1:xvHjsYWWP7yO8PZwtuIp/k0DBlu07pSJqH4SEC78Vwc=
|
| 49 |
+
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM=
|
| 50 |
+
github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM=
|
| 51 |
+
github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
|
| 52 |
+
github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
|
| 53 |
+
github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
|
| 54 |
+
github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
|
| 55 |
+
github.com/anacrolix/missinggo v1.3.0 h1:06HlMsudotL7BAELRZs0yDZ4yVXsHXGi323QBjAVASw=
|
| 56 |
+
github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc=
|
| 57 |
+
github.com/anacrolix/missinggo/perf v1.0.0 h1:7ZOGYziGEBytW49+KmYGTaNfnwUqP1HBsy6BqESAJVw=
|
| 58 |
+
github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
|
| 59 |
+
github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY=
|
| 60 |
+
github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA=
|
| 61 |
+
github.com/anacrolix/missinggo/v2 v2.7.4 h1:47h5OXoPV8JbA/ACA+FLwKdYbAinuDO8osc2Cu9xkxg=
|
| 62 |
+
github.com/anacrolix/missinggo/v2 v2.7.4/go.mod h1:vVO5FEziQm+NFmJesc7StpkquZk+WJFCaL0Wp//2sa0=
|
| 63 |
+
github.com/anacrolix/mmsg v1.0.1 h1:TxfpV7kX70m3f/O7ielL/2I3OFkMPjrRCPo7+4X5AWw=
|
| 64 |
+
github.com/anacrolix/mmsg v1.0.1/go.mod h1:x8kRaJY/dCrY9Al0PEcj1mb/uFHwP6GCJ9fLl4thEPc=
|
| 65 |
+
github.com/anacrolix/multiless v0.4.0 h1:lqSszHkliMsZd2hsyrDvHOw4AbYWa+ijQ66LzbjqWjM=
|
| 66 |
+
github.com/anacrolix/multiless v0.4.0/go.mod h1:zJv1JF9AqdZiHwxqPgjuOZDGWER6nyE48WBCi/OOrMM=
|
| 67 |
+
github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
|
| 68 |
+
github.com/anacrolix/stm v0.4.0 h1:tOGvuFwaBjeu1u9X1eIh9TX8OEedEiEQ1se1FjhFnXY=
|
| 69 |
+
github.com/anacrolix/stm v0.4.0/go.mod h1:GCkwqWoAsP7RfLW+jw+Z0ovrt2OO7wRzcTtFYMYY5t8=
|
| 70 |
+
github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
|
| 71 |
+
github.com/anacrolix/sync v0.3.0/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
|
| 72 |
+
github.com/anacrolix/sync v0.5.1 h1:FbGju6GqSjzVoTgcXTUKkF041lnZkG5P0C3T5RL3SGc=
|
| 73 |
+
github.com/anacrolix/sync v0.5.1/go.mod h1:BbecHL6jDSExojhNtgTFSBcdGerzNc64tz3DCOj/I0g=
|
| 74 |
+
github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
|
| 75 |
+
github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
|
| 76 |
+
github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
|
| 77 |
+
github.com/anacrolix/torrent v1.58.1 h1:6FP+KH57b1gyT2CpVL9fEqf9MGJEgh3xw1VA8rI0pW8=
|
| 78 |
+
github.com/anacrolix/torrent v1.58.1/go.mod h1:/7ZdLuHNKgtCE1gjYJCfbtG9JodBcDaF5ip5EUWRtk8=
|
| 79 |
+
github.com/anacrolix/upnp v0.1.4 h1:+2t2KA6QOhm/49zeNyeVwDu1ZYS9dB9wfxyVvh/wk7U=
|
| 80 |
+
github.com/anacrolix/upnp v0.1.4/go.mod h1:Qyhbqo69gwNWvEk1xNTXsS5j7hMHef9hdr984+9fIic=
|
| 81 |
+
github.com/anacrolix/utp v0.1.0 h1:FOpQOmIwYsnENnz7tAGohA+r6iXpRjrq8ssKSre2Cp4=
|
| 82 |
+
github.com/anacrolix/utp v0.1.0/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk=
|
| 83 |
+
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
| 84 |
+
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
| 85 |
+
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
| 86 |
+
github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
|
| 87 |
+
github.com/benbjohnson/immutable v0.3.0 h1:TVRhuZx2wG9SZ0LRdqlbs9S5BZ6Y24hJEHTCgWHZEIw=
|
| 88 |
+
github.com/benbjohnson/immutable v0.3.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
|
| 89 |
+
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
| 90 |
+
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
| 91 |
+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
| 92 |
+
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
| 93 |
+
github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk=
|
| 94 |
+
github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
| 95 |
+
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
| 96 |
+
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
| 97 |
+
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
| 98 |
+
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
|
| 99 |
+
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
| 100 |
+
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
| 101 |
+
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
| 102 |
+
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
| 103 |
+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
| 104 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 105 |
+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
| 106 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
| 107 |
+
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
| 108 |
+
github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
| 109 |
+
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
| 110 |
+
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
| 111 |
+
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
| 112 |
+
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
| 113 |
+
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
| 114 |
+
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
| 115 |
+
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
| 116 |
+
github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
| 117 |
+
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
| 118 |
+
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
| 119 |
+
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
| 120 |
+
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
| 121 |
+
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
| 122 |
+
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
| 123 |
+
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
| 124 |
+
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
| 125 |
+
github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
| 126 |
+
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
| 127 |
+
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
| 128 |
+
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
| 129 |
+
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 h1:OyQmpAN302wAopDgwVjgs2HkFawP9ahIEqkUYz7V7CA=
|
| 130 |
+
github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916/go.mod h1:DADrR88ONKPPeSGjFp5iEN55Arx3fi2qXZeKCYDpbmU=
|
| 131 |
+
github.com/go-llsqlite/crawshaw v0.5.2-0.20240425034140-f30eb7704568 h1:3EpZo8LxIzF4q3BT+vttQQlRfA6uTtTb/cxVisWa5HM=
|
| 132 |
+
github.com/go-llsqlite/crawshaw v0.5.2-0.20240425034140-f30eb7704568/go.mod h1:/YJdV7uBQaYDE0fwe4z3wwJIZBJxdYzd38ICggWqtaE=
|
| 133 |
+
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
| 134 |
+
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
| 135 |
+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
| 136 |
+
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
| 137 |
+
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
| 138 |
+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
| 139 |
+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
| 140 |
+
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
| 141 |
+
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
| 142 |
+
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
| 143 |
+
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
| 144 |
+
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
| 145 |
+
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
| 146 |
+
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
| 147 |
+
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
| 148 |
+
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
| 149 |
+
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
| 150 |
+
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
| 151 |
+
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
| 152 |
+
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
| 153 |
+
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
| 154 |
+
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
| 155 |
+
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
| 156 |
+
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
| 157 |
+
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
| 158 |
+
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
| 159 |
+
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
| 160 |
+
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
| 161 |
+
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
| 162 |
+
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
| 163 |
+
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
| 164 |
+
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
| 165 |
+
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
| 166 |
+
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
| 167 |
+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
| 168 |
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
| 169 |
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
| 170 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
| 171 |
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
| 172 |
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
| 173 |
+
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
| 174 |
+
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
| 175 |
+
github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
| 176 |
+
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
| 177 |
+
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
| 178 |
+
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
| 179 |
+
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
| 180 |
+
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
| 181 |
+
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
| 182 |
+
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
| 183 |
+
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
| 184 |
+
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
| 185 |
+
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
| 186 |
+
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
| 187 |
+
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
| 188 |
+
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
| 189 |
+
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
|
| 190 |
+
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
| 191 |
+
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
| 192 |
+
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
| 193 |
+
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
| 194 |
+
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
| 195 |
+
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
| 196 |
+
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
| 197 |
+
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
| 198 |
+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
| 199 |
+
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
| 200 |
+
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
| 201 |
+
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
| 202 |
+
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
| 203 |
+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
| 204 |
+
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
| 205 |
+
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
| 206 |
+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
| 207 |
+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
| 208 |
+
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
| 209 |
+
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
| 210 |
+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
| 211 |
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
| 212 |
+
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
| 213 |
+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
| 214 |
+
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
| 215 |
+
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
| 216 |
+
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
| 217 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 218 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
| 219 |
+
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
| 220 |
+
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
| 221 |
+
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
| 222 |
+
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
| 223 |
+
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
| 224 |
+
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
| 225 |
+
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
| 226 |
+
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
|
| 227 |
+
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
| 228 |
+
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
|
| 229 |
+
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
|
| 230 |
+
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
| 231 |
+
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
| 232 |
+
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
| 233 |
+
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
| 234 |
+
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
| 235 |
+
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
| 236 |
+
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
| 237 |
+
github.com/pion/datachannel v1.5.9 h1:LpIWAOYPyDrXtU+BW7X0Yt/vGtYxtXQ8ql7dFfYUVZA=
|
| 238 |
+
github.com/pion/datachannel v1.5.9/go.mod h1:kDUuk4CU4Uxp82NH4LQZbISULkX/HtzKa4P7ldf9izE=
|
| 239 |
+
github.com/pion/dtls/v3 v3.0.3 h1:j5ajZbQwff7Z8k3pE3S+rQ4STvKvXUdKsi/07ka+OWM=
|
| 240 |
+
github.com/pion/dtls/v3 v3.0.3/go.mod h1:weOTUyIV4z0bQaVzKe8kpaP17+us3yAuiQsEAG1STMU=
|
| 241 |
+
github.com/pion/ice/v4 v4.0.2 h1:1JhBRX8iQLi0+TfcavTjPjI6GO41MFn4CeTBX+Y9h5s=
|
| 242 |
+
github.com/pion/ice/v4 v4.0.2/go.mod h1:DCdqyzgtsDNYN6/3U8044j3U7qsJ9KFJC92VnOWHvXg=
|
| 243 |
+
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
|
| 244 |
+
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
|
| 245 |
+
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
| 246 |
+
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
| 247 |
+
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
| 248 |
+
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
| 249 |
+
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
| 250 |
+
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
| 251 |
+
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
|
| 252 |
+
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
| 253 |
+
github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
|
| 254 |
+
github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
| 255 |
+
github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=
|
| 256 |
+
github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM=
|
| 257 |
+
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
|
| 258 |
+
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
| 259 |
+
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
|
| 260 |
+
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
|
| 261 |
+
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
| 262 |
+
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
| 263 |
+
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
| 264 |
+
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
| 265 |
+
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
|
| 266 |
+
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
|
| 267 |
+
github.com/pion/webrtc/v4 v4.0.0 h1:x8ec7uJQPP3D1iI8ojPAiTOylPI7Fa7QgqZrhpLyqZ8=
|
| 268 |
+
github.com/pion/webrtc/v4 v4.0.0/go.mod h1:SfNn8CcFxR6OUVjLXVslAQ3a3994JhyE3Hw1jAuqEto=
|
| 269 |
+
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
| 270 |
+
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
| 271 |
+
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
| 272 |
+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
| 273 |
+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
| 274 |
+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
| 275 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
| 276 |
+
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
| 277 |
+
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
| 278 |
+
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
| 279 |
+
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
| 280 |
+
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
| 281 |
+
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
| 282 |
+
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
| 283 |
+
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
| 284 |
+
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
| 285 |
+
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
| 286 |
+
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
| 287 |
+
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
| 288 |
+
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
| 289 |
+
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
| 290 |
+
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
| 291 |
+
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
| 292 |
+
github.com/protolambda/ctxlock v0.1.0 h1:rCUY3+vRdcdZXqT07iXgyr744J2DU2LCBIXowYAjBCE=
|
| 293 |
+
github.com/protolambda/ctxlock v0.1.0/go.mod h1:vefhX6rIZH8rsg5ZpOJfEDYQOppZi19SfPiGOFrNnwM=
|
| 294 |
+
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
| 295 |
+
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
| 296 |
+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
| 297 |
+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
| 298 |
+
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
| 299 |
+
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
| 300 |
+
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
| 301 |
+
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
| 302 |
+
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
| 303 |
+
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 h1:Lt9DzQALzHoDwMBGJ6v8ObDPR0dzr2a6sXTB1Fq7IHs=
|
| 304 |
+
github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
|
| 305 |
+
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
| 306 |
+
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
| 307 |
+
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
| 308 |
+
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
| 309 |
+
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
| 310 |
+
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
| 311 |
+
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
| 312 |
+
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
|
| 313 |
+
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
| 314 |
+
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
| 315 |
+
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
| 316 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
| 317 |
+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
| 318 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
| 319 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
| 320 |
+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
| 321 |
+
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
| 322 |
+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
| 323 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
| 324 |
+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
| 325 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 326 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
| 327 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
| 328 |
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
| 329 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
| 330 |
+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
| 331 |
+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
| 332 |
+
github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg=
|
| 333 |
+
github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
|
| 334 |
+
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
| 335 |
+
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
| 336 |
+
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
| 337 |
+
github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
| 338 |
+
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
| 339 |
+
github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg=
|
| 340 |
+
github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
| 341 |
+
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
| 342 |
+
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
| 343 |
+
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
| 344 |
+
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
| 345 |
+
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
| 346 |
+
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
| 347 |
+
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
| 348 |
+
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
|
| 349 |
+
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
|
| 350 |
+
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
|
| 351 |
+
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
|
| 352 |
+
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
| 353 |
+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
| 354 |
+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
| 355 |
+
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
| 356 |
+
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
| 357 |
+
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
| 358 |
+
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
| 359 |
+
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
| 360 |
+
golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
| 361 |
+
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
|
| 362 |
+
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
| 363 |
+
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
| 364 |
+
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
| 365 |
+
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
| 366 |
+
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
| 367 |
+
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
| 368 |
+
golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
| 369 |
+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
| 370 |
+
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
| 371 |
+
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
| 372 |
+
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
| 373 |
+
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
| 374 |
+
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
| 375 |
+
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
| 376 |
+
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
| 377 |
+
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
| 378 |
+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
| 379 |
+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
| 380 |
+
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
| 381 |
+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
| 382 |
+
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
| 383 |
+
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
| 384 |
+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
| 385 |
+
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
| 386 |
+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
| 387 |
+
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
| 388 |
+
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
| 389 |
+
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
| 390 |
+
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
| 391 |
+
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
| 392 |
+
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 393 |
+
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 394 |
+
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 395 |
+
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 396 |
+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 397 |
+
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 398 |
+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 399 |
+
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 400 |
+
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
| 401 |
+
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
| 402 |
+
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
| 403 |
+
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
| 404 |
+
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
| 405 |
+
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
| 406 |
+
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
| 407 |
+
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
| 408 |
+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
| 409 |
+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 410 |
+
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 411 |
+
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 412 |
+
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 413 |
+
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 414 |
+
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 415 |
+
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 416 |
+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 417 |
+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
| 418 |
+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 419 |
+
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 420 |
+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 421 |
+
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 422 |
+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 423 |
+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 424 |
+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 425 |
+
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 426 |
+
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
| 427 |
+
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
| 428 |
+
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
| 429 |
+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
| 430 |
+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
| 431 |
+
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
| 432 |
+
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
| 433 |
+
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
| 434 |
+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
| 435 |
+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
| 436 |
+
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
| 437 |
+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
| 438 |
+
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
| 439 |
+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
| 440 |
+
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
| 441 |
+
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
| 442 |
+
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
|
| 443 |
+
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
| 444 |
+
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
| 445 |
+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
| 446 |
+
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
| 447 |
+
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
| 448 |
+
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
| 449 |
+
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
| 450 |
+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
| 451 |
+
golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
| 452 |
+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
| 453 |
+
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
| 454 |
+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
| 455 |
+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
| 456 |
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
| 457 |
+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
| 458 |
+
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
| 459 |
+
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
| 460 |
+
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
| 461 |
+
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
| 462 |
+
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
| 463 |
+
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
| 464 |
+
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
| 465 |
+
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
| 466 |
+
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
| 467 |
+
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
| 468 |
+
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
| 469 |
+
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
| 470 |
+
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
| 471 |
+
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
| 472 |
+
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
| 473 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 474 |
+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 475 |
+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
| 476 |
+
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
| 477 |
+
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
| 478 |
+
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
| 479 |
+
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
| 480 |
+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
| 481 |
+
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
| 482 |
+
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
| 483 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 484 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
| 485 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
| 486 |
+
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
| 487 |
+
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
| 488 |
+
lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
|
| 489 |
+
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
| 490 |
+
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
|
| 491 |
+
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
|
| 492 |
+
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
| 493 |
+
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
| 494 |
+
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
| 495 |
+
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
| 496 |
+
modernc.org/sqlite v1.21.1 h1:GyDFqNnESLOhwwDRaHGdp2jKLDzpyT/rNLglX3ZkMSU=
|
| 497 |
+
modernc.org/sqlite v1.21.1/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
|
| 498 |
+
zombiezen.com/go/sqlite v0.13.1 h1:qDzxyWWmMtSSEH5qxamqBFmqA2BLSSbtODi3ojaE02o=
|
| 499 |
+
zombiezen.com/go/sqlite v0.13.1/go.mod h1:Ht/5Rg3Ae2hoyh1I7gbWtWAl89CNocfqeb/aAMTkJr4=
|
main.go
ADDED
|
@@ -0,0 +1,1434 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package main
|
| 2 |
+
|
| 3 |
+
import (
|
| 4 |
+
"bytes"
|
| 5 |
+
"context"
|
| 6 |
+
"encoding/json"
|
| 7 |
+
"errors"
|
| 8 |
+
"fmt"
|
| 9 |
+
"io"
|
| 10 |
+
"log"
|
| 11 |
+
"math/rand"
|
| 12 |
+
"net"
|
| 13 |
+
"net/http"
|
| 14 |
+
"os"
|
| 15 |
+
"reflect"
|
| 16 |
+
"runtime"
|
| 17 |
+
"strconv"
|
| 18 |
+
"strings"
|
| 19 |
+
"sync"
|
| 20 |
+
"time"
|
| 21 |
+
|
| 22 |
+
"net/url"
|
| 23 |
+
"path/filepath"
|
| 24 |
+
|
| 25 |
+
"github.com/anacrolix/torrent"
|
| 26 |
+
"github.com/anacrolix/torrent/metainfo"
|
| 27 |
+
"github.com/anacrolix/torrent/storage"
|
| 28 |
+
"golang.org/x/net/proxy"
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
var (
|
| 32 |
+
currentSettings Settings
|
| 33 |
+
settingsMutex sync.RWMutex
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
type TorrentSession struct {
|
| 37 |
+
Client *torrent.Client
|
| 38 |
+
Torrent *torrent.Torrent
|
| 39 |
+
Port int
|
| 40 |
+
LastUsed time.Time
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
type Settings struct {
|
| 44 |
+
EnableProxy bool `json:"enableProxy"`
|
| 45 |
+
ProxyURL string `json:"proxyUrl"`
|
| 46 |
+
EnableProwlarr bool `json:"enableProwlarr"`
|
| 47 |
+
ProwlarrHost string `json:"prowlarrHost"`
|
| 48 |
+
ProwlarrApiKey string `json:"prowlarrApiKey"`
|
| 49 |
+
EnableJackett bool `json:"enableJackett"`
|
| 50 |
+
JackettHost string `json:"jackettHost"`
|
| 51 |
+
JackettApiKey string `json:"jackettApiKey"`
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
type ProxySettings struct {
|
| 55 |
+
EnableProxy bool `json:"enableProxy"`
|
| 56 |
+
ProxyURL string `json:"proxyUrl"`
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
type ProwlarrSettings struct {
|
| 60 |
+
EnableProwlarr bool `json:"enableProwlarr"`
|
| 61 |
+
ProwlarrHost string `json:"prowlarrHost"`
|
| 62 |
+
ProwlarrApiKey string `json:"prowlarrApiKey"`
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
type JackettSettings struct {
|
| 66 |
+
EnableJackett bool `json:"enableJackett"`
|
| 67 |
+
JackettHost string `json:"jackettHost"`
|
| 68 |
+
JackettApiKey string `json:"jackettApiKey"`
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
var (
|
| 72 |
+
sessions sync.Map
|
| 73 |
+
usedPorts sync.Map
|
| 74 |
+
portMutex sync.Mutex
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
// Helper function to format file sizes
|
| 78 |
+
func formatSize(sizeInBytes float64) string {
|
| 79 |
+
if sizeInBytes < 1024 {
|
| 80 |
+
return fmt.Sprintf("%.0f B", sizeInBytes)
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
sizeInKB := sizeInBytes / 1024
|
| 84 |
+
if sizeInKB < 1024 {
|
| 85 |
+
return fmt.Sprintf("%.2f KB", sizeInKB)
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
sizeInMB := sizeInKB / 1024
|
| 89 |
+
if sizeInMB < 1024 {
|
| 90 |
+
return fmt.Sprintf("%.2f MB", sizeInMB)
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
sizeInGB := sizeInMB / 1024
|
| 94 |
+
return fmt.Sprintf("%.2f GB", sizeInGB)
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
var (
|
| 98 |
+
proxyTransport = &http.Transport{
|
| 99 |
+
// copy your existing timeouts & DialContext logic here...
|
| 100 |
+
TLSHandshakeTimeout: 10 * time.Second,
|
| 101 |
+
ResponseHeaderTimeout: 20 * time.Second,
|
| 102 |
+
ExpectContinueTimeout: 1 * time.Second,
|
| 103 |
+
IdleConnTimeout: 30 * time.Second,
|
| 104 |
+
MaxIdleConnsPerHost: 10,
|
| 105 |
+
}
|
| 106 |
+
proxyClient = &http.Client{
|
| 107 |
+
Transport: proxyTransport,
|
| 108 |
+
Timeout: 30 * time.Second,
|
| 109 |
+
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
| 110 |
+
if len(via) >= 10 {
|
| 111 |
+
return errors.New("too many redirects")
|
| 112 |
+
}
|
| 113 |
+
for k, vv := range via[0].Header {
|
| 114 |
+
if _, ok := req.Header[k]; !ok {
|
| 115 |
+
req.Header[k] = vv
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
return nil
|
| 119 |
+
},
|
| 120 |
+
}
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
func createSelectiveProxyClient() *http.Client {
|
| 124 |
+
settingsMutex.RLock()
|
| 125 |
+
defer settingsMutex.RUnlock()
|
| 126 |
+
|
| 127 |
+
if !currentSettings.EnableProxy {
|
| 128 |
+
return &http.Client{Timeout: 30 * time.Second}
|
| 129 |
+
}
|
| 130 |
+
// Reconfigure proxyTransport’s DialContext if URL changed:
|
| 131 |
+
dialer, _ := createProxyDialer(currentSettings.ProxyURL)
|
| 132 |
+
proxyTransport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
| 133 |
+
return dialer.Dial(network, addr)
|
| 134 |
+
}
|
| 135 |
+
// Drop any old idle conns after reconfiguration:
|
| 136 |
+
proxyTransport.CloseIdleConnections()
|
| 137 |
+
|
| 138 |
+
return proxyClient
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
// Create a proxy dialer for SOCKS5
|
| 142 |
+
func createProxyDialer(proxyURL string) (proxy.Dialer, error) {
|
| 143 |
+
proxyURLParsed, err := url.Parse(proxyURL)
|
| 144 |
+
if err != nil {
|
| 145 |
+
return nil, fmt.Errorf("failed to parse proxy URL: %v", err)
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
// Extract auth information
|
| 149 |
+
auth := &proxy.Auth{}
|
| 150 |
+
if proxyURLParsed.User != nil {
|
| 151 |
+
auth.User = proxyURLParsed.User.Username()
|
| 152 |
+
if password, ok := proxyURLParsed.User.Password(); ok {
|
| 153 |
+
auth.Password = password
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
// Create a SOCKS5 dialer
|
| 158 |
+
return proxy.SOCKS5("tcp", proxyURLParsed.Host, auth, proxy.Direct)
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
// Implement a port allocation function to prevent conflicts
|
| 162 |
+
func getAvailablePort() int {
|
| 163 |
+
portMutex.Lock()
|
| 164 |
+
defer portMutex.Unlock()
|
| 165 |
+
|
| 166 |
+
// Try up to 50 times to find an unused port
|
| 167 |
+
for i := 0; i < 50; i++ {
|
| 168 |
+
// Generate a random port in the high range
|
| 169 |
+
port := 10000 + rand.Intn(50000)
|
| 170 |
+
|
| 171 |
+
// Check if this port is already in use by our app
|
| 172 |
+
if _, exists := usedPorts.Load(port); !exists {
|
| 173 |
+
// Mark this port as used
|
| 174 |
+
usedPorts.Store(port, true)
|
| 175 |
+
return port
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
// If we can't find an available port, return a very high random port
|
| 180 |
+
// as a last resort
|
| 181 |
+
return 60000 + rand.Intn(5000)
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
// Release a port when we're done with it
|
| 185 |
+
func releasePort(port int) {
|
| 186 |
+
portMutex.Lock()
|
| 187 |
+
defer portMutex.Unlock()
|
| 188 |
+
usedPorts.Delete(port)
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
// Initialize the torrent client with proxy settings
|
| 192 |
+
func initTorrentWithProxy() (*torrent.Client, int, error) {
|
| 193 |
+
settingsMutex.RLock()
|
| 194 |
+
enableProxy := currentSettings.EnableProxy
|
| 195 |
+
proxyURL := currentSettings.ProxyURL
|
| 196 |
+
settingsMutex.RUnlock()
|
| 197 |
+
|
| 198 |
+
config := torrent.NewDefaultClientConfig()
|
| 199 |
+
config.DefaultStorage = storage.NewFile("./torrent-data")
|
| 200 |
+
port := getAvailablePort()
|
| 201 |
+
config.ListenPort = port
|
| 202 |
+
|
| 203 |
+
if enableProxy {
|
| 204 |
+
log.Println("Creating torrent client with proxy...")
|
| 205 |
+
os.Setenv("ALL_PROXY", proxyURL)
|
| 206 |
+
os.Setenv("SOCKS_PROXY", proxyURL)
|
| 207 |
+
os.Setenv("HTTP_PROXY", proxyURL)
|
| 208 |
+
os.Setenv("HTTPS_PROXY", proxyURL)
|
| 209 |
+
|
| 210 |
+
proxyDialer, err := createProxyDialer(proxyURL)
|
| 211 |
+
if err != nil {
|
| 212 |
+
releasePort(port)
|
| 213 |
+
return nil, port, fmt.Errorf("could not create proxy dialer: %v", err)
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
config.HTTPProxy = func(*http.Request) (*url.URL, error) {
|
| 217 |
+
return url.Parse(proxyURL)
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
client, err := torrent.NewClient(config)
|
| 221 |
+
if err != nil {
|
| 222 |
+
releasePort(port)
|
| 223 |
+
return nil, port, err
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
setValue(client, "dialerNetwork", func(ctx context.Context, network, addr string) (net.Conn, error) {
|
| 227 |
+
return proxyDialer.Dial(network, addr)
|
| 228 |
+
})
|
| 229 |
+
|
| 230 |
+
return client, port, nil
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
log.Println("Creating torrent client without proxy...")
|
| 234 |
+
os.Unsetenv("ALL_PROXY")
|
| 235 |
+
os.Unsetenv("SOCKS_PROXY")
|
| 236 |
+
os.Unsetenv("HTTP_PROXY")
|
| 237 |
+
os.Unsetenv("HTTPS_PROXY")
|
| 238 |
+
|
| 239 |
+
client, err := torrent.NewClient(config)
|
| 240 |
+
if err != nil {
|
| 241 |
+
releasePort(port)
|
| 242 |
+
return nil, port, err
|
| 243 |
+
}
|
| 244 |
+
return client, port, nil
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
// Helper function to try to set a field value using reflection
|
| 248 |
+
// This is a bit hacky but might help override the client's dialer
|
| 249 |
+
func setValue(obj interface{}, fieldName string, value interface{}) {
|
| 250 |
+
// This is a best-effort approach that may not work with all library versions
|
| 251 |
+
defer func() {
|
| 252 |
+
if r := recover(); r != nil {
|
| 253 |
+
log.Printf("Warning: Could not set %s field: %v", fieldName, r)
|
| 254 |
+
}
|
| 255 |
+
}()
|
| 256 |
+
|
| 257 |
+
reflectValue := reflect.ValueOf(obj).Elem()
|
| 258 |
+
field := reflectValue.FieldByName(fieldName)
|
| 259 |
+
|
| 260 |
+
if field.IsValid() && field.CanSet() {
|
| 261 |
+
field.Set(reflect.ValueOf(value))
|
| 262 |
+
log.Printf("Successfully set %s to use proxy", fieldName)
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
// Override system settings with our proxy
|
| 267 |
+
func init() {
|
| 268 |
+
|
| 269 |
+
// check if settings.json exists
|
| 270 |
+
if _, err := os.Stat("config/settings.json"); os.IsNotExist(err) {
|
| 271 |
+
log.Println("settings.json not found, creating default settings")
|
| 272 |
+
defaultSettings := Settings{
|
| 273 |
+
EnableProxy: false,
|
| 274 |
+
ProxyURL: "",
|
| 275 |
+
EnableProwlarr: false,
|
| 276 |
+
ProwlarrHost: "",
|
| 277 |
+
ProwlarrApiKey: "",
|
| 278 |
+
EnableJackett: false,
|
| 279 |
+
JackettHost: "",
|
| 280 |
+
JackettApiKey: "",
|
| 281 |
+
}
|
| 282 |
+
// Create the config directory if it doesn't exist
|
| 283 |
+
if err := os.MkdirAll("config", 0755); err != nil {
|
| 284 |
+
log.Fatalf("Failed to create config directory: %v", err)
|
| 285 |
+
}
|
| 286 |
+
settingsFile, err := os.Create("config/settings.json")
|
| 287 |
+
if err != nil {
|
| 288 |
+
log.Fatalf("Failed to create settings.json: %v", err)
|
| 289 |
+
}
|
| 290 |
+
defer settingsFile.Close()
|
| 291 |
+
encoder := json.NewEncoder(settingsFile)
|
| 292 |
+
encoder.SetIndent("", " ")
|
| 293 |
+
if err := encoder.Encode(defaultSettings); err != nil {
|
| 294 |
+
log.Fatalf("Failed to encode default settings: %v", err)
|
| 295 |
+
}
|
| 296 |
+
log.Println("Default settings created in settings.json")
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
// Load settings from settings.json
|
| 300 |
+
settingsFile, err := os.Open("config/settings.json")
|
| 301 |
+
if err != nil {
|
| 302 |
+
log.Fatalf("Failed to open settings.json: %v", err)
|
| 303 |
+
}
|
| 304 |
+
defer settingsFile.Close()
|
| 305 |
+
|
| 306 |
+
var s Settings
|
| 307 |
+
if err := json.NewDecoder(settingsFile).Decode(&s); err != nil {
|
| 308 |
+
log.Fatalf("Failed to decode settings.json: %v", err)
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
settingsMutex.Lock()
|
| 312 |
+
currentSettings = s
|
| 313 |
+
settingsMutex.Unlock()
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
func main() {
|
| 317 |
+
// Seed random number generator
|
| 318 |
+
rand.Seed(time.Now().UnixNano())
|
| 319 |
+
|
| 320 |
+
// Force proxy for all Go HTTP connections
|
| 321 |
+
setGlobalProxy()
|
| 322 |
+
|
| 323 |
+
// Set up endpoint handlers
|
| 324 |
+
http.HandleFunc("/api/v1/torrent/add", addTorrentHandler)
|
| 325 |
+
http.HandleFunc("/api/v1/torrent/", torrentHandler)
|
| 326 |
+
http.HandleFunc("/api/v1/settings", func(w http.ResponseWriter, r *http.Request) {
|
| 327 |
+
if r.Method == http.MethodGet {
|
| 328 |
+
settingsMutex.RLock()
|
| 329 |
+
defer settingsMutex.RUnlock()
|
| 330 |
+
respondWithJSON(w, http.StatusOK, currentSettings)
|
| 331 |
+
} else {
|
| 332 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 333 |
+
}
|
| 334 |
+
})
|
| 335 |
+
http.HandleFunc("/api/v1/settings/proxy", saveProxySettingsHandler)
|
| 336 |
+
http.HandleFunc("/api/v1/settings/prowlarr", saveProwlarrSettingsHandler)
|
| 337 |
+
http.HandleFunc("/api/v1/settings/jackett", saveJackettSettingsHandler)
|
| 338 |
+
http.HandleFunc("/api/v1/prowlarr/search", searchFromProwlarr)
|
| 339 |
+
http.HandleFunc("/api/v1/jackett/search", searchFromJackett)
|
| 340 |
+
http.HandleFunc("/api/v1/prowlarr/test", testProwlarrConnection)
|
| 341 |
+
http.HandleFunc("/api/v1/jackett/test", testJackettConnection)
|
| 342 |
+
http.HandleFunc("/api/v1/proxy/test", testProxyConnection)
|
| 343 |
+
http.HandleFunc("/api/v1/torrent/convert", convertTorrentToMagnetHandler)
|
| 344 |
+
|
| 345 |
+
// Set up client file serving
|
| 346 |
+
http.Handle("/", http.FileServer(http.Dir("./client")))
|
| 347 |
+
http.HandleFunc("/client/", func(w http.ResponseWriter, r *http.Request) {
|
| 348 |
+
http.StripPrefix("/client/", http.FileServer(http.Dir("./client"))).ServeHTTP(w, r)
|
| 349 |
+
})
|
| 350 |
+
http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
| 351 |
+
http.ServeFile(w, r, "./client/favicon.ico")
|
| 352 |
+
})
|
| 353 |
+
|
| 354 |
+
go cleanupSessions()
|
| 355 |
+
|
| 356 |
+
port := 3347
|
| 357 |
+
|
| 358 |
+
addr := fmt.Sprintf(":%d", port)
|
| 359 |
+
log.Printf("Attempting to start server on %s", addr)
|
| 360 |
+
|
| 361 |
+
// Create channel to signal if server started successfully
|
| 362 |
+
serverStarted := make(chan bool, 1)
|
| 363 |
+
|
| 364 |
+
// Create a server with graceful shutdown
|
| 365 |
+
server := &http.Server{
|
| 366 |
+
Addr: addr,
|
| 367 |
+
Handler: nil, // Use the default ServeMux
|
| 368 |
+
}
|
| 369 |
+
|
| 370 |
+
// Start the server in a goroutine
|
| 371 |
+
go func() {
|
| 372 |
+
err := server.ListenAndServe()
|
| 373 |
+
if err != nil && err != http.ErrServerClosed {
|
| 374 |
+
log.Printf("Server failed on %s: %v", addr, err)
|
| 375 |
+
serverStarted <- false
|
| 376 |
+
}
|
| 377 |
+
}()
|
| 378 |
+
|
| 379 |
+
// Give the server a moment to start or fail
|
| 380 |
+
select {
|
| 381 |
+
case success := <-serverStarted:
|
| 382 |
+
if !success {
|
| 383 |
+
log.Printf("Server failed to start on %s", addr)
|
| 384 |
+
return
|
| 385 |
+
}
|
| 386 |
+
case <-time.After(1 * time.Second):
|
| 387 |
+
// No immediate error, assume it started successfully
|
| 388 |
+
log.Printf("🚀 Server successfully started on %s", addr)
|
| 389 |
+
|
| 390 |
+
// Create a simple message to display in the browser
|
| 391 |
+
fmt.Printf("\n------------------------------------------------\n")
|
| 392 |
+
fmt.Printf("✅ Server started! Open in your browser:\n")
|
| 393 |
+
fmt.Printf(" http://localhost:%d\n", port)
|
| 394 |
+
fmt.Printf("------------------------------------------------\n\n")
|
| 395 |
+
|
| 396 |
+
// Block forever (the server is running in a goroutine)
|
| 397 |
+
select {}
|
| 398 |
+
}
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
// Set up global proxy for all Go HTTP calls
|
| 402 |
+
func setGlobalProxy() {
|
| 403 |
+
settingsMutex.RLock()
|
| 404 |
+
enableProxy := currentSettings.EnableProxy
|
| 405 |
+
proxyURL := currentSettings.ProxyURL
|
| 406 |
+
settingsMutex.RUnlock()
|
| 407 |
+
|
| 408 |
+
if !enableProxy {
|
| 409 |
+
log.Println("Proxy is disabled, not setting global HTTP proxy.")
|
| 410 |
+
return
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
proxyDialer, err := createProxyDialer(proxyURL)
|
| 414 |
+
if err != nil {
|
| 415 |
+
log.Printf("Warning: Could not create proxy dialer: %v", err)
|
| 416 |
+
return
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
httpTransport, ok := http.DefaultTransport.(*http.Transport)
|
| 420 |
+
if ok {
|
| 421 |
+
httpTransport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
| 422 |
+
return proxyDialer.Dial(network, addr)
|
| 423 |
+
}
|
| 424 |
+
log.Printf("Successfully configured SOCKS5 proxy for all HTTP traffic: %s", proxyURL)
|
| 425 |
+
} else {
|
| 426 |
+
log.Println("⚠️ Warning: Could not override HTTP transport")
|
| 427 |
+
}
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
// Handler to add a torrent using a magnet link
|
| 431 |
+
func addTorrentHandler(w http.ResponseWriter, r *http.Request) {
|
| 432 |
+
var request struct{ Magnet string }
|
| 433 |
+
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
| 434 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request"})
|
| 435 |
+
return
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
magnet := request.Magnet
|
| 439 |
+
if magnet == "" {
|
| 440 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "No magnet link provided"})
|
| 441 |
+
}
|
| 442 |
+
|
| 443 |
+
// handle http links like Prowlarr or Jackett
|
| 444 |
+
if strings.HasPrefix(request.Magnet, "http") {
|
| 445 |
+
// Use the client that bypasses proxy for Prowlarr
|
| 446 |
+
httpClient := createSelectiveProxyClient()
|
| 447 |
+
|
| 448 |
+
httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
| 449 |
+
return http.ErrUseLastResponse
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
// Make the HTTP request to follow the Prowlarr link
|
| 453 |
+
req, err := http.NewRequest("GET", request.Magnet, nil)
|
| 454 |
+
if err != nil {
|
| 455 |
+
log.Printf("Error creating request: %v", err)
|
| 456 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{
|
| 457 |
+
"error": "Invalid URL: " + err.Error(),
|
| 458 |
+
})
|
| 459 |
+
return
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
|
| 463 |
+
|
| 464 |
+
// Follow the Prowlarr link
|
| 465 |
+
log.Printf("Following Prowlarr URL: %s", request.Magnet)
|
| 466 |
+
resp, err := httpClient.Do(req)
|
| 467 |
+
if err != nil {
|
| 468 |
+
log.Printf("Error following URL: %v", err)
|
| 469 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{
|
| 470 |
+
"error": "Failed to download: " + err.Error(),
|
| 471 |
+
})
|
| 472 |
+
return
|
| 473 |
+
}
|
| 474 |
+
defer resp.Body.Close()
|
| 475 |
+
|
| 476 |
+
log.Printf("Got response: %d %s", resp.StatusCode, resp.Status)
|
| 477 |
+
|
| 478 |
+
// Check for redirects to magnet links
|
| 479 |
+
if resp.StatusCode >= 300 && resp.StatusCode < 400 {
|
| 480 |
+
location := resp.Header.Get("Location")
|
| 481 |
+
log.Printf("Found redirect to: %s", location)
|
| 482 |
+
|
| 483 |
+
if strings.HasPrefix(location, "magnet:") {
|
| 484 |
+
log.Printf("Found magnet redirect: %s", location)
|
| 485 |
+
magnet = location
|
| 486 |
+
} else {
|
| 487 |
+
log.Printf("Non-magnet redirect: %s", location)
|
| 488 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{
|
| 489 |
+
"error": "URL redirects to non-magnet content",
|
| 490 |
+
})
|
| 491 |
+
return
|
| 492 |
+
}
|
| 493 |
+
}
|
| 494 |
+
}
|
| 495 |
+
|
| 496 |
+
// check if magnet link is valid
|
| 497 |
+
if magnet == "" || !strings.HasPrefix(magnet, "magnet:") {
|
| 498 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid magnet link"})
|
| 499 |
+
return
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
// Use the simpler, more secure proxy configuration
|
| 503 |
+
client, port, err := initTorrentWithProxy()
|
| 504 |
+
if err != nil {
|
| 505 |
+
log.Printf("Client creation error: %v", err)
|
| 506 |
+
respondWithJSON(w, http.StatusInternalServerError,
|
| 507 |
+
map[string]string{"error": "Failed to create client with proxy"})
|
| 508 |
+
return
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
// if we bail out before session‑storage, make sure to release both client & port
|
| 512 |
+
defer func() {
|
| 513 |
+
if client != nil {
|
| 514 |
+
releasePort(port)
|
| 515 |
+
client.Close()
|
| 516 |
+
}
|
| 517 |
+
}()
|
| 518 |
+
|
| 519 |
+
t, err := client.AddMagnet(magnet)
|
| 520 |
+
if err != nil {
|
| 521 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid magnet url"})
|
| 522 |
+
return
|
| 523 |
+
}
|
| 524 |
+
log.Printf("Torrent added: %s", t.InfoHash().HexString())
|
| 525 |
+
|
| 526 |
+
select {
|
| 527 |
+
case <-t.GotInfo():
|
| 528 |
+
log.Printf("Successfully got torrent info for %s", t.InfoHash().HexString())
|
| 529 |
+
case <-time.After(3 * time.Minute):
|
| 530 |
+
respondWithJSON(w, http.StatusGatewayTimeout, map[string]string{"error": "Timeout getting info - proxy might be blocking BitTorrent traffic"})
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
+
sessionID := t.InfoHash().HexString()
|
| 534 |
+
log.Printf("Creating new session with ID: %s", sessionID)
|
| 535 |
+
sessions.Store(sessionID, &TorrentSession{
|
| 536 |
+
Client: client,
|
| 537 |
+
Torrent: t,
|
| 538 |
+
Port: port,
|
| 539 |
+
LastUsed: time.Now(),
|
| 540 |
+
})
|
| 541 |
+
|
| 542 |
+
// Log successful storage
|
| 543 |
+
log.Printf("Successfully stored session: %s", sessionID)
|
| 544 |
+
|
| 545 |
+
// Set client to nil so it doesn't get closed by the defer function
|
| 546 |
+
// since it's now stored in the sessions map
|
| 547 |
+
client = nil
|
| 548 |
+
|
| 549 |
+
respondWithJSON(w, http.StatusOK, map[string]string{"sessionId": sessionID})
|
| 550 |
+
}
|
| 551 |
+
|
| 552 |
+
// Torrent handler to serve torrent files and stream content
|
| 553 |
+
func torrentHandler(w http.ResponseWriter, r *http.Request) {
|
| 554 |
+
// Log the entire URL path for debugging
|
| 555 |
+
log.Printf("Torrent handler called with path: %s", r.URL.Path)
|
| 556 |
+
|
| 557 |
+
// Extract sessionId and possibly fileIndex from the URL
|
| 558 |
+
parts := strings.Split(r.URL.Path, "/")
|
| 559 |
+
|
| 560 |
+
// Debug the path parts
|
| 561 |
+
log.Printf("Path parts: %v (length: %d)", parts, len(parts))
|
| 562 |
+
|
| 563 |
+
// The URL structure is /api/v1/torrent/[sessionId]/...
|
| 564 |
+
if len(parts) < 5 { // Changed from 4 to 5
|
| 565 |
+
log.Printf("Invalid path: not enough parts")
|
| 566 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid path"})
|
| 567 |
+
return
|
| 568 |
+
}
|
| 569 |
+
|
| 570 |
+
// The session ID is at position 4, not 3 (because array is 0-indexed and path starts with /)
|
| 571 |
+
sessionID := parts[4] // Changed from parts[3] to parts[4]
|
| 572 |
+
|
| 573 |
+
log.Printf("Looking for session with ID: %s", sessionID)
|
| 574 |
+
|
| 575 |
+
// Debug: Print all sessions that we have
|
| 576 |
+
var sessionKeys []string
|
| 577 |
+
sessions.Range(func(key, value interface{}) bool {
|
| 578 |
+
keyStr, ok := key.(string)
|
| 579 |
+
if ok {
|
| 580 |
+
sessionKeys = append(sessionKeys, keyStr)
|
| 581 |
+
}
|
| 582 |
+
return true
|
| 583 |
+
})
|
| 584 |
+
log.Printf("Available sessions: %v", sessionKeys)
|
| 585 |
+
|
| 586 |
+
// Get the torrent session from our sessions map
|
| 587 |
+
sessionValue, ok := sessions.Load(sessionID)
|
| 588 |
+
if !ok {
|
| 589 |
+
log.Printf("Session not found with ID: %s", sessionID)
|
| 590 |
+
respondWithJSON(w, http.StatusNotFound, map[string]string{
|
| 591 |
+
"error": "Session not found",
|
| 592 |
+
"id": sessionID,
|
| 593 |
+
"available_sessions": strings.Join(sessionKeys, ", "),
|
| 594 |
+
})
|
| 595 |
+
return
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
log.Printf("Found session with ID: %s", sessionID)
|
| 599 |
+
session := sessionValue.(*TorrentSession)
|
| 600 |
+
session.LastUsed = time.Now() // Update last used time
|
| 601 |
+
|
| 602 |
+
// If there's a streaming request, handle it
|
| 603 |
+
if len(parts) > 5 && parts[5] == "stream" { // Changed from parts[4] to parts[5]
|
| 604 |
+
if len(parts) < 7 { // Changed from 6 to 7
|
| 605 |
+
http.Error(w, "Invalid stream path", http.StatusBadRequest)
|
| 606 |
+
return
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
fileIndexString := parts[6]
|
| 610 |
+
// remove .vtt from fileIndex if it exists
|
| 611 |
+
fileIndexString = strings.TrimSuffix(fileIndexString, ".vtt")
|
| 612 |
+
|
| 613 |
+
fileIndex, err := strconv.Atoi(fileIndexString)
|
| 614 |
+
|
| 615 |
+
if err != nil {
|
| 616 |
+
http.Error(w, "Invalid file index", http.StatusBadRequest)
|
| 617 |
+
return
|
| 618 |
+
}
|
| 619 |
+
|
| 620 |
+
if fileIndex < 0 || fileIndex >= len(session.Torrent.Files()) {
|
| 621 |
+
http.Error(w, "File index out of range", http.StatusBadRequest)
|
| 622 |
+
return
|
| 623 |
+
}
|
| 624 |
+
|
| 625 |
+
file := session.Torrent.Files()[fileIndex]
|
| 626 |
+
|
| 627 |
+
// Set appropriate Content-Type based on file extension
|
| 628 |
+
fileName := file.DisplayPath()
|
| 629 |
+
extension := strings.ToLower(filepath.Ext(fileName))
|
| 630 |
+
|
| 631 |
+
log.Printf("Streaming file: %s (type: %s)", fileName, extension)
|
| 632 |
+
|
| 633 |
+
switch extension {
|
| 634 |
+
case ".mp4":
|
| 635 |
+
w.Header().Set("Content-Type", "video/mp4")
|
| 636 |
+
case ".webm":
|
| 637 |
+
w.Header().Set("Content-Type", "video/webm")
|
| 638 |
+
case ".mkv":
|
| 639 |
+
w.Header().Set("Content-Type", "video/x-matroska")
|
| 640 |
+
case ".avi":
|
| 641 |
+
w.Header().Set("Content-Type", "video/x-msvideo")
|
| 642 |
+
case ".srt":
|
| 643 |
+
// For SRT, convert to VTT on-the-fly if requested as VTT
|
| 644 |
+
if r.URL.Query().Get("format") == "vtt" {
|
| 645 |
+
w.Header().Set("Content-Type", "text/vtt")
|
| 646 |
+
w.Header().Set("Access-Control-Allow-Origin", "*") // Allow cross-origin requests
|
| 647 |
+
|
| 648 |
+
// Read the SRT file with size limit
|
| 649 |
+
reader := file.NewReader()
|
| 650 |
+
// Wrap with limiting reader to prevent memory issues (10MB max)
|
| 651 |
+
limitReader := io.LimitReader(reader, 10*1024*1024) // 10MB limit for subtitles
|
| 652 |
+
srtBytes, err := io.ReadAll(limitReader)
|
| 653 |
+
if err != nil {
|
| 654 |
+
http.Error(w, "Failed to read subtitle file", http.StatusInternalServerError)
|
| 655 |
+
return
|
| 656 |
+
}
|
| 657 |
+
|
| 658 |
+
// Convert from SRT to VTT
|
| 659 |
+
vttBytes := convertSRTtoVTT(srtBytes)
|
| 660 |
+
w.Write(vttBytes)
|
| 661 |
+
return
|
| 662 |
+
} else {
|
| 663 |
+
w.Header().Set("Content-Type", "text/plain")
|
| 664 |
+
w.Header().Set("Access-Control-Allow-Origin", "*") // Allow cross-origin requests
|
| 665 |
+
}
|
| 666 |
+
case ".vtt":
|
| 667 |
+
w.Header().Set("Content-Type", "text/vtt")
|
| 668 |
+
w.Header().Set("Access-Control-Allow-Origin", "*") // Allow cross-origin requests
|
| 669 |
+
case ".sub":
|
| 670 |
+
w.Header().Set("Content-Type", "text/plain")
|
| 671 |
+
w.Header().Set("Access-Control-Allow-Origin", "*") // Allow cross-origin requests
|
| 672 |
+
default:
|
| 673 |
+
w.Header().Set("Content-Type", "application/octet-stream")
|
| 674 |
+
}
|
| 675 |
+
|
| 676 |
+
// Add CORS headers for all content
|
| 677 |
+
// Stream the file
|
| 678 |
+
reader := file.NewReader()
|
| 679 |
+
// ServeContent will close the reader when done but we need to
|
| 680 |
+
// ensure it gets closed if there's a panic or other error
|
| 681 |
+
defer func() {
|
| 682 |
+
if closer, ok := reader.(io.Closer); ok {
|
| 683 |
+
closer.Close()
|
| 684 |
+
println("Closed reader***************************************")
|
| 685 |
+
}
|
| 686 |
+
}()
|
| 687 |
+
println("Serving content*****************************************")
|
| 688 |
+
http.ServeContent(w, r, fileName, time.Time{}, reader)
|
| 689 |
+
return
|
| 690 |
+
}
|
| 691 |
+
|
| 692 |
+
// If we get here, just return file list
|
| 693 |
+
var files []map[string]interface{}
|
| 694 |
+
for i, file := range session.Torrent.Files() {
|
| 695 |
+
files = append(files, map[string]interface{}{
|
| 696 |
+
"index": i,
|
| 697 |
+
"name": file.DisplayPath(),
|
| 698 |
+
"size": file.Length(),
|
| 699 |
+
})
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
respondWithJSON(w, http.StatusOK, files)
|
| 703 |
+
}
|
| 704 |
+
|
| 705 |
+
// Add a function to convert SRT to VTT format
|
| 706 |
+
func convertSRTtoVTT(srtBytes []byte) []byte {
|
| 707 |
+
srtContent := string(srtBytes)
|
| 708 |
+
|
| 709 |
+
// Add VTT header
|
| 710 |
+
vttContent := "WEBVTT\n\n"
|
| 711 |
+
|
| 712 |
+
// Convert SRT content to VTT format
|
| 713 |
+
// Simple conversion - replace timestamps format
|
| 714 |
+
lines := strings.Split(srtContent, "\n")
|
| 715 |
+
|
| 716 |
+
for i := 0; i < len(lines); i++ {
|
| 717 |
+
line := lines[i]
|
| 718 |
+
|
| 719 |
+
// Skip subtitle numbers
|
| 720 |
+
if _, err := strconv.Atoi(strings.TrimSpace(line)); err == nil {
|
| 721 |
+
continue
|
| 722 |
+
}
|
| 723 |
+
|
| 724 |
+
// Convert timestamp lines
|
| 725 |
+
if strings.Contains(line, " --> ") {
|
| 726 |
+
// SRT: 00:00:20,000 --> 00:00:24,400
|
| 727 |
+
// VTT: 00:00:20.000 --> 00:00:24.400
|
| 728 |
+
line = strings.Replace(line, ",", ".", -1)
|
| 729 |
+
vttContent += line + "\n"
|
| 730 |
+
} else {
|
| 731 |
+
vttContent += line + "\n"
|
| 732 |
+
}
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
return []byte(vttContent)
|
| 736 |
+
}
|
| 737 |
+
|
| 738 |
+
// Helper function to respond with JSON
|
| 739 |
+
func respondWithJSON(w http.ResponseWriter, status int, data interface{}) {
|
| 740 |
+
w.Header().Set("Content-Type", "application/json")
|
| 741 |
+
w.WriteHeader(status)
|
| 742 |
+
json.NewEncoder(w).Encode(data)
|
| 743 |
+
}
|
| 744 |
+
|
| 745 |
+
// Update cleanupSessions with safer reflection
|
| 746 |
+
func cleanupSessions() {
|
| 747 |
+
ticker := time.NewTicker(5 * time.Minute)
|
| 748 |
+
defer ticker.Stop()
|
| 749 |
+
|
| 750 |
+
for range ticker.C {
|
| 751 |
+
log.Printf("Checking for unused sessions...")
|
| 752 |
+
sessions.Range(func(key, value interface{}) bool {
|
| 753 |
+
session := value.(*TorrentSession)
|
| 754 |
+
|
| 755 |
+
if time.Since(session.LastUsed) > 15*time.Minute {
|
| 756 |
+
releasePort(session.Port)
|
| 757 |
+
session.Torrent.Drop()
|
| 758 |
+
session.Client.Close()
|
| 759 |
+
sessions.Delete(key)
|
| 760 |
+
log.Printf("Removed unused session: %s", key)
|
| 761 |
+
}
|
| 762 |
+
return true
|
| 763 |
+
})
|
| 764 |
+
runtime.GC()
|
| 765 |
+
}
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
// Test the proxy connection
|
| 769 |
+
func testProwlarrConnection(w http.ResponseWriter, r *http.Request) {
|
| 770 |
+
// Add CORS headers
|
| 771 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 772 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 773 |
+
|
| 774 |
+
// Handle preflight requests
|
| 775 |
+
if r.Method == "OPTIONS" {
|
| 776 |
+
return
|
| 777 |
+
}
|
| 778 |
+
|
| 779 |
+
var settings ProwlarrSettings
|
| 780 |
+
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
|
| 781 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
|
| 782 |
+
return
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
prowlarrHost := settings.ProwlarrHost
|
| 786 |
+
prowlarrApiKey := settings.ProwlarrApiKey
|
| 787 |
+
|
| 788 |
+
if prowlarrHost == "" || prowlarrApiKey == "" {
|
| 789 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Prowlarr host or API key not set"})
|
| 790 |
+
return
|
| 791 |
+
}
|
| 792 |
+
|
| 793 |
+
client := createSelectiveProxyClient()
|
| 794 |
+
testURL := fmt.Sprintf("%s/api/v1/system/status", prowlarrHost)
|
| 795 |
+
|
| 796 |
+
req, err := http.NewRequest("GET", testURL, nil)
|
| 797 |
+
if err != nil {
|
| 798 |
+
log.Printf("Error creating request: %v", err)
|
| 799 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
| 800 |
+
return
|
| 801 |
+
}
|
| 802 |
+
|
| 803 |
+
req.Header.Set("X-Api-Key", prowlarrApiKey)
|
| 804 |
+
resp, err := client.Do(req)
|
| 805 |
+
if err != nil {
|
| 806 |
+
log.Printf("Error making request to Prowlarr: %v", err)
|
| 807 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to connect to Prowlarr: " + err.Error()})
|
| 808 |
+
return
|
| 809 |
+
}
|
| 810 |
+
defer resp.Body.Close()
|
| 811 |
+
|
| 812 |
+
if resp.StatusCode != http.StatusOK {
|
| 813 |
+
respondWithJSON(w, resp.StatusCode, map[string]string{"error": fmt.Sprintf("Prowlarr returned status %d", resp.StatusCode)})
|
| 814 |
+
return
|
| 815 |
+
}
|
| 816 |
+
|
| 817 |
+
responseBody, err := io.ReadAll(resp.Body)
|
| 818 |
+
if err != nil {
|
| 819 |
+
log.Printf("Error reading response: %v", err)
|
| 820 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to read Prowlarr response"})
|
| 821 |
+
return
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
w.Header().Set("Content-Type", "application/json")
|
| 825 |
+
w.WriteHeader(http.StatusOK)
|
| 826 |
+
w.Write(responseBody)
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
// Search from Prowlarr
|
| 830 |
+
func searchFromProwlarr(w http.ResponseWriter, r *http.Request) {
|
| 831 |
+
// Add CORS headers
|
| 832 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 833 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-Prowlarr-Host, X-Api-Key")
|
| 834 |
+
|
| 835 |
+
// Handle preflight requests
|
| 836 |
+
if r.Method == "OPTIONS" {
|
| 837 |
+
return
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
if r.Method != http.MethodPost {
|
| 841 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 842 |
+
return
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
+
query := r.URL.Query().Get("q")
|
| 846 |
+
if query == "" {
|
| 847 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "No search query provided"})
|
| 848 |
+
return
|
| 849 |
+
}
|
| 850 |
+
|
| 851 |
+
// search movies in prowlarr
|
| 852 |
+
settingsMutex.RLock()
|
| 853 |
+
prowlarrHost := currentSettings.ProwlarrHost
|
| 854 |
+
prowlarrApiKey := currentSettings.ProwlarrApiKey
|
| 855 |
+
settingsMutex.RUnlock()
|
| 856 |
+
|
| 857 |
+
if prowlarrHost == "" || prowlarrApiKey == "" {
|
| 858 |
+
http.Error(w, "Prowlarr host or API key not set", http.StatusBadRequest)
|
| 859 |
+
return
|
| 860 |
+
}
|
| 861 |
+
|
| 862 |
+
// Use the client that bypasses proxy for Prowlarr
|
| 863 |
+
client := createSelectiveProxyClient()
|
| 864 |
+
|
| 865 |
+
// Prowlarr search endpoint - looking for movie torrents
|
| 866 |
+
searchURL := fmt.Sprintf("%s/api/v1/search?query=%s&limit=10", prowlarrHost, url.QueryEscape(query))
|
| 867 |
+
|
| 868 |
+
req, err := http.NewRequest("GET", searchURL, nil)
|
| 869 |
+
if err != nil {
|
| 870 |
+
log.Printf("Error creating request: %v", err)
|
| 871 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
| 872 |
+
return
|
| 873 |
+
}
|
| 874 |
+
|
| 875 |
+
req.Header.Set("X-Api-Key", prowlarrApiKey)
|
| 876 |
+
resp, err := client.Do(req)
|
| 877 |
+
if err != nil {
|
| 878 |
+
log.Printf("Error making request to Prowlarr: %v", err)
|
| 879 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to connect to Prowlarr: " + err.Error()})
|
| 880 |
+
return
|
| 881 |
+
}
|
| 882 |
+
defer resp.Body.Close()
|
| 883 |
+
|
| 884 |
+
// Read the response body
|
| 885 |
+
body, err := io.ReadAll(resp.Body)
|
| 886 |
+
if err != nil {
|
| 887 |
+
log.Printf("Error reading response: %v", err)
|
| 888 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to read Prowlarr response"})
|
| 889 |
+
return
|
| 890 |
+
}
|
| 891 |
+
|
| 892 |
+
if resp.StatusCode != http.StatusOK {
|
| 893 |
+
respondWithJSON(w, resp.StatusCode, map[string]string{"error": fmt.Sprintf("Prowlarr returned status %d: %s", resp.StatusCode, string(body))})
|
| 894 |
+
return
|
| 895 |
+
}
|
| 896 |
+
|
| 897 |
+
// Parse the JSON response and process the results
|
| 898 |
+
var results []map[string]interface{}
|
| 899 |
+
if err := json.Unmarshal(body, &results); err != nil {
|
| 900 |
+
log.Printf("Error parsing JSON: %v", err)
|
| 901 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to parse Prowlarr response"})
|
| 902 |
+
return
|
| 903 |
+
}
|
| 904 |
+
|
| 905 |
+
// Process the results to make them more usable by the frontend
|
| 906 |
+
var processedResults []map[string]interface{}
|
| 907 |
+
for _, result := range results {
|
| 908 |
+
// Get title and download URL
|
| 909 |
+
title, hasTitle := result["title"].(string)
|
| 910 |
+
downloadUrl, hasDownloadUrl := result["downloadUrl"].(string)
|
| 911 |
+
|
| 912 |
+
// Magnet URL might be present in some results
|
| 913 |
+
magnetUrl, hasMagnet := result["magnetUrl"].(string)
|
| 914 |
+
|
| 915 |
+
if !hasTitle || title == "" {
|
| 916 |
+
// Skip results without titles
|
| 917 |
+
continue
|
| 918 |
+
}
|
| 919 |
+
|
| 920 |
+
// We need at least one of download URL or magnet URL
|
| 921 |
+
if (!hasDownloadUrl || downloadUrl == "") && (!hasMagnet || magnetUrl == "") {
|
| 922 |
+
continue
|
| 923 |
+
}
|
| 924 |
+
|
| 925 |
+
// Create a simplified result object with just what we need
|
| 926 |
+
processedResult := map[string]interface{}{
|
| 927 |
+
"title": title,
|
| 928 |
+
}
|
| 929 |
+
|
| 930 |
+
// Prefer magnet URLs if available directly
|
| 931 |
+
if hasMagnet && magnetUrl != "" {
|
| 932 |
+
processedResult["magnetUrl"] = magnetUrl
|
| 933 |
+
processedResult["directMagnet"] = true
|
| 934 |
+
} else if hasDownloadUrl && downloadUrl != "" {
|
| 935 |
+
processedResult["downloadUrl"] = downloadUrl
|
| 936 |
+
processedResult["directMagnet"] = false
|
| 937 |
+
}
|
| 938 |
+
|
| 939 |
+
// Include optional fields if they exist
|
| 940 |
+
if size, ok := result["size"].(float64); ok {
|
| 941 |
+
processedResult["size"] = formatSize(size)
|
| 942 |
+
}
|
| 943 |
+
|
| 944 |
+
if seeders, ok := result["seeders"].(float64); ok {
|
| 945 |
+
processedResult["seeders"] = seeders
|
| 946 |
+
}
|
| 947 |
+
|
| 948 |
+
if leechers, ok := result["leechers"].(float64); ok {
|
| 949 |
+
processedResult["leechers"] = leechers
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
if indexer, ok := result["indexer"].(string); ok {
|
| 953 |
+
processedResult["indexer"] = indexer
|
| 954 |
+
}
|
| 955 |
+
|
| 956 |
+
if publishDate, ok := result["publishDate"].(string); ok {
|
| 957 |
+
processedResult["publishDate"] = publishDate
|
| 958 |
+
}
|
| 959 |
+
|
| 960 |
+
if category, ok := result["category"].(string); ok {
|
| 961 |
+
processedResult["category"] = category
|
| 962 |
+
}
|
| 963 |
+
|
| 964 |
+
processedResults = append(processedResults, processedResult)
|
| 965 |
+
}
|
| 966 |
+
|
| 967 |
+
respondWithJSON(w, http.StatusOK, processedResults)
|
| 968 |
+
}
|
| 969 |
+
|
| 970 |
+
// Test Jackett Connection Handler
|
| 971 |
+
func testJackettConnection(w http.ResponseWriter, r *http.Request) {
|
| 972 |
+
// Add CORS headers
|
| 973 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 974 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 975 |
+
// Handle preflight requests
|
| 976 |
+
if r.Method == "OPTIONS" {
|
| 977 |
+
return
|
| 978 |
+
}
|
| 979 |
+
|
| 980 |
+
var settings JackettSettings
|
| 981 |
+
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
|
| 982 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
|
| 983 |
+
return
|
| 984 |
+
}
|
| 985 |
+
|
| 986 |
+
jackettHost := settings.JackettHost
|
| 987 |
+
jackettApiKey := settings.JackettApiKey
|
| 988 |
+
|
| 989 |
+
if jackettHost == "" || jackettApiKey == "" {
|
| 990 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Jackett host or API key not set"})
|
| 991 |
+
return
|
| 992 |
+
}
|
| 993 |
+
|
| 994 |
+
client := createSelectiveProxyClient()
|
| 995 |
+
testURL := fmt.Sprintf("%s/api/v2.0/indexers/all/results?apikey=%s", jackettHost, jackettApiKey)
|
| 996 |
+
req, err := http.NewRequest("GET", testURL, nil)
|
| 997 |
+
if err != nil {
|
| 998 |
+
log.Printf("Error creating request: %v", err)
|
| 999 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
| 1000 |
+
return
|
| 1001 |
+
}
|
| 1002 |
+
resp, err := client.Do(req)
|
| 1003 |
+
if err != nil {
|
| 1004 |
+
log.Printf("Error making request to Jackett: %v", err)
|
| 1005 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to connect to Jackett: " + err.Error()})
|
| 1006 |
+
return
|
| 1007 |
+
}
|
| 1008 |
+
defer resp.Body.Close()
|
| 1009 |
+
if resp.StatusCode != http.StatusOK {
|
| 1010 |
+
respondWithJSON(w, resp.StatusCode, map[string]string{"error": fmt.Sprintf("Jackett returned status %d", resp.StatusCode)})
|
| 1011 |
+
return
|
| 1012 |
+
}
|
| 1013 |
+
responseBody, err := io.ReadAll(resp.Body)
|
| 1014 |
+
if err != nil {
|
| 1015 |
+
log.Printf("Error reading response: %v", err)
|
| 1016 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to read Jackett response"})
|
| 1017 |
+
return
|
| 1018 |
+
}
|
| 1019 |
+
w.Header().Set("Content-Type", "application/json")
|
| 1020 |
+
w.WriteHeader(http.StatusOK)
|
| 1021 |
+
w.Write(responseBody)
|
| 1022 |
+
}
|
| 1023 |
+
|
| 1024 |
+
// Search from Jackett
|
| 1025 |
+
func searchFromJackett(w http.ResponseWriter, r *http.Request) {
|
| 1026 |
+
// Add CORS headers
|
| 1027 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 1028 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 1029 |
+
|
| 1030 |
+
// Handle preflight requests
|
| 1031 |
+
if r.Method == "OPTIONS" {
|
| 1032 |
+
return
|
| 1033 |
+
}
|
| 1034 |
+
|
| 1035 |
+
if r.Method != http.MethodPost {
|
| 1036 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 1037 |
+
return
|
| 1038 |
+
}
|
| 1039 |
+
|
| 1040 |
+
query := r.URL.Query().Get("q")
|
| 1041 |
+
if query == "" {
|
| 1042 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "No search query provided"})
|
| 1043 |
+
return
|
| 1044 |
+
}
|
| 1045 |
+
|
| 1046 |
+
// search movies in jackett
|
| 1047 |
+
settingsMutex.RLock()
|
| 1048 |
+
jackettHost := currentSettings.JackettHost
|
| 1049 |
+
jackettApiKey := currentSettings.JackettApiKey
|
| 1050 |
+
settingsMutex.RUnlock()
|
| 1051 |
+
|
| 1052 |
+
if jackettHost == "" || jackettApiKey == "" {
|
| 1053 |
+
http.Error(w, "Jackett host or API key not set", http.StatusBadRequest)
|
| 1054 |
+
return
|
| 1055 |
+
}
|
| 1056 |
+
|
| 1057 |
+
// Use the client that bypasses proxy for Jackett
|
| 1058 |
+
client := createSelectiveProxyClient()
|
| 1059 |
+
|
| 1060 |
+
// Jackett search endpoint - looking for movie torrents
|
| 1061 |
+
searchURL := fmt.Sprintf("%s/api/v2.0/indexers/all/results?Query=%s&apikey=%s", jackettHost, url.QueryEscape(query), jackettApiKey)
|
| 1062 |
+
|
| 1063 |
+
req, err := http.NewRequest("GET", searchURL, nil)
|
| 1064 |
+
if err != nil {
|
| 1065 |
+
log.Printf("Error creating request: %v", err)
|
| 1066 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
| 1067 |
+
return
|
| 1068 |
+
}
|
| 1069 |
+
|
| 1070 |
+
resp, err := client.Do(req)
|
| 1071 |
+
if err != nil {
|
| 1072 |
+
log.Printf("Error making request to Jackett: %v", err)
|
| 1073 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to connect to Jackett: " + err.Error()})
|
| 1074 |
+
return
|
| 1075 |
+
}
|
| 1076 |
+
defer resp.Body.Close()
|
| 1077 |
+
|
| 1078 |
+
// Read the response body
|
| 1079 |
+
body, err := io.ReadAll(resp.Body)
|
| 1080 |
+
if err != nil {
|
| 1081 |
+
log.Printf("Error reading response: %v", err)
|
| 1082 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to read Jackett response"})
|
| 1083 |
+
return
|
| 1084 |
+
}
|
| 1085 |
+
|
| 1086 |
+
if resp.StatusCode != http.StatusOK {
|
| 1087 |
+
respondWithJSON(w, resp.StatusCode, map[string]string{"error": fmt.Sprintf("Jackett returned status %d: %s", resp.StatusCode, string(body))})
|
| 1088 |
+
return
|
| 1089 |
+
}
|
| 1090 |
+
|
| 1091 |
+
var jacketResponse struct {
|
| 1092 |
+
Results []map[string]interface{} `json:"Results"`
|
| 1093 |
+
}
|
| 1094 |
+
|
| 1095 |
+
// Parse the JSON response and process the results
|
| 1096 |
+
if err := json.Unmarshal(body, &jacketResponse); err != nil {
|
| 1097 |
+
log.Printf("Error parsing JSON: %v", err)
|
| 1098 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to parse Jackett response"})
|
| 1099 |
+
return
|
| 1100 |
+
}
|
| 1101 |
+
|
| 1102 |
+
// Process the results to make them more usable by the frontend
|
| 1103 |
+
var processedResults []map[string]interface{}
|
| 1104 |
+
for _, result := range jacketResponse.Results {
|
| 1105 |
+
// Get title and download URL
|
| 1106 |
+
title, hasTitle := result["Title"].(string)
|
| 1107 |
+
downloadUrl, hasDownloadUrl := result["Link"].(string)
|
| 1108 |
+
|
| 1109 |
+
// Magnet URL might be present in some results
|
| 1110 |
+
magnetUrl, hasMagnet := result["MagnetUri"].(string)
|
| 1111 |
+
|
| 1112 |
+
if !hasTitle || title == "" {
|
| 1113 |
+
// Skip results without titles
|
| 1114 |
+
continue
|
| 1115 |
+
}
|
| 1116 |
+
|
| 1117 |
+
// We need at least one of download URL or magnet URL
|
| 1118 |
+
if (!hasDownloadUrl || downloadUrl == "") && (!hasMagnet || magnetUrl == "") {
|
| 1119 |
+
continue
|
| 1120 |
+
}
|
| 1121 |
+
|
| 1122 |
+
// Create a simplified result object with just what we need
|
| 1123 |
+
processedResult := map[string]interface{}{
|
| 1124 |
+
"title": title,
|
| 1125 |
+
}
|
| 1126 |
+
|
| 1127 |
+
// Prefer magnet URLs if available directly
|
| 1128 |
+
if hasMagnet && magnetUrl != "" && strings.HasPrefix(magnetUrl, "magnet:") {
|
| 1129 |
+
processedResult["magnetUrl"] = magnetUrl
|
| 1130 |
+
processedResult["directMagnet"] = true
|
| 1131 |
+
} else if hasDownloadUrl && downloadUrl != "" {
|
| 1132 |
+
processedResult["downloadUrl"] = downloadUrl
|
| 1133 |
+
processedResult["directMagnet"] = false
|
| 1134 |
+
}
|
| 1135 |
+
|
| 1136 |
+
// Include optional fields if they exist
|
| 1137 |
+
if size, ok := result["Size"].(float64); ok {
|
| 1138 |
+
processedResult["size"] = formatSize(size)
|
| 1139 |
+
}
|
| 1140 |
+
|
| 1141 |
+
if seeders, ok := result["Seeders"].(float64); ok {
|
| 1142 |
+
processedResult["seeders"] = seeders
|
| 1143 |
+
}
|
| 1144 |
+
|
| 1145 |
+
if leechers, ok := result["Peers"].(float64); ok {
|
| 1146 |
+
processedResult["leechers"] = leechers
|
| 1147 |
+
}
|
| 1148 |
+
|
| 1149 |
+
if indexer, ok := result["Tracker"].(string); ok {
|
| 1150 |
+
processedResult["indexer"] = indexer
|
| 1151 |
+
}
|
| 1152 |
+
|
| 1153 |
+
if publishDate, ok := result["PublishDate"].(string); ok {
|
| 1154 |
+
processedResult["publishDate"] = publishDate
|
| 1155 |
+
}
|
| 1156 |
+
|
| 1157 |
+
if category, ok := result["category"].(string); ok {
|
| 1158 |
+
processedResult["category"] = category
|
| 1159 |
+
}
|
| 1160 |
+
|
| 1161 |
+
processedResults = append(processedResults, processedResult)
|
| 1162 |
+
}
|
| 1163 |
+
|
| 1164 |
+
respondWithJSON(w, http.StatusOK, processedResults)
|
| 1165 |
+
}
|
| 1166 |
+
|
| 1167 |
+
// Test Proxy Connection Handler
|
| 1168 |
+
func testProxyConnection(w http.ResponseWriter, r *http.Request) {
|
| 1169 |
+
// Add CORS headers
|
| 1170 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 1171 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 1172 |
+
|
| 1173 |
+
// Handle preflight requests
|
| 1174 |
+
if r.Method == "OPTIONS" {
|
| 1175 |
+
return
|
| 1176 |
+
}
|
| 1177 |
+
|
| 1178 |
+
if r.Method != http.MethodPost {
|
| 1179 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 1180 |
+
return
|
| 1181 |
+
}
|
| 1182 |
+
|
| 1183 |
+
var settings ProxySettings
|
| 1184 |
+
if err := json.NewDecoder(r.Body).Decode(&settings); err != nil {
|
| 1185 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
|
| 1186 |
+
return
|
| 1187 |
+
}
|
| 1188 |
+
|
| 1189 |
+
proxyURL := settings.ProxyURL
|
| 1190 |
+
|
| 1191 |
+
if proxyURL == "" {
|
| 1192 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Proxy URL not set"})
|
| 1193 |
+
return
|
| 1194 |
+
}
|
| 1195 |
+
|
| 1196 |
+
// Parse the proxy URL
|
| 1197 |
+
parsedProxyURL, err := url.Parse(proxyURL)
|
| 1198 |
+
if err != nil {
|
| 1199 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid proxy URL: " + err.Error()})
|
| 1200 |
+
return
|
| 1201 |
+
}
|
| 1202 |
+
|
| 1203 |
+
// Create a transport that uses the proxy
|
| 1204 |
+
transport := &http.Transport{
|
| 1205 |
+
Proxy: http.ProxyURL(parsedProxyURL),
|
| 1206 |
+
}
|
| 1207 |
+
|
| 1208 |
+
// Create client with custom transport and timeout
|
| 1209 |
+
client := &http.Client{
|
| 1210 |
+
Transport: transport,
|
| 1211 |
+
Timeout: 10 * time.Second, // Adjust timeout as needed
|
| 1212 |
+
}
|
| 1213 |
+
|
| 1214 |
+
testURL := "https://httpbin.org/ip"
|
| 1215 |
+
req, err := http.NewRequest("GET", testURL, nil)
|
| 1216 |
+
if err != nil {
|
| 1217 |
+
log.Printf("Error creating request: %v", err)
|
| 1218 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
| 1219 |
+
return
|
| 1220 |
+
}
|
| 1221 |
+
|
| 1222 |
+
resp, err := client.Do(req)
|
| 1223 |
+
if err != nil {
|
| 1224 |
+
log.Printf("Error making request through proxy: %v", err)
|
| 1225 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Proxy connection failed: " + err.Error()})
|
| 1226 |
+
return
|
| 1227 |
+
}
|
| 1228 |
+
defer resp.Body.Close()
|
| 1229 |
+
|
| 1230 |
+
responseBody, err := io.ReadAll(resp.Body)
|
| 1231 |
+
if err != nil {
|
| 1232 |
+
log.Printf("Error reading response: %v", err)
|
| 1233 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to read proxy response"})
|
| 1234 |
+
return
|
| 1235 |
+
}
|
| 1236 |
+
|
| 1237 |
+
w.Header().Set("Content-Type", "application/json")
|
| 1238 |
+
w.WriteHeader(http.StatusOK)
|
| 1239 |
+
w.Write(responseBody)
|
| 1240 |
+
}
|
| 1241 |
+
|
| 1242 |
+
// Helper function to save settings to file (assumes mutex is already locked)
|
| 1243 |
+
func saveSettingsToFile() error {
|
| 1244 |
+
// Create the directory if it doesn't exist
|
| 1245 |
+
if err := os.MkdirAll("config", 0755); err != nil {
|
| 1246 |
+
log.Fatalf("Failed to create config directory: %v", err)
|
| 1247 |
+
}
|
| 1248 |
+
|
| 1249 |
+
file, err := os.Create("config/settings.json")
|
| 1250 |
+
if err != nil {
|
| 1251 |
+
return err
|
| 1252 |
+
}
|
| 1253 |
+
defer file.Close()
|
| 1254 |
+
|
| 1255 |
+
encoder := json.NewEncoder(file)
|
| 1256 |
+
encoder.SetIndent("", " ")
|
| 1257 |
+
if err := encoder.Encode(currentSettings); err != nil {
|
| 1258 |
+
return err
|
| 1259 |
+
}
|
| 1260 |
+
|
| 1261 |
+
return nil
|
| 1262 |
+
}
|
| 1263 |
+
|
| 1264 |
+
// Proxy Settings Save Handler
|
| 1265 |
+
func saveProxySettingsHandler(w http.ResponseWriter, r *http.Request) {
|
| 1266 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 1267 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 1268 |
+
if r.Method == "OPTIONS" {
|
| 1269 |
+
return
|
| 1270 |
+
}
|
| 1271 |
+
if r.Method != http.MethodPost {
|
| 1272 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 1273 |
+
return
|
| 1274 |
+
}
|
| 1275 |
+
|
| 1276 |
+
var newSettings ProxySettings
|
| 1277 |
+
if err := json.NewDecoder(r.Body).Decode(&newSettings); err != nil {
|
| 1278 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
|
| 1279 |
+
return
|
| 1280 |
+
}
|
| 1281 |
+
|
| 1282 |
+
settingsMutex.RLock()
|
| 1283 |
+
currentSettings.EnableProxy = newSettings.EnableProxy
|
| 1284 |
+
currentSettings.ProxyURL = newSettings.ProxyURL
|
| 1285 |
+
defer settingsMutex.RUnlock()
|
| 1286 |
+
|
| 1287 |
+
if err := saveSettingsToFile(); err != nil {
|
| 1288 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to save settings: " + err.Error()})
|
| 1289 |
+
return
|
| 1290 |
+
}
|
| 1291 |
+
println("Proxy settings saved successfully")
|
| 1292 |
+
|
| 1293 |
+
setGlobalProxy()
|
| 1294 |
+
|
| 1295 |
+
respondWithJSON(w, http.StatusOK, map[string]string{"message": "Proxy settings saved successfully"})
|
| 1296 |
+
}
|
| 1297 |
+
|
| 1298 |
+
// Prowlarr Settings Save Handler
|
| 1299 |
+
func saveProwlarrSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
| 1300 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 1301 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 1302 |
+
if r.Method == "OPTIONS" {
|
| 1303 |
+
return
|
| 1304 |
+
}
|
| 1305 |
+
if r.Method != http.MethodPost {
|
| 1306 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 1307 |
+
return
|
| 1308 |
+
}
|
| 1309 |
+
|
| 1310 |
+
var newSettings ProwlarrSettings
|
| 1311 |
+
if err := json.NewDecoder(r.Body).Decode(&newSettings); err != nil {
|
| 1312 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
|
| 1313 |
+
return
|
| 1314 |
+
}
|
| 1315 |
+
|
| 1316 |
+
settingsMutex.RLock()
|
| 1317 |
+
currentSettings.EnableProwlarr = newSettings.EnableProwlarr
|
| 1318 |
+
currentSettings.ProwlarrHost = newSettings.ProwlarrHost
|
| 1319 |
+
currentSettings.ProwlarrApiKey = newSettings.ProwlarrApiKey
|
| 1320 |
+
defer settingsMutex.RUnlock()
|
| 1321 |
+
|
| 1322 |
+
if err := saveSettingsToFile(); err != nil {
|
| 1323 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to save settings: " + err.Error()})
|
| 1324 |
+
return
|
| 1325 |
+
}
|
| 1326 |
+
|
| 1327 |
+
respondWithJSON(w, http.StatusOK, map[string]string{"message": "Prowlarr settings saved successfully"})
|
| 1328 |
+
}
|
| 1329 |
+
|
| 1330 |
+
// Jackett Settings Save Handler
|
| 1331 |
+
func saveJackettSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
| 1332 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 1333 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 1334 |
+
if r.Method == "OPTIONS" {
|
| 1335 |
+
return
|
| 1336 |
+
}
|
| 1337 |
+
if r.Method != http.MethodPost {
|
| 1338 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 1339 |
+
return
|
| 1340 |
+
}
|
| 1341 |
+
|
| 1342 |
+
var newSettings JackettSettings
|
| 1343 |
+
if err := json.NewDecoder(r.Body).Decode(&newSettings); err != nil {
|
| 1344 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"})
|
| 1345 |
+
return
|
| 1346 |
+
}
|
| 1347 |
+
|
| 1348 |
+
settingsMutex.RLock()
|
| 1349 |
+
currentSettings.EnableJackett = newSettings.EnableJackett
|
| 1350 |
+
currentSettings.JackettHost = newSettings.JackettHost
|
| 1351 |
+
currentSettings.JackettApiKey = newSettings.JackettApiKey
|
| 1352 |
+
defer settingsMutex.RUnlock()
|
| 1353 |
+
|
| 1354 |
+
if err := saveSettingsToFile(); err != nil {
|
| 1355 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to save settings: " + err.Error()})
|
| 1356 |
+
return
|
| 1357 |
+
}
|
| 1358 |
+
|
| 1359 |
+
respondWithJSON(w, http.StatusOK, map[string]string{"message": "Jackett settings saved successfully"})
|
| 1360 |
+
}
|
| 1361 |
+
|
| 1362 |
+
// Convert Torrent to Magnet Handler
|
| 1363 |
+
func convertTorrentToMagnetHandler(w http.ResponseWriter, r *http.Request) {
|
| 1364 |
+
// Set CORS headers
|
| 1365 |
+
w.Header().Set("Access-Control-Allow-Origin", "*")
|
| 1366 |
+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
| 1367 |
+
|
| 1368 |
+
if r.Method == "OPTIONS" {
|
| 1369 |
+
return
|
| 1370 |
+
}
|
| 1371 |
+
|
| 1372 |
+
if r.Method != http.MethodPost {
|
| 1373 |
+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
| 1374 |
+
return
|
| 1375 |
+
}
|
| 1376 |
+
|
| 1377 |
+
// Parse multipart form with 10MB memory limit
|
| 1378 |
+
const maxUploadSize = 10 << 20 // 10MB
|
| 1379 |
+
if err := r.ParseMultipartForm(maxUploadSize); err != nil {
|
| 1380 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Failed to parse form: " + err.Error()})
|
| 1381 |
+
return
|
| 1382 |
+
}
|
| 1383 |
+
|
| 1384 |
+
// Get the torrent file from the form data
|
| 1385 |
+
file, header, err := r.FormFile("torrent")
|
| 1386 |
+
if err != nil {
|
| 1387 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Missing torrent file"})
|
| 1388 |
+
return
|
| 1389 |
+
}
|
| 1390 |
+
defer file.Close()
|
| 1391 |
+
|
| 1392 |
+
// Check file size
|
| 1393 |
+
if header.Size > maxUploadSize {
|
| 1394 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "File too large"})
|
| 1395 |
+
return
|
| 1396 |
+
}
|
| 1397 |
+
|
| 1398 |
+
// Read the torrent file content
|
| 1399 |
+
fileBytes, err := io.ReadAll(file)
|
| 1400 |
+
if err != nil {
|
| 1401 |
+
respondWithJSON(w, http.StatusInternalServerError, map[string]string{"error": "Failed to read file"})
|
| 1402 |
+
return
|
| 1403 |
+
}
|
| 1404 |
+
|
| 1405 |
+
// Parse torrent file
|
| 1406 |
+
mi, err := metainfo.Load(bytes.NewReader(fileBytes))
|
| 1407 |
+
if err != nil {
|
| 1408 |
+
respondWithJSON(w, http.StatusBadRequest, map[string]string{"error": "Invalid torrent file: " + err.Error()})
|
| 1409 |
+
return
|
| 1410 |
+
}
|
| 1411 |
+
|
| 1412 |
+
// Get info hash
|
| 1413 |
+
infoHash := mi.HashInfoBytes().String()
|
| 1414 |
+
|
| 1415 |
+
// Build magnet URL components
|
| 1416 |
+
magnet := fmt.Sprintf("magnet:?xt=urn:btih:%s", infoHash)
|
| 1417 |
+
|
| 1418 |
+
// Add display name
|
| 1419 |
+
info, err := mi.UnmarshalInfo()
|
| 1420 |
+
if err == nil {
|
| 1421 |
+
magnet += fmt.Sprintf("&dn=%s", url.QueryEscape(info.Name))
|
| 1422 |
+
}
|
| 1423 |
+
|
| 1424 |
+
// Add trackers
|
| 1425 |
+
for _, tier := range mi.AnnounceList {
|
| 1426 |
+
for _, tracker := range tier {
|
| 1427 |
+
magnet += fmt.Sprintf("&tr=%s", url.QueryEscape(tracker))
|
| 1428 |
+
}
|
| 1429 |
+
}
|
| 1430 |
+
|
| 1431 |
+
respondWithJSON(w, http.StatusOK, map[string]string{
|
| 1432 |
+
"magnet": magnet,
|
| 1433 |
+
})
|
| 1434 |
+
}
|