<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"id3"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"unicode/utf8"
)

type SongInfo struct {
	Title, Album, Artist, Filename string
	full_filename string
}

type MusicService struct {
	music_path_root string
	songs []*SongInfo
}

func (ms *MusicService) ScanSongFile(path string) (*SongInfo, error) {
	if !utf8.ValidString(path) {
		return nil, errors.New(fmt.Sprintf("Invalid utf8 in filename: %s", path))
	}

	song := new(SongInfo)
	song.full_filename = path
	song.Filename = song.full_filename[len(ms.music_path_root)+1:]

	var fd, err = os.Open(path)
	if err != nil {
		return song, err
	}
	defer fd.Close()
	id3_data := id3.Read(fd)

	if id3_data != nil {
		song.Title = id3_data.Name
		song.Album = id3_data.Album
		song.Artist = id3_data.Artist
	}
	return song, nil
}

func (ms *MusicService) scanVisit(path string, info os.FileInfo, err error) error {
	if filepath.Ext(path) == ".mp3" {
		song, err := ms.ScanSongFile(path)
		if err != nil {
			fmt.Printf("Error: %s\n", err.Error())
		} else {
			ms.songs = append(ms.songs, song)
//			fmt.Printf("Scanned file %s\n", path)
		}
	}
	return nil
}

func (ms *MusicService) ScanMusic() {
	ms.songs = make([]*SongInfo, 0, 1000)
	filepath.Walk(ms.music_path_root, ms.scanVisit)
	fmt.Printf("len(ms.songs) = %d\n", len(ms.songs))
}

func (ms *MusicService) listHandler(w http.ResponseWriter, r *http.Request) {
	json_data, err := json.MarshalIndent(ms.songs, "", " ")
	if err != nil {
		fmt.Fprintf(w, "Error encoding data: %s\n", err.Error())
		return
	}
	w.Write(json_data)
}

func (ms *MusicService) getHandler(w http.ResponseWriter, r *http.Request) {
	filename := ms.music_path_root + "/" + r.URL.Path[5:]
	file, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(w, "404 Error: %s\n", err.Error())
                return
	}
	fileinfo, err := file.Stat()
	if err != nil {
		fmt.Fprintf(w, "404 Error: %s\n", err.Error())
                return
	}
	if fileinfo.IsDir() {
		fmt.Fprintf(w, "404 Error: %s is a directory, not a file\n", filename)
                return
	}
	io.Copy(w, file)
}

func main() {
	if len(os.Args) < 2 {
		panic(fmt.Sprintf("Usage: %s path/to/music", os.Args[0]))
	}
	service := new(MusicService)
	service.music_path_root = os.Args[1]
	service.ScanMusic()

	http.HandleFunc("/list", service.listHandler)
	http.HandleFunc("/get/", service.getHandler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>server/list now returns structured directory tree instead of flat filelist<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"unicode/utf8"
)

type Node struct {
	Name string
	Children []*Node
}

type MusicService struct {
	music_path_root string
	song_tree *Node
}

// filename is relative to ms.music_path_root
func (ms *MusicService) scanFile(filename string) (*Node, error) {
	if path.Ext(filename) != ".mp3" {
		return nil, errors.New("Skipping non-mp3 file")
	}
	if !utf8.ValidString(filename) {
		return nil, errors.New(fmt.Sprintf("Invalid utf8 in filename %s", filename))
	}
	node := new(Node)
	node.Name = filename
	return node, nil
}

// filename is relative to ms.music_path_root
func (ms *MusicService) scanDir(filename string) (*Node, error) {
	if !utf8.ValidString(filename) {
		return nil, errors.New(fmt.Sprintf("Invalid utf8 in filename %s", filename))
	}
	fullname := path.Join(ms.music_path_root, filename)
	
	file, err := os.Open(fullname)
	if err != nil {
		return nil, err
	}
	children, err := file.Readdir(0)
	if err != nil {
		return nil, err
	}
	node := new(Node)
	node.Name = filename
	node.Children = make([]*Node, 0, len(children))

	for _, child := range(children) {
		child_path := path.Join(filename, child.Name())
		fmt.Printf("child is %s\n", child_path)
		var child_node *Node;
		if child.IsDir() {
			child_node, err = ms.scanDir(child_path)
		} else {
			child_node, err = ms.scanFile(child_path)
		}
		if child_node != nil {
			node.Children = append(node.Children, child_node)
		}
	}
	if len(node.Children) == 0 {
		return nil, nil
	}
	return node, nil
}

func (ms *MusicService) ScanMusic() {
	var err error
	ms.song_tree, err = ms.scanDir("")
	if err != nil {
		fmt.Printf("Error scanning music: %s\n", err.Error())
	}
}

func (ms *MusicService) listHandler(w http.ResponseWriter, r *http.Request) {
	json_data, err := json.MarshalIndent(ms.song_tree, "", " ")
	if err != nil {
		fmt.Fprintf(w, "Error encoding data: %s\n", err.Error())
		return
	}
	w.Write(json_data)
}

func (ms *MusicService) getHandler(w http.ResponseWriter, r *http.Request) {
	filename := ms.music_path_root + "/" + r.URL.Path[5:] // 5 is length of '/get/'
	file, err := os.Open(filename)
	if err != nil {
		fmt.Fprintf(w, "404 Error: %s\n", err.Error())
                return
	}
	fileinfo, err := file.Stat()
	if err != nil {
		fmt.Fprintf(w, "404 Error: %s\n", err.Error())
                return
	}
	if fileinfo.IsDir() {
		fmt.Fprintf(w, "404 Error: %s is a directory, not a file\n", filename)
                return
	}
	io.Copy(w, file)
}

func main() {
	if len(os.Args) < 2 {
		panic(fmt.Sprintf("Usage: %s path/to/music", os.Args[0]))
	}
	service := new(MusicService)
	service.music_path_root = os.Args[1]
	service.ScanMusic()

	http.HandleFunc("/list", service.listHandler)
	http.HandleFunc("/get/", service.getHandler)
	http.ListenAndServe(":8080", nil)
}

{'fraction_non_alphanumeric': 0.16060155959896028, 'fraction_numerical': 0.011511325659116228, 'mean_word_length': 3.688424717145344, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2592266', 'n_tokens_mistral': 2175, 'n_tokens_neox': 2048, 'n_words': 487}