diff --git a/backend/controllers/browser.go b/backend/controllers/browser.go new file mode 100644 index 0000000..966a58d --- /dev/null +++ b/backend/controllers/browser.go @@ -0,0 +1,9 @@ +package controllers + +import ( + "github.com/wailsapp/wails/v2/pkg/runtime" +) + +func (a *App) OpenBrowser(url string) { + runtime.BrowserOpenURL(a.ctx, url) +} diff --git a/backend/controllers/download.go b/backend/controllers/download.go index 3264a0f..5d140a2 100644 --- a/backend/controllers/download.go +++ b/backend/controllers/download.go @@ -8,17 +8,11 @@ import ( "unicode" "youtube-downloader-go/backend/models" + "youtube-downloader-go/backend/utils" "github.com/wailsapp/wails/v2/pkg/runtime" ) -var youtubeRegex, _ = regexp.Compile(`^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(?:-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|live\/|v\/)?)([\w\-]+)(\S+)?$`) - -// Check if the url is valid youtube video URL -func isValidYouTubeURL(url string) bool { - return youtubeRegex.MatchString(url) -} - func slugify(input string) string { // Remove non-ASCII characters cleaned := strings.Map(func(r rune) rune { @@ -47,12 +41,9 @@ func slugify(input string) string { func (a *App) DownloadVideo(opts models.DownloadOptions) (string, error) { //* check if the url is valid - var url = opts.URL + _, ytdlpPath := utils.YtDlpSetup() - if !isValidYouTubeURL(url) { - err := fmt.Errorf("invalid url: %s", url) - return err.Error(), err - } + var url = opts.URL pathToSave, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{Title: "Save File"}) @@ -78,7 +69,7 @@ func (a *App) DownloadVideo(opts models.DownloadOptions) (string, error) { } // Execute yt-dlp command - cmd := exec.Command("yt-dlp", cmdArgs...) + cmd := exec.Command(ytdlpPath, cmdArgs...) output, err := cmd.CombinedOutput() if err != nil { err := fmt.Errorf("error executing yt-dlp: %v\nOutput: %s", err, string(output)) diff --git a/backend/controllers/get.go b/backend/controllers/get.go new file mode 100644 index 0000000..c2a7087 --- /dev/null +++ b/backend/controllers/get.go @@ -0,0 +1,33 @@ +package controllers + +import ( + "encoding/json" + "fmt" + "os/exec" + + "youtube-downloader-go/backend/models" + "youtube-downloader-go/backend/utils" +) + +func (a *App) GetVideoInfo(videoURL string) (models.VideoInfo, error) { + _, ytDlpPath := utils.YtDlpSetup() + + cmd := exec.Command(ytDlpPath, "--dump-json", videoURL) + + // Capture the output + output, err := cmd.CombinedOutput() + if err != nil { + return models.VideoInfo{Valid: false}, fmt.Errorf("1 error executing yt-dlp: %w", err) + } + + // Parse the JSON output + var videoInfo models.VideoInfo + err = json.Unmarshal(output, &videoInfo) + if err != nil { + return models.VideoInfo{Valid: false}, fmt.Errorf("error parsing JSON: %w", err) + } + + videoInfo.Valid = true + + return videoInfo, nil +} diff --git a/backend/models/video_info.go b/backend/models/video_info.go new file mode 100644 index 0000000..e62f89d --- /dev/null +++ b/backend/models/video_info.go @@ -0,0 +1,9 @@ +package models + +type VideoInfo struct { + Valid bool + Title string `json:"title"` + Uploader string `json:"uploader"` + Duration int `json:"duration"` // Duration in seconds + ViewCount int `json:"view_count"` +} diff --git a/backend/utils/get_ytdlp.go b/backend/utils/get_ytdlp.go index 98c55f5..a216fea 100644 --- a/backend/utils/get_ytdlp.go +++ b/backend/utils/get_ytdlp.go @@ -6,28 +6,33 @@ import ( "net/http" "os" "path/filepath" - - "github.com/wader/goutubedl" ) const YtDlpGitgubRelease = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe" // If yt-dlp isn't installed yet, download it locally. -func getYtDlp() { +func downloadYtDlpBinary() (string, error) { folderPath := "./bin-deps" - // Check if the folder exists - _, err := os.Stat(folderPath) - // If the folder doesn't exist, create it + _, err := os.Stat(folderPath) if os.IsNotExist(err) { err := os.Mkdir(folderPath, 0755) if err != nil { fmt.Println("Error creating folder:", err) - return + return "", err } } - out, err := os.Create("./bin-deps/yt-dlp.exe") + + // create a new file in the folder + + cwd, err := os.Getwd() + if err != nil { + panic(err) + } + pathToCreate := filepath.Join(cwd, "bin-deps", "yt-dlp.exe") + + out, err := os.Create(pathToCreate) if err != nil { panic(err) } @@ -35,26 +40,30 @@ func getYtDlp() { resp, err := http.Get(YtDlpGitgubRelease) if err != nil { - panic(err) + return "", err } defer resp.Body.Close() io.Copy(out, resp.Body) + return pathToCreate, nil } // Get yt-dlp if not exists, returns cwd and path of yt-dlp -func YtDlSetup() (string, string) { - // check if ./bin-deps/yt-dlp.exe exists - if _, err := os.Stat("./bin-deps/yt-dlp.exe"); os.IsNotExist(err) { - getYtDlp() - } +func YtDlpSetup() (string, string) { + var ytDlpPath string var cwd, err = os.Getwd() if err != nil { panic(err) } - goutubedl.Path = filepath.Join(cwd, "bin-deps", "yt-dlp.exe") - return cwd, goutubedl.Path + // check if ./bin-deps/yt-dlp.exe exists + if _, err := os.Stat("./bin-deps/yt-dlp.exe"); os.IsNotExist(err) { + ytDlpPath, _ = downloadYtDlpBinary() + } else { + ytDlpPath = filepath.Join(cwd, "bin-deps", "yt-dlp.exe") + + } + return cwd, ytDlpPath } diff --git a/frontend/package.json b/frontend/package.json index 8c9ae62..c903c36 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,5 +12,8 @@ "@sveltejs/vite-plugin-svelte": "^1.0.1", "svelte": "^3.49.0", "vite": "^3.0.7" + }, + "dependencies": { + "@fortawesome/fontawesome-free": "^6.6.0" } -} \ No newline at end of file +} diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index a14aba6..0543e54 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -1,42 +1,158 @@
- -
-

{text}

- - -
+ + +

Youtube Downloader

+ +
+ + +
+
+

Works with openInBrowser("https://github.com/yt-dlp/yt-dlp")} + >almost anything. Blazingly fast, I guess.

+

Made by openInBrowser("https://github.com/nxrmqlly")} + >@nxrmqlly with + + openInBrowser("https://youtu.be/dQw4w9WgXcQ")} + >❤ using Go and Svelte

+
+ + {#if showModal} + + {/if}
- diff --git a/frontend/src/components/ThemeSwitcher.svelte b/frontend/src/components/ThemeSwitcher.svelte new file mode 100644 index 0000000..2850ce8 --- /dev/null +++ b/frontend/src/components/ThemeSwitcher.svelte @@ -0,0 +1,61 @@ + + + + + +
+ {#if isDarkMode} + + {:else} + + {/if} +
diff --git a/frontend/src/components/VideoModal.svelte b/frontend/src/components/VideoModal.svelte new file mode 100644 index 0000000..ecdf459 --- /dev/null +++ b/frontend/src/components/VideoModal.svelte @@ -0,0 +1,36 @@ + + + + + + diff --git a/frontend/src/style.css b/frontend/src/style.css index 3940d6c..2023e05 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -1,26 +1,26 @@ +:root { + --accent: #99667a; + --duo-accent: #744457; +} + html { - background-color: rgba(27, 38, 54, 1); - text-align: center; - color: white; + text-align: center; + color: #100a0d; } body { - margin: 0; - color: white; - font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", - "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; + margin: 0; + height: 100vh; + overflow: hidden; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; } @font-face { - font-family: "Nunito"; - font-style: normal; - font-weight: 400; - src: local(""), - url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2"); -} - -#app { - height: 100vh; - text-align: center; + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + src: local(""), url("assets/fonts/nunito-v16-latin-regular.woff2") + format("woff2"); } diff --git a/frontend/src/utils/accessibility.js b/frontend/src/utils/accessibility.js new file mode 100644 index 0000000..8a68aa8 --- /dev/null +++ b/frontend/src/utils/accessibility.js @@ -0,0 +1,5 @@ +export function onAccesibilityKeydown(e) { + if (e.key !== "Enter" && e.key !== " ") return; + e.preventDefault(); + e.target.click(); +} \ No newline at end of file diff --git a/go.mod b/go.mod index 8924f25..a8f1d0d 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,7 @@ go 1.21 toolchain go1.22.1 -require ( - github.com/wader/goutubedl v0.0.0-20240910081116-fed2a6ab45a3 - github.com/wailsapp/wails/v2 v2.9.2 -) +require github.com/wailsapp/wails/v2 v2.9.2 require ( github.com/bep/debounce v1.2.1 // indirect diff --git a/go.sum b/go.sum index 8e03131..6ed0576 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,6 @@ github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3IS github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -59,10 +57,6 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/wader/goutubedl v0.0.0-20240910081116-fed2a6ab45a3 h1:4MhEgER7rSBGYMU1YcSYcU+gQU1Z/HbddFdQAme50i8= -github.com/wader/goutubedl v0.0.0-20240910081116-fed2a6ab45a3/go.mod h1:5KXd5tImdbmz4JoVhePtbIokCwAfEhUVVx3WLHmjYuw= -github.com/wader/osleaktest v0.0.0-20191111175233-f643b0fed071 h1:QkrG4Zr5OVFuC9aaMPmFI0ibfhBZlAgtzDYWfu7tqQk= -github.com/wader/osleaktest v0.0.0-20191111175233-f643b0fed071/go.mod h1:XD6emOFPHVzb0+qQpiNOdPL2XZ0SRUM0N5JHuq6OmXo= github.com/wailsapp/go-webview2 v1.0.16 h1:wffnvnkkLvhRex/aOrA3R7FP7rkvOqL/bir1br7BekU= github.com/wailsapp/go-webview2 v1.0.16/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= diff --git a/package.json b/package.json new file mode 100644 index 0000000..e48b7f2 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@fortawesome/fontawesome-free": "^6.6.0" + } +}