From a72404a977317267d458e9ee015fadd2450ea540 Mon Sep 17 00:00:00 2001 From: PrivateGER Date: Sat, 16 Oct 2021 16:48:59 +0200 Subject: [PATCH] Initial framework --- DirectoryIndexer.go | 108 +++++++++++++++++++++++++++++ go.mod | 7 ++ main.go | 158 ++++++++++++++++++++++++++++++++++++++++++ templates/index.html | 24 +++++++ templates/search.html | 19 +++++ templates/view.html | 30 ++++++++ 6 files changed, 346 insertions(+) create mode 100644 DirectoryIndexer.go create mode 100644 go.mod create mode 100644 main.go create mode 100644 templates/index.html create mode 100644 templates/search.html create mode 100644 templates/view.html diff --git a/DirectoryIndexer.go b/DirectoryIndexer.go new file mode 100644 index 0000000..980ff59 --- /dev/null +++ b/DirectoryIndexer.go @@ -0,0 +1,108 @@ +package main + +import ( + "fmt" + "github.com/dlclark/regexp2" + "io/ioutil" + "log" + "path/filepath" + "strings" + "sync" +) + +type FileList struct { + files map[string]VideoFile + mu sync.Mutex +} + +type VideoFile struct { + Filename string + Extension string + Title string + Id string +} + +var FL FileList + +func ScanDirectory(group *sync.WaitGroup, path string) { + defer group.Done() + + FL.mu.Lock() + defer FL.mu.Unlock() + + fmt.Println("Scanning archive...") + + fileList, err := ioutil.ReadDir(path) + if err != nil { + log.Println(err) + return + } + + FL.files = make(map[string]VideoFile) + for _, video := range fileList { + extension := filepath.Ext(video.Name())[1:] + // check if extension is one of valid yt-dlp extensions, if not ignore file + switch extension { + case "3gp", + "aac", + "flv", + "m4a", + "mp3", + "mp4", + "webm": + break + default: + continue + } + + id := filenameToID(video.Name()) + + FL.files[id] = VideoFile{ + Filename: video.Name(), + Extension: extension, + Title: filenameToTitle(video.Name(), extension), + Id: id, + } + } + + fmt.Println("Archive scan finished.") +} + +func filenameToID(filename string) string { + r := regexp2.MustCompile("([^[]+(?=]))", regexp2.RegexOptions(0)) + + matches := regexp2FindAllString(r, filename) + if len(matches) == 0 { + fmt.Println("Got video without square-bracket ID format. Falling back to youtube-dl 11-char string matching (!THIS MAY CAUSE ISSUES!):", filename) + + r = regexp2.MustCompile("-[A-Za-z0-9_-]{11}", regexp2.RegexOptions(0)) + matches = regexp2FindAllString(r, filename) + if len(matches) == 0 { + log.Fatal("Got a video file without ID. Please remove or fix: " + filename) + } + + // strips first dash away from the result (yes, I know this is DIRTY. + matches = []string{matches[len(matches)-1]} + matches[0] = matches[0][1:] + fmt.Println("Recovered ID:", matches[0]) + } + return matches[len(matches)-1] // last element = the id between square brackets +} + +func filenameToTitle(filename string, extension string) string { + title := strings.Replace(filename, "[" + filenameToID(filename) + "]", "", 1) + title = strings.Replace(title, "." + extension, "", 1) + title = strings.Replace(title, filenameToID(filename), "", 1) + + return title +} + +func regexp2FindAllString(re *regexp2.Regexp, s string) []string { + var matches []string + m, _ := re.FindStringMatch(s) + for m != nil { + matches = append(matches, m.String()) + m, _ = re.FindNextMatch(m) + } + return matches +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3b83705 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module ytdlp-viewer + +go 1.17 + +require github.com/dlclark/regexp v1.4.0 + +require github.com/dlclark/regexp2 v1.4.0 // indirect diff --git a/main.go b/main.go new file mode 100644 index 0000000..c8111a3 --- /dev/null +++ b/main.go @@ -0,0 +1,158 @@ +package main + +import ( + _ "embed" + "fmt" + "log" + "net/http" + "os" + "strconv" + "strings" + "sync" + "text/template" +) + +//go:embed templates/index.html +var indexTmplSource string + +//go:embed templates/search.html +var searchTmplSource string + +//go:embed templates/view.html +var viewTmplSource string + +type IndexPageData struct { + FileCount string + Files map[string]VideoFile +} + +type SearchPageData struct { + Results []VideoFile + ResultCount string + SearchTerm string +} + +type ViewPageData struct { + Title string + Filename string + Id string + Extension string +} + +func main() { + path := os.Getenv("directory") + if path == "" { + path = "Z:/MainArchive" + } + + var wg sync.WaitGroup + wg.Add(1) + + fmt.Println("Starting scanner at", path) + go ScanDirectory(&wg, path) + + wg.Wait() + + FL.mu.Lock() + fmt.Println(strconv.Itoa(len(FL.files))) + for _, file := range FL.files { + fmt.Println("Name:", file.Title,"Extension:", file.Extension, "ID:", file.Id) + } + FL.mu.Unlock() + + indexTmpl, err := template.New("index.tmpl").Parse(indexTmplSource) + if err != nil { + fmt.Println(err) + } + searchTmpl, err := template.New("search.tmpl").Parse(searchTmplSource) + if err != nil { + fmt.Println(err) + } + viewTmpl, err := template.New("view.tmpl").Parse(viewTmplSource) + if err != nil { + fmt.Println(err) + } + + http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { + FL.mu.Lock() + defer FL.mu.Unlock() + + data := IndexPageData{ + FileCount: strconv.Itoa(len(FL.files)), + Files: FL.files, + } + + err := indexTmpl.Execute(writer, data) + if err != nil { + fmt.Println(err) + } + }) + http.HandleFunc("/search", func(writer http.ResponseWriter, request *http.Request) { + FL.mu.Lock() + defer FL.mu.Unlock() + + keys, ok := request.URL.Query()["term"] + if !ok || len(keys[0]) < 1 { + log.Println("Url Param 'term' is missing") + return + } + + var results []VideoFile + + for _, video := range FL.files { + if video.Id == keys[0] { + results = append(results, video) + break + } + if strings.Contains(strings.ToUpper(video.Title), strings.ToUpper(keys[0])) { + results = append(results, video) + continue + } + } + + data := SearchPageData{ + Results: results, + ResultCount: strconv.Itoa(len(results)), + SearchTerm: keys[0], + } + + err := searchTmpl.Execute(writer, data) + if err != nil { + fmt.Println(err) + } + }) + http.HandleFunc("/view", func(writer http.ResponseWriter, request *http.Request) { + FL.mu.Lock() + defer FL.mu.Unlock() + + keys, ok := request.URL.Query()["id"] + if !ok || len(keys[0]) < 1 { + log.Println("Url Param 'id' is missing") + return + } + + if _, ok := FL.files[keys[0]]; !ok { + return + } + + video := FL.files[keys[0]] + data := ViewPageData{ + Title: video.Title, + Filename: video.Filename, + Id: video.Id, + Extension: video.Extension, + } + + err := viewTmpl.Execute(writer, data) + if err != nil { + fmt.Println(err) + } + }) + http.Handle("/videos/", http.StripPrefix("/videos/", http.FileServer(http.Dir(path)))) + + err = http.ListenAndServe(":8000", nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..f1efe94 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,24 @@ + + + + + ytdlp-viewer | {{.FileCount}} + + +
+

yt-dlp Viewer

+

{{.FileCount}} files in this archive

+ +
+ +
+ + {{range .Files}} + {{.Id}} - {{.Title}}
+ {{end}} +
+ + \ No newline at end of file diff --git a/templates/search.html b/templates/search.html new file mode 100644 index 0000000..3a6ae95 --- /dev/null +++ b/templates/search.html @@ -0,0 +1,19 @@ + + + + + Search result | {{.SearchTerm}} + + +
+ +
+ + {{range .Results}} + {{.Id}} - {{.Title}}
+ {{end}} + + \ No newline at end of file diff --git a/templates/view.html b/templates/view.html new file mode 100644 index 0000000..04acccb --- /dev/null +++ b/templates/view.html @@ -0,0 +1,30 @@ + + + + + {{.Title}} | {{.Id}} + + + + + +

{{.Title}}

+

Filetype: {{.Extension}}

+

Youtube ID: {{.Id}}

+

Filename: {{.Filename}}

+ direct video link + + + + + + \ No newline at end of file