Last active
August 3, 2018 22:32
-
-
Save gregtaole/c50d51086ce098e721a17cb8fbc38fde to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"encoding/json" | |
"encoding/xml" | |
"flag" | |
"fmt" | |
"io" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"os" | |
"os/exec" | |
"path" | |
"strings" | |
) | |
/* | |
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"> | |
<channel> | |
<title>Arch Linux: Releases</title> | |
<link>https://www.archlinux.org/download/</link> | |
<description>Release ISOs</description> | |
<atom:link href="https://www.archlinux.org/feeds/releases/" rel="self"></atom:link> | |
<language>en-us</language> | |
<lastBuildDate>Sun, 01 Jul 2018 05:03:32 +0000</lastBuildDate> | |
<item> | |
<title>2018.07.01</title> | |
<link>https://www.archlinux.org/releng/releases/2018.07.01/</link> | |
<description></description> | |
<pubDate>Sun, 01 Jul 2018 00:00:00 +0000</pubDate> | |
<guid isPermaLink="false">tag:www.archlinux.org,2018-07-01:/releng/releases/2018.07.01/</guid> | |
<enclosure url="https://www.archlinux.org/iso/2018.07.01/archlinux-2018.07.01-x86_64.iso.torrent" length="601882624" type="application/x-bittorrent"></enclosure> | |
</item> | |
<item> | |
<title>2018.06.01</title> | |
<link>https://www.archlinux.org/releng/releases/2018.06.01/</link> | |
<description></description> | |
<pubDate>Fri, 01 Jun 2018 00:00:00 +0000</pubDate> | |
<guid isPermaLink="false">tag:www.archlinux.org,2018-06-01:/releng/releases/2018.06.01/</guid> | |
<enclosure url="https://www.archlinux.org/iso/2018.06.01/archlinux-2018.06.01-x86_64.iso.torrent" length="598736896" type="application/x-bittorrent"></enclosure> | |
</item> | |
<item> | |
<title>2018.05.01</title> | |
<link>https://www.archlinux.org/releng/releases/2018.05.01/</link> | |
<description></description> | |
<pubDate>Tue, 01 May 2018 00:00:00 +0000</pubDate> | |
<guid isPermaLink="false">tag:www.archlinux.org,2018-05-01:/releng/releases/2018.05.01/</guid> | |
<enclosure url="https://www.archlinux.org/iso/2018.05.01/archlinux-2018.05.01-x86_64.iso.torrent" length="587202560" type="application/x-bittorrent"></enclosure> | |
</item> | |
</channel> | |
</rss> | |
*/ | |
type feed struct { | |
Channel channel `xml:"channel"` | |
} | |
type channel struct { | |
ItemList []item `xml:"item"` | |
} | |
type item struct { | |
Title date `xml:"title" json:"title"` | |
Enclosure enclosure `xml:"enclosure" json:"enclosure"` | |
} | |
type date string | |
func (t *date) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { | |
var v string | |
d.DecodeElement(&v, &start) | |
*t = date(v) | |
return nil | |
} | |
func (i item) String() string { | |
return fmt.Sprintf("%v, %v\n", i.Title, i.Enclosure.URL) | |
} | |
type enclosure struct { | |
URL url `xml:"url,attr" json:"url"` | |
} | |
type url string | |
func (u *url) UnmarshalXMLAttr(attr xml.Attr) error { | |
*u = url(attr.Value) | |
return nil | |
} | |
var ( | |
logFileFlag *string | |
dbFileFlag *string | |
torrentDirFlag *string | |
downloadDirFlag *string | |
logger *log.Logger | |
) | |
func init() { | |
logFileFlag = flag.String("l", "arch_release.log", "path to log file") | |
dbFileFlag = flag.String("o", "release_list.json", "json file where previous releases are listed") | |
torrentDirFlag = flag.String("d", "torrent", "directory where torrents are stored") | |
downloadDirFlag = flag.String("w", "ISOs", "directory where downloaded ISOs are stored") | |
flag.Parse() | |
logFile, err := os.OpenFile(*logFileFlag, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) | |
if err != nil { | |
log.Fatalf("could not open log file %v for writing : %v", *logFileFlag, err) | |
} | |
logger = log.New(logFile, "arch_release : ", log.LstdFlags|log.Lshortfile) | |
} | |
func main() { | |
resp, err := http.Get("https://www.archlinux.org/feeds/releases/") | |
if err != nil { | |
writeLog("could not get url : %v", err) | |
} | |
defer resp.Body.Close() | |
feed := &feed{} | |
body, err := ioutil.ReadAll(resp.Body) | |
if err != nil { | |
writeLog("could not read response body : %v", err) | |
} | |
err = xml.Unmarshal(body, feed) | |
if err != nil { | |
writeLog("could not unmarshal xml : %v", err) | |
} | |
torrentURL := string(feed.Channel.ItemList[0].Enclosure.URL) | |
_, err = os.Stat(*dbFileFlag) | |
if os.IsNotExist(err) { | |
dbFile, err := os.OpenFile(*dbFileFlag, os.O_CREATE|os.O_RDWR, 0644) | |
if err != nil { | |
writeLog("could not create database file %v : %v", *dbFileFlag, err) | |
} | |
jsonEncoder := json.NewEncoder(dbFile) | |
for _, item := range feed.Channel.ItemList { | |
err = jsonEncoder.Encode(item) | |
if err != nil { | |
writeLog("could not encode item to json : %v", err) | |
} | |
} | |
err = createTorrent(torrentURL) | |
if err != nil { | |
writeLog("could not create torrent file : %v", err) | |
} | |
} else { | |
dbFile, err := os.Open(*dbFileFlag) | |
if err != nil { | |
writeLog("could not open %v for reading : %v", *dbFileFlag, err) | |
} | |
jsonDecoder := json.NewDecoder(dbFile) | |
existing := []item{} | |
for jsonDecoder.More() { | |
var i item | |
err = jsonDecoder.Decode(&i) | |
if err != nil { | |
writeLog("could not decode json from %v : %v", *dbFileFlag, err) | |
} | |
existing = append(existing, i) | |
} | |
lastItem := feed.Channel.ItemList[0] | |
exists := false | |
for k := range existing { | |
if lastItem == existing[k] { | |
exists = true | |
} | |
} | |
if !exists { | |
err = createTorrent(torrentURL) | |
if err != nil { | |
writeLog("could not create torrent file : %v", err) | |
} | |
} | |
} | |
urlComp := strings.Split(torrentURL, "/") | |
torrentName := path.Join(*torrentDirFlag, urlComp[len(urlComp)-1]) | |
cmd := exec.Command("transmission-remote", "-w", *downloadDirFlag, "-a", torrentName) | |
err = cmd.Run() | |
if err != nil { | |
writeLog("error while running %v : %v", cmd.Args[0], err) | |
} | |
} | |
func writeLog(format string, v ...interface{}) { | |
fmt.Fprintf(os.Stderr, format, v) | |
logger.Fatalf(format, v) | |
} | |
func createTorrent(torrentURL string) error { | |
if _, err := os.Stat(*torrentDirFlag); os.IsNotExist(err) { | |
if err = os.MkdirAll(*torrentDirFlag, 0755); err != nil { | |
return fmt.Errorf("could not create destination folder for torrent files : %v", err) | |
} | |
} | |
urlComp := strings.Split(torrentURL, "/") | |
torrentName := path.Join(*torrentDirFlag, urlComp[len(urlComp)-1]) | |
torrentFile, err := os.Create(torrentName) | |
if err != nil { | |
return fmt.Errorf("could not create torrent file %v : %v", torrentName, err) | |
} | |
defer torrentFile.Close() | |
resp, err := http.Get(torrentURL) | |
if err != nil { | |
return fmt.Errorf("could not get torrent from address %v : %v", torrentURL, err) | |
} | |
defer resp.Body.Close() | |
_, err = io.Copy(torrentFile, resp.Body) | |
if err != nil { | |
return fmt.Errorf("could not write torrent file %v to disk : %v", torrentName, err) | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment