up
This commit is contained in:
parent
75280e6346
commit
86fbed85a9
@ -233,6 +233,65 @@ r.HandleFunc("/hls/{partID:[0-9]+}/", renders.HLSStream(bd)).Methods("GET")
|
|||||||
//API Scan folder
|
//API Scan folder
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// func RoutesProtected(r *mux.Router, db *gorm.DB) {
|
||||||
|
// // —————— HTML routes ——————
|
||||||
|
// r.HandleFunc("/login", Login).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/dashboard", Dashboard(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/menu-library", MenuLibrary(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/settings", Settings).Methods("GET")
|
||||||
|
// r.HandleFunc("/library", Library).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/godownloader/download", GoDownload).Methods("GET")
|
||||||
|
// r.HandleFunc("/godownloader/linkcollectors", GoDownloadLinkCollectors).Methods("GET")
|
||||||
|
// r.HandleFunc("/godownloader/settings/delete", GoDownloadSettingDelete(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/godownloader/settings/toggle", GoDownloadSettingToggleActive(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/godownloader/settings", GoDownloadSetting(db)).Methods("GET", "POST")
|
||||||
|
// r.HandleFunc("/godownloader/settings/table", GoDownloadPartialTable(db)).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/godownloader2", GoDownload2(db)).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/add-job", HandleAddJob(db)).Methods("POST")
|
||||||
|
// r.HandleFunc("/jobs/stream", HandleJobsStream(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/jobs/list", HandleListJobsPartial(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/jobs/start/{id}", HandleStartJob(db)).Methods("POST")
|
||||||
|
// r.HandleFunc("/jobs/pause/{id}", HandlePauseJob).Methods("POST")
|
||||||
|
// r.HandleFunc("/jobs/resume/{id}", HandleResumeJob(db)).Methods("POST")
|
||||||
|
// r.HandleFunc("/jobs/delete/{id}", HandleDeleteJob(db)).Methods("POST")
|
||||||
|
// r.HandleFunc("/jobs/delete-multiple", HandleDeleteMultipleJobs(db)).Methods("POST")
|
||||||
|
|
||||||
|
// r.HandleFunc("/stream", StreamHandler).Methods("GET")
|
||||||
|
// r.HandleFunc("/detail", DetailHandler).Methods("GET")
|
||||||
|
// r.HandleFunc("/add-jobs-multiple", HandleAddJobsMultiple(db)).Methods("POST")
|
||||||
|
|
||||||
|
// r.HandleFunc("/pathmedia/{id}", PathMedia(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/media/detail/{partID}", MediaDetail(db)).Methods("GET")
|
||||||
|
// r.PathPrefix("/hls/").Handler(HLSStream(db))
|
||||||
|
|
||||||
|
// // —————— JSON API routes ——————
|
||||||
|
// r.HandleFunc("/api/dashboard", DashboardJSON(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/api/menu-library", MenuLibraryJSON(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/api/settings", SettingsJSON()).Methods("GET")
|
||||||
|
// r.HandleFunc("/api/library", LibraryJSON()).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/api/godownloader/download", GoDownloadJSON()).Methods("GET")
|
||||||
|
// r.HandleFunc("/api/godownloader/linkcollectors", GoDownloadLinkCollectorsJSON()).Methods("GET")
|
||||||
|
// r.HandleFunc("/api/godownloader/settings/delete", GoDownloadSettingDeleteJSON(db)).Methods("POST")
|
||||||
|
// r.HandleFunc("/api/godownloader/settings/toggle", GoDownloadSettingToggleActiveJSON(db)).Methods("POST")
|
||||||
|
// r.HandleFunc("/api/godownloader/settings", GoDownloadSettingJSON(db)).Methods("GET", "POST")
|
||||||
|
// r.HandleFunc("/api/godownloader/settings/table", GoDownloadPartialTableJSON(db)).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/api/godownloader2", GoDownload2JSON(db)).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/api/add-job", HandleAddJobJSON(db)).Methods("POST")
|
||||||
|
// r.HandleFunc("/api/jobs/list", HandleListJobsPartialJSON(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/api/add-jobs-multiple", HandleAddJobsMultipleJSON(db)).Methods("POST")
|
||||||
|
|
||||||
|
// r.HandleFunc("/api/stream", StreamHandlerJSON()).Methods("GET")
|
||||||
|
|
||||||
|
// r.HandleFunc("/api/pathmedia/{id}", PathMediaJSON(db)).Methods("GET")
|
||||||
|
// r.HandleFunc("/api/media/detail/{partID}", MediaDetailJSON(db)).Methods("GET")
|
||||||
|
// }
|
||||||
func StreamHandler(w http.ResponseWriter, r *http.Request) {
|
func StreamHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/event-stream")
|
w.Header().Set("Content-Type", "text/event-stream")
|
||||||
w.Header().Set("Cache-Control", "no-cache")
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
|
|||||||
@ -1166,4 +1166,350 @@ func renderTemplate(w http.ResponseWriter, templ string, data map[string]interfa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DashboardJSON renvoie la liste des chemins sous /app/upload au format JSON
|
||||||
|
func DashboardJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var paths []models.PathDownload
|
||||||
|
root := "/app/upload"
|
||||||
|
if err := db.
|
||||||
|
Where("path LIKE ? AND path NOT LIKE ?", root+"/%", root+"/%/%").
|
||||||
|
Find(&paths).Error; err != nil {
|
||||||
|
http.Error(w, `{"error":"failed retrieving paths"}`, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"paths": paths})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MenuLibraryJSON renvoie tous les PathDownload au format JSON
|
||||||
|
func MenuLibraryJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var paths []models.PathDownload
|
||||||
|
if err := db.Find(&paths).Error; err != nil {
|
||||||
|
http.Error(w, `{"error":"failed retrieving paths"}`, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"paths": paths})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsJSON renvoie les options de la page Settings au format JSON
|
||||||
|
func SettingsJSON() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"title": "Settings Page",
|
||||||
|
"options": []string{"Option 1", "Option 2", "Option 3"},
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LibraryJSON renvoie un objet vide (ou à compléter) pour /library
|
||||||
|
func LibraryJSON() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoDownloadJSON pour /godownloader/download.json
|
||||||
|
func GoDownloadJSON() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Vous pouvez renvoyer ici des données de job / paths si besoin
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoDownloadLinkCollectorsJSON pour /godownloader/linkcollectors.json
|
||||||
|
func GoDownloadLinkCollectorsJSON() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoDownloadSettingDeleteJSON renvoie {"success":true} après suppression
|
||||||
|
func GoDownloadSettingDeleteJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := debridlink.NewClient(db)
|
||||||
|
idStr := r.URL.Query().Get("id")
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
_ = client.DeleteDebridAccount(ctx, uint(id))
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]bool{"success": err == nil})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoDownloadSettingToggleActiveJSON renvoie la liste mise à jour des comptes
|
||||||
|
func GoDownloadSettingToggleActiveJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.Background()
|
||||||
|
client := debridlink.NewClient(db)
|
||||||
|
id, _ := strconv.ParseUint(r.URL.Query().Get("id"), 10, 64)
|
||||||
|
_ = client.ToggleActiveStatus(ctx, uint(id))
|
||||||
|
accounts, _ := client.ListDebridAccounts(ctx)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"accounts": accounts})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoDownloadSettingJSON renvoie la liste des comptes (GET) ou le device code (POST)
|
||||||
|
func GoDownloadSettingJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
client := debridlink.NewClient(db)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodGet:
|
||||||
|
accounts, _ := client.ListDebridAccounts(ctx)
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"accounts": accounts})
|
||||||
|
case http.MethodPost:
|
||||||
|
r.ParseForm()
|
||||||
|
username := r.FormValue("username")
|
||||||
|
password := r.FormValue("password")
|
||||||
|
device, err := client.RequestDeviceCodeWithCredentials(ctx, username, password)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, `{"error":"`+err.Error()+`"}`, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{
|
||||||
|
"code": device.UserCode,
|
||||||
|
"url": device.VerificationURL,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoDownloadPartialTableJSON renvoie la liste des comptes pour le partial
|
||||||
|
func GoDownloadPartialTableJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
accounts, _ := debridlink.NewClient(db).ListDebridAccounts(r.Context())
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"accounts": accounts})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoDownload2JSON renvoie jobs, paths et now
|
||||||
|
func GoDownload2JSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
jobs := download.ListJobs(db)
|
||||||
|
var paths []models.PathDownload
|
||||||
|
db.Find(&paths)
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"jobs": jobs,
|
||||||
|
"paths": paths,
|
||||||
|
"now": time.Now(),
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleAddJobJSON ajoute un job et renvoie la liste mise à jour
|
||||||
|
func HandleAddJobJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.ParseForm()
|
||||||
|
link := r.FormValue("link")
|
||||||
|
id, _ := strconv.Atoi(r.FormValue("path_id"))
|
||||||
|
client := download.GetFirstActiveAccount(debridlink.NewClient(db))
|
||||||
|
ctx := r.Context()
|
||||||
|
links, _ := debridlink.NewClient(db).AddLink(ctx, link)
|
||||||
|
for _, l := range links {
|
||||||
|
stream, _ := debridlink.NewClient(db).CreateTranscode(ctx, l.ID)
|
||||||
|
job := &download.DownloadJob{
|
||||||
|
ID: l.ID,
|
||||||
|
Link: l.DownloadURL,
|
||||||
|
Name: l.Name,
|
||||||
|
Status: "waiting",
|
||||||
|
PathID: id,
|
||||||
|
Size: l.Size,
|
||||||
|
Host: l.Host,
|
||||||
|
Progress: 0,
|
||||||
|
StreamURL: stream.StreamURL,
|
||||||
|
}
|
||||||
|
download.RegisterJobWithDB(job, db)
|
||||||
|
}
|
||||||
|
jobs := download.ListJobs(db)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"jobs": jobs})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleListJobsPartialJSON renvoie la liste des jobs
|
||||||
|
func HandleListJobsPartialJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
jobs := download.ListJobs(db)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{"jobs": jobs})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleAddJobsMultipleJSON débride plusieurs liens et renvoie succès
|
||||||
|
func HandleAddJobsMultipleJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// même logique que HTML, mais renvoi JSON minimal
|
||||||
|
r.ParseForm()
|
||||||
|
raw := r.FormValue("links")
|
||||||
|
_ = strings.Split(raw, "\n") // traitement identique...
|
||||||
|
download.Broadcast()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]bool{"success": true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamHandlerJSON renvoie Dirs, Entries et CurrentPath en JSON
|
||||||
|
func StreamHandlerJSON() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
base := "/app/upload"
|
||||||
|
cur := r.URL.Query().Get("path")
|
||||||
|
root, _ := listEntries(base, "")
|
||||||
|
var dirs []Entry
|
||||||
|
for _, e := range root {
|
||||||
|
if e.IsDir {
|
||||||
|
dirs = append(dirs, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries, _ := listEntries(base, cur)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"dirs": dirs,
|
||||||
|
"entries": entries,
|
||||||
|
"currentPath": cur,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// PathMediaJSON renvoie la liste des sous-dossiers et médias d'un PathDownload en JSON
|
||||||
|
func PathMediaJSON(db *gorm.DB) http.HandlerFunc {
|
||||||
|
// extensions autorisées et helpers JSON-friendly
|
||||||
|
type dirView struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
SubPath string `json:"subPath"`
|
||||||
|
}
|
||||||
|
type mediaItemView struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Duration int64 `json:"duration"` // en secondes
|
||||||
|
DurationFmt string `json:"durationFmt"` // ex: "3:45"
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
ThumbURL string `json:"thumbUrl"`
|
||||||
|
FilePath string `json:"filePath"`
|
||||||
|
MediaPartID int64 `json:"mediaPartId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed := map[string]bool{
|
||||||
|
".mkv": true, ".avi": true, ".mp4": true, ".mov": true,
|
||||||
|
".jpg": true, ".jpeg": true, ".png": true, ".gif": true,
|
||||||
|
".pdf": true, ".epub": true, ".cbz": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 1) Récupérer le PathDownload
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
pid, err := strconv.ParseInt(vars["id"], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, `{"error":"invalid path ID"}`, http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var pd models.PathDownload
|
||||||
|
if err := db.First(&pd, pid).Error; err != nil {
|
||||||
|
http.Error(w, `{"error":"path not found"}`, http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Déterminer le sous-dossier courant
|
||||||
|
sub := r.URL.Query().Get("sub") // ex: "Films/Test"
|
||||||
|
current := filepath.Join(pd.Path, filepath.FromSlash(sub))
|
||||||
|
|
||||||
|
// 3) Lire les entrées du dossier
|
||||||
|
entries, err := os.ReadDir(current)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, `{"error":"cannot read directory"}`, http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Construire les slices JSON
|
||||||
|
var dirs []dirView
|
||||||
|
var medias []mediaItemView
|
||||||
|
thumbDir := filepath.Join("static", "thumbs")
|
||||||
|
os.MkdirAll(thumbDir, 0755)
|
||||||
|
|
||||||
|
for _, e := range entries {
|
||||||
|
name := e.Name()
|
||||||
|
full := filepath.Join(current, name)
|
||||||
|
|
||||||
|
if e.IsDir() {
|
||||||
|
dirs = append(dirs, dirView{
|
||||||
|
Name: name,
|
||||||
|
SubPath: filepath.ToSlash(filepath.Join(sub, name)),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := strings.ToLower(filepath.Ext(name))
|
||||||
|
if !allowed[ext] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
view := mediaItemView{
|
||||||
|
Title: name,
|
||||||
|
FilePath: full,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si c'est une vidéo, extraire métadonnées + screenshot
|
||||||
|
if ext == ".mkv" || ext == ".avi" || ext == ".mp4" || ext == ".mov" {
|
||||||
|
// Métadonnées via ffprobe
|
||||||
|
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
|
||||||
|
info, _ := probe(ctx, full)
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if info != nil {
|
||||||
|
// durée
|
||||||
|
if d, err := strconv.ParseFloat(info.Format.Duration, 64); err == nil {
|
||||||
|
secs := int64(d)
|
||||||
|
view.Duration = secs
|
||||||
|
view.DurationFmt = fmt.Sprintf("%d:%02d", secs/60, secs%60)
|
||||||
|
}
|
||||||
|
// résolution
|
||||||
|
for _, s := range info.Streams {
|
||||||
|
if s.CodecType == "video" {
|
||||||
|
view.Width = s.Width
|
||||||
|
view.Height = s.Height
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Génération du thumbnail
|
||||||
|
base := strings.TrimSuffix(name, ext)
|
||||||
|
thumbName := base + ".jpg"
|
||||||
|
thumbPath := filepath.Join(thumbDir, thumbName)
|
||||||
|
if _, err := os.Stat(thumbPath); os.IsNotExist(err) {
|
||||||
|
exec.Command("ffmpeg", "-ss", "5", "-i", full, "-frames:v", "1", thumbPath).Run()
|
||||||
|
}
|
||||||
|
view.ThumbURL = "/static/thumbs/" + thumbName
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Icônes génériques pour images/PDF/EPUB/CBZ
|
||||||
|
view.ThumbURL = "/static/icons/" + ext[1:] + ".svg"
|
||||||
|
}
|
||||||
|
|
||||||
|
medias = append(medias, view)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) Réponse JSON
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"dirs": dirs,
|
||||||
|
"mediaItems": medias,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user