Skip to content

import / export opml file feature #530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/dustin/go-humanize v1.0.1
github.com/fatih/structs v1.1.0
github.com/fsnotify/fsnotify v1.7.0
github.com/gilliek/go-opml v1.0.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/gorilla/securecookie v1.1.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gilliek/go-opml v1.0.0 h1:X8xVjtySRXU/x6KvaiXkn7OV3a4DHqxY8Rpv6U/JvCY=
github.com/gilliek/go-opml v1.0.0/go.mod h1:fOxmtlzyBvUjU6bjpdjyxCGlWz+pgtAHrHf/xRZl3lk=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
Expand Down
8 changes: 8 additions & 0 deletions server/ctrladmin/adminui/pages/home.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@
<input class="md:col-start-2 col-span-2" type="text" name="feed" placeholder="rss feed url">
<input type="submit" value="add new">
</form>
<form id="opml-import-form" class="contents" action="{{ path "/admin/import_opml_podcast_do" }}" method="post" enctype="multipart/form-data">
<input class="md:col-start-2 col-span-1" type="text" name="feed" id="opml-text" placeholder="opml file" disabled>
<input type="button" value="import..." onclick="document.getElementById('opml-file').click()">
<input type="file" id="opml-file" name="opml-file" style="display:none;" onchange="document.getElementById('opml-text').value=document.getElementById('opml-file').value.match(/[^\\]*\.(\w+)$/im)[0]; document.getElementById('opml-import-form').submit()">
</form>
<form class="contents" action="{{ path "/admin/export_opml_podcast_do" }}" method="post">
<input type="submit" value="export...">
</form>
</div>
{{ end }}
{{ end }}
Expand Down
3 changes: 3 additions & 0 deletions server/ctrladmin/ctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ func New(dbc *db.DB, sessDB *gormstore.Store, scanner *scanner.Scanner, podcasts
c.Handle("/delete_internet_radio_station_do", adminChain(resp(c.ServeInternetRadioStationDeleteDo)))
c.Handle("/update_internet_radio_station_do", adminChain(resp(c.ServeInternetRadioStationUpdateDo)))

c.Handle("/import_opml_podcast_do", adminChain(resp(c.ServeImportOpmlPodcastDo)))
c.Handle("/export_opml_podcast_do", adminChain(respRaw(c.ServerExportOpmlPodcastDo)))

c.Handle("/", baseChain(resp(c.ServeNotFound)))

return &c, nil
Expand Down
101 changes: 101 additions & 0 deletions server/ctrladmin/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import (
"fmt"
"image"
"image/jpeg"
"io"
"log"
"net/http"
"net/url"
"sort"
"strconv"
"time"

"github.com/gilliek/go-opml/opml"
"github.com/gorilla/sessions"
"github.com/mmcdole/gofeed"
"github.com/nfnt/resize"

Expand Down Expand Up @@ -621,3 +624,101 @@ func doScan(scanner *scanner.Scanner, opts scanner.ScanOptions) {
}
}()
}

func (c *Controller) ServeImportOpmlPodcastDo(r *http.Request) *Response {
if err := r.ParseMultipartForm(10 << 20); err != nil {
return &Response{
redirect: "/admin/home",
flashW: []string{"error parseMultipartform opml file"},
}
}

file, _, err := r.FormFile("opml-file")
if err != nil {
log.Printf("error retrieving opml file: %s", err)
return &Response{
redirect: "/admin/home",
flashW: []string{"error retrieving opml file"},
}
}
defer file.Close()

fileBytes, err := io.ReadAll(file)
if err != nil {
log.Printf("error reading OPML file: %s", err)
return &Response{
redirect: "/admin/home",
flashW: []string{"error reading OPML file"},
}
}

doc, err := opml.NewOPML(fileBytes)
if err != nil {
return &Response{
redirect: "/admin/home",
flashW: []string{"error OPML file bad format"},
}
}

go func() {
fp := gofeed.NewParser()
for _, opmlOutline := range doc.Body.Outlines {
feed, err := fp.ParseURL(opmlOutline.XMLURL)
if err != nil {
log.Printf("could not parse url: %v\n", err)
continue
}

if _, err := c.podcasts.AddNewPodcast(opmlOutline.XMLURL, feed); err != nil {
log.Printf("could not add new podcast: %v\n", err)
}
}
}()

return &Response{
redirect: "/admin/home",
flashN: []string{"opml import started. refresh for results"},
}
}

func (c *Controller) ServerExportOpmlPodcastDo(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value(CtxSession).(*sessions.Session)
var podcastOMPL = &opml.OPML{
Version: "1.0",
Head: opml.Head{
Title: "Gonic Podcasts",
DateCreated: time.Now().Format(time.RFC3339),
OwnerName: "gonic",
},
}

data := &templateData{}
c.dbc.Find(&data.Podcasts)

for _, pod := range data.Podcasts {
podcastOMPL.Body.Outlines = append(podcastOMPL.Body.Outlines, opml.Outline{
Type: "rss",
Text: pod.Title,
Title: pod.Title,
XMLURL: pod.URL,
HTMLURL: pod.URL,
})
}

w.Header().Add("content-type", "application/opml")
w.Header().Add("content-disposition", "attachment; filename="+time.Now().Format(time.DateOnly)+"-gonic-podcasts.opml")
outp, err := podcastOMPL.XML()
if err != nil {
sessAddFlashW(session, []string{"error exporting opml file"})
sessLogSave(session, w, r)
http.Redirect(w, r, r.Referer(), http.StatusSeeOther)
return
}

if _, err := w.Write([]byte(outp)); err != nil {
sessAddFlashW(session, []string{"error writing opml file"})
sessLogSave(session, w, r)
http.Redirect(w, r, r.Referer(), http.StatusSeeOther)
return
}
}
Loading