|
|
|
package DirectoryIndexers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"github.com/dlclark/regexp2"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FileList struct {
|
|
|
|
Files map[string]VideoFile
|
|
|
|
*sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
type VideoFile struct {
|
|
|
|
Filename string
|
|
|
|
Extension string
|
|
|
|
Title string
|
|
|
|
Id string
|
|
|
|
Metadata Metadata
|
|
|
|
}
|
|
|
|
|
|
|
|
func Index(path string, results chan FileList) {
|
|
|
|
var FL FileList
|
|
|
|
|
|
|
|
// Initialize the RWMutex HERE manually because *IT IS A POINTER TO A MUTEX*, so it defaults to a nil value
|
|
|
|
FL.RWMutex = &sync.RWMutex{}
|
|
|
|
FL.Lock()
|
|
|
|
|
|
|
|
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())
|
|
|
|
|
|
|
|
videoObject := VideoFile{
|
|
|
|
Filename: video.Name(),
|
|
|
|
Extension: extension,
|
|
|
|
Title: filenameToTitle(video.Name(), extension),
|
|
|
|
Id: id,
|
|
|
|
}
|
|
|
|
|
|
|
|
metadataFilename := strings.TrimSuffix(video.Name(), filepath.Ext(video.Name())) + ".info.json"
|
|
|
|
jsonMetadataFile, err := os.Open(path + metadataFilename)
|
|
|
|
if err != nil {
|
|
|
|
FL.Files[id] = videoObject
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
metadataBytes, _ := ioutil.ReadAll(jsonMetadataFile)
|
|
|
|
_ = jsonMetadataFile.Close()
|
|
|
|
|
|
|
|
metadata, err := ParseMetadata(metadataBytes)
|
|
|
|
if err != nil {
|
|
|
|
FL.Files[id] = videoObject
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
FL.Files[id] = VideoFile{
|
|
|
|
Filename: video.Name(),
|
|
|
|
Extension: extension,
|
|
|
|
Title: filenameToTitle(video.Name(), extension),
|
|
|
|
Id: id,
|
|
|
|
Metadata: metadata,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FL.Unlock()
|
|
|
|
|
|
|
|
results <- FL
|
|
|
|
close(results)
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|