daily pastebin goal
58%
SHARE
TWEET

Open Directory to M3U Generator

a guest Feb 22nd, 2019 345 in 333 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * open directory to m3u generator
  3.  * Simple application to recurse through an open directory, download the list of files and make a playlist from it
  4.  *
  5.  * build with: go build opendirectorytom3u.go
  6.  */
  7.  
  8.  // fixme urls with # in will cause it to get the root url - why?
  9. // todo i think i need to ascertain the length of the file where possible as it can cause the end of the file to be cut short
  10. // todo optional parsing of filename-to-title and remove extension and convert "." to spaces
  11. // todo use go subroutines to recurse through the directories faster?
  12.  
  13. package main
  14.  
  15. import (
  16.     "fmt"
  17.     "io/ioutil"
  18.     "log"
  19.     "net/http"
  20.     "net/url"
  21.     "path/filepath"
  22.     "regexp"
  23.     "strings"
  24. )
  25.  
  26. func main() {
  27.     println(" ** Open Directory To M3U Generator **\n")
  28.  
  29.     // get open directory url
  30.     println("Enter the URL of the open directory, include http(s):// and omit trailing slash:")
  31.     baseURL := getUserInput()
  32.  
  33.     // ensure the url starts with http
  34.     if baseURL[:4] != "http" {
  35.         log.Fatalln("URL does not start with http")
  36.     }
  37.  
  38.     // remove trailing slash incase the user is a moron
  39.     if baseURL[len(baseURL) - 1:] == "/" {
  40.         baseURL = baseURL[:len(baseURL) - 1]
  41.     }
  42.  
  43.     println("Gathering files and directories, please wait...")
  44.  
  45.     // download the page
  46.     c := new(http.Client)
  47.  
  48.     urls := parseURLRecursively(baseURL, c)
  49.  
  50.     println("\nFinished parsing, let's write it to an M3U!")
  51.     println("Enter the output filename of the m3u")
  52.     fn := getUserInput()
  53.  
  54.     CreateM3U(baseURL, urls, fn)
  55.  
  56.     println("All done!")
  57. }
  58.  
  59. // generate the m3u file from a list of urls to files
  60. func CreateM3U(baseURL string, urls []string, outputFileName string) {
  61.  
  62.     // m3u header
  63.     m3u := "#EXTM3U\n"
  64.  
  65.     // add items to the m3u
  66.     for _, u := range urls {
  67.         // the -1 here denotes the length of the file, we don't know it, so -1 means 'not known'
  68.         encoded, _ := url.PathUnescape(u)
  69.         m3u += fmt.Sprintf("#EXTINF:-1, %s\n", encoded)
  70.         m3u += baseURL + "/" + u + "\n"
  71.     }
  72.  
  73.     // write the file to the specified output directory
  74.     ioutil.WriteFile(outputFileName, []byte(m3u), 0777)
  75. }
  76.  
  77. // parse an url and pull out directories and files
  78. func parseURLRecursively(theURL string, c *http.Client) []string {
  79.  
  80.     var urls []string
  81.  
  82.     s, err := url.PathUnescape(theURL)
  83.     resp, err := c.Get(s)
  84.     if err != nil {
  85.         log.Fatalln(err)
  86.     }
  87.  
  88.     // close the body of the current html response at the end of the function
  89.     defer resp.Body.Close()
  90.  
  91.     // parse the html for files and recurse through directories
  92.     var directories []string
  93.     var files []string
  94.  
  95.     if resp.StatusCode == http.StatusOK {
  96.  
  97.         // extract the html from the response
  98.         htmlBytes, err := ioutil.ReadAll(resp.Body)
  99.         if err != nil {
  100.             log.Fatalln(err)
  101.         }
  102.         html := string(htmlBytes)
  103.  
  104.         // get files
  105.         files = parseFiles(html)
  106.  
  107.         // add files to the list of urls
  108.         for _,f := range files {
  109.             urls = append(urls, f)
  110.         }
  111.  
  112.         // get directories
  113.         directories = parseDirectories(html)
  114.  
  115.         for _, dir := range directories {
  116.  
  117.             s, _ := url.PathUnescape(dir)
  118.  
  119.             // fixme for some reason, urls with # screw up, so we skip them for now
  120.             if strings.Contains(s, "#") {
  121.                 continue
  122.             }
  123.  
  124.             println(s)
  125.  
  126.             // get the new page
  127.             // todo is there a // instead of a / here?
  128.             subdirURL := theURL + "/" + dir
  129.  
  130.             newurls := parseURLRecursively(subdirURL, c)
  131.             if len(newurls) > 0 {
  132.  
  133.                 // todo is it possible to merge two arrays together rather than doing this?
  134.                 for _, u := range newurls {
  135.                     urls = append(urls, dir + "/" + u)
  136.                 }
  137.             }
  138.         }
  139.     }
  140.  
  141.     return urls
  142. }
  143.  
  144. // parse a directory recursively
  145. func parseDirectories(html string) []string {
  146.  
  147.     var dirs []string
  148.  
  149.     // we're using regex! so sue me!
  150.     reg, err := regexp.Compile("<a href=\"([^\"]+)/\">[^\"]+</a>")
  151.     if err != nil {
  152.         log.Fatalln(err)
  153.     }
  154.  
  155.     matches := reg.FindAllStringSubmatch(html, -1)
  156.     if matches == nil {
  157.         // no directories found
  158.         return dirs
  159.     }
  160.  
  161. //  println(fmt.Sprintf("Found %d sub-directories", len(matches)))
  162.  
  163.     for _, match := range matches {
  164.         m := match[1]
  165.  
  166.         dirs = append(dirs, m)
  167.     }
  168.  
  169.     return dirs
  170. }
  171.  
  172. // parse files
  173. func parseFiles(html string) []string {
  174.  
  175.     // only files with these extension will be added to the playlist output
  176.     // note the period is necessary as the first character
  177.     whitelistExtensions := []string{
  178.         ".mp3", ".mp4", ".flac", ".wmv", ".m4b", ".mov",
  179.         // ".mkv", // files that can't be streamed yet
  180.     }
  181.  
  182.     var files []string
  183.  
  184.     // we're using regex! so sue me!
  185.     reg, err := regexp.Compile("<a href=\"([^\"]+)\">[^\"]+</a>")
  186.     if err != nil {
  187.         log.Fatalln(err)
  188.     }
  189.  
  190.     matches := reg.FindAllStringSubmatch(html, -1)
  191.     if matches == nil {
  192.         return files
  193. //      log.Fatalln("no files found")
  194.     }
  195.  
  196. //  println(fmt.Sprintf("Found %d matches", len(matches)))
  197.  
  198.     for _, match := range matches {
  199.         m := match[1]
  200.  
  201.         // the first character of the extension returned from .Ext is the "."
  202.         ext := filepath.Ext(m)
  203.  
  204.         // check the extension is in the above extension whitelist
  205.         for _,e := range whitelistExtensions {
  206.  
  207.             // if found, then it's an extension we accept and we add it to the list of files
  208.             if ext == e {
  209.                 print(".")
  210. //              println(m)
  211.                 files = append(files, m)
  212.                 break
  213.             }
  214.         }
  215.     }
  216.  
  217.     return files
  218. }
  219.  
  220. // helper to get input from user
  221. func getUserInput() string {
  222.     var inp string
  223.     fmt.Scanln(&inp)
  224.     return inp
  225. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top