Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit c2b64833e4b50c0369a6153e654c87ab35bee2ec @mxcl committed Mar 11, 2009
Showing with 621 additions and 0 deletions.
  1. +33 −0 Makefile
  2. +6 −0 README
  3. +10 −0 The/prefs.d
  4. +10 −0 The/user.d
  5. +1 −0 VERSION
  6. +49 −0 lib/PlainResponse.d
  7. +6 −0 lib/RoofParty.d
  8. +22 −0 lib/Shell.d
  9. +36 −0 lib/Utils.d
  10. +63 −0 lib/XmlRpc.d
  11. +57 −0 lib/os/paths.d
  12. +61 −0 main.d
  13. +15 −0 radio/Track.d
  14. +80 −0 radio/Xspf.d
  15. +62 −0 radio/adjust.d
  16. +73 −0 radio/handshake.d
  17. +37 −0 radio/stream.d
@@ -0,0 +1,33 @@
+SOURCES=*.d lib/*.d radio/*.d lib/os/*.d The/*.d
+VERSION=`cat VERSION`
+VERSION_D=lib/RoofParty_v.d
+DMD=dmd
+OUT=roofparty
+
+
+all: build
+
+build: $(VERSION_D) $(SOURCES)
+ $(DMD) -debug -of$(OUT) -odbuild/debug $(SOURCES)
+
+release: $(VERSION_D) $(SOURCES)
+ $(DMD) -inline -release -O -of$(OUT) -odbuild/release $(SOURCES)
+
+test: $(VERSION_D) $(SOURCES)
+ $(DMD) -unittest -debug -of$(OUT) -odbuild/debug $(SOURCES)
+
+$(VERSION_D): VERSION
+ @echo "// generated by make" > $(VERSION_D)
+ @echo "const char[] v = \"$(VERSION)\";" >> $(VERSION_D)
+
+install: all
+ mkdir -p $(DESTDIR)/usr/local/bin
+ cp $(OUT) $(DESTDIR)/usr/local/bin
+ strip $(DESTDIR)/usr/local/bin/$(OUT)
+
+clean:
+ rm -rf build
+ rm -f $(OUT)
+ rm -f $(VERSION_D)
+ @rm -f install.log
+ @for x in `find -name .\*~`; do rm $x; done
6 README
@@ -0,0 +1,6 @@
+Download and install the Tango dmd package, (you can install it to home, I
+use ~/opt/).
+
+http://www.dsource.org/projects/tango/wiki/DmdDownloads
+
+Ensure that dmd is in the path, (eg. ~/opt/bin), and make.
@@ -0,0 +1,10 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module The.prefs;
+
+
+char[] applicationFolder()
+{
+ return "";
+}
@@ -0,0 +1,10 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module The.user;
+
+
+char[] name = "2girls1cup";
+char[] password = "77e3c764678e809f1e72727c1f26e3f3";
+
+bool isDiscovery = false;
@@ -0,0 +1 @@
+0.1
@@ -0,0 +1,49 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module lib.PlainResponse;
+
+import tango.text.stream.LineIterator;
+import tango.io.Buffer;
+
+
+class PlainResponse
+{
+ this( char[] input )
+ {
+ foreach (line; new LineIterator!(char)( new Buffer( input ) ))
+ {
+ if (line.length is 0)
+ continue;
+
+ int find()
+ {
+ foreach (int n, char c; line)
+ if (c == '=')
+ return n;
+ return 0;
+ }
+
+ int n = find();
+ char[] first = line[0..n];
+ char[] s = line[n+1..$];
+ m_keys[first] = s;
+ }
+ }
+
+ char[] opIndex( char[] key )
+ {
+ char[]* v = key in m_keys;
+ return v ? *v : "";
+ }
+
+private:
+ char[][char[]] m_keys;
+}
+
+
+bool toBool( char[] s )
+{
+ return s == "1";
+}
+
@@ -0,0 +1,6 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module lib.RoofParty;
+
+public import RoofParty_v;
@@ -0,0 +1,22 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module lib.Shell;
+import tango.io.Console : Cerr;
+
+
+const char[] red = "\033[0;31m";
+const char[] purple = "\033[1;35m";
+const char[] yellow = "\033[1;33m";
+
+const char[] normal = "\033[0;0m";
+const char[] bold = "\033[1m";
+const char[] title = "\033[1;4m";
+
+const char[] reset = normal;
+
+
+void writeh( char[] text )
+{
+ Cerr( "\033[0;34m==>\033[0;0;1m " )( text )( reset ).newline;
+}
@@ -0,0 +1,36 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module lib.Utils;
+
+char[] percentEncoded( char[] s1 )
+{
+ char toHex( char c )
+ {
+ const char[] hexnumbers = "0123456789ABCDEF";
+ return hexnumbers[c & 0xf];
+ }
+
+ char[] s2;
+
+ foreach (char c; s1)
+ {
+ if (c >= 0x61 && c <= 0x7A // ALPHA
+ || c >= 0x41 && c <= 0x5A // ALPHA
+ || c >= 0x30 && c <= 0x39 // DIGIT
+ || c == 0x2D // -
+ || c == 0x2E // .
+ || c == 0x5F // _
+ || c == 0x7E) // ~
+ {
+ s2 ~= c;
+ }
+ else {
+ s2 ~= '%';
+ s2 ~= toHex( (c & 0xf0) >> 4 );
+ s2 ~= toHex( (c & 0xf) );
+ }
+ }
+
+ return s2;
+}
@@ -0,0 +1,63 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module lib.XmlRpc;
+
+public import tango.core.Variant;
+
+private import tango.text.convert.Integer : toInt;
+private import tango.text.Util : substitute;
+private import tango.text.xml.Document;
+private import tango.io.Console;
+
+
+private char[] unescape( char[] s )
+{
+ s.substitute( "&amp;", "&" );
+ s.substitute( "&lt;", "<" );
+ s.substitute( "&gt;", ">" );
+ return s;
+}
+
+
+private Variant parameter( char[] name, char[] value )
+{
+ switch (name)
+ {
+ case "i4":
+ case "int":
+ return Variant( toInt( value ) );
+
+ case "boolean":
+ return Variant( value == "1" );
+
+ case "string":
+ return Variant( unescape( value ) );
+
+ case "struct":
+ case "array":
+ default:
+ throw new Exception("Unhandled XmlRpc response parameter");
+ }
+}
+
+
+Variant[char[]] parse( char[] s )
+{
+ auto xml = new Document!( char );
+ xml.parse( s );
+
+ auto faults = xml.query["fault"];
+ if (faults.count)
+ throw new Exception("Fault present in XmlRpc response");
+
+ Variant[char[]] map;
+
+ foreach (e; xml.query["param"] )
+ {
+ Cout( e.name )( e.value ).newline;
+ map[e.name] = parameter( e.name, e.value );
+ }
+
+ return map;
+}
@@ -0,0 +1,57 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module lib.os.paths;
+import The.prefs;
+import tango.core.Exception;
+import tango.io.FilePath;
+import tango.sys.Environment;
+
+
+char[] applicationSupport()
+{
+ version( Windows )
+ {
+ static assert( 0 );
+ }
+ else
+ {
+ version( linux )
+ {
+ return Environment.get( "HOME", "/tmp" ) ~ "/.local/share/Last.fm/";
+ }
+ else
+ {
+ return Environment.get( "HOME", "/tmp" ) ~ "/Library/Application Support/Last.fm/";
+ }
+ }
+}
+
+
+char[] applicationFolder()
+{
+ return The.prefs.applicationFolder();
+}
+
+
+static this()
+{
+ //hideous, but effective
+ void mkpath( char[] path )
+ {
+ scope auto fp = new FilePath( path );
+
+ if (!fp.exists())
+ try
+ {
+ fp.createFolder();
+ }
+ catch (IOException)
+ {
+ mkpath( fp.parent() );
+ fp.createFolder();
+ }
+ }
+
+ mkpath( applicationSupport() );
+}
61 main.d
@@ -0,0 +1,61 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+import sh = lib.Shell : writeh;
+import radio.handshake;
+import radio.adjust;
+import radio.Track;
+import radio.Xspf;
+import radio.stream;
+import tango.io.Console;
+
+
+int main( char[][] args )
+{
+ if (args.length != 2)
+ return usage();
+
+ switch (args[1])
+ {
+ case "--version":
+ case "-v":
+ Cout( "RoofParty " )( RoofParty.v ).newline;
+ return 0;
+ case "--help":
+ usage();
+ Cout( "usage: roofparty [-v | --version]" ).newline;
+ return 0;
+ }
+
+ writeh( "Handshaking" );
+ handshake();
+
+ writeh( "Adjusting" );
+ adjust( args[1] );
+
+ for (;;)
+ {
+ writeh( "Fetching Playlist" );
+ auto xspf = new Xspf;
+
+ if (xspf.urls.length is 0)
+ break;
+
+ foreach (Track track; xspf.urls)
+ {
+ writeh( "Streaming `" ~ track.name ~ "'" );
+ stream( track.url );
+ }
+ }
+
+ Cerr( "No more tracks available for this station" ).newline;
+
+ return 0;
+}
+
+
+int usage()
+{
+ Cout( "usage: roofparty [" )( sh.purple )( "url" )( sh.reset )( "]" ).newline;
+ return 0;
+}
@@ -0,0 +1,15 @@
+// Copyright Max Howell <max@last.fm>
+// See the GNU General Public Licence for distribution semantics
+
+module radio.Track;
+
+struct Track
+{
+ char[] authCode;
+ char[] name;
+ char[] artist;
+ char[] album;
+ uint duration;
+ char[] sponsor;
+ char[] url;
+}
Oops, something went wrong.

0 comments on commit c2b6483

Please sign in to comment.