Jump to content

Pandoracaster - Streaming Pandora Server!


ZigZagJoe

Recommended Posts

So basically i've been a little bored oer the last two weeks, and after shooting the wind with pegasus some I had an idea.

To keep it simple, it streams pandora radio over a shoutcast protocol, and presents a web interface to manage it.

Presenting (copyn paste from readme.txt):

** PandoraCaster v1.0 by ZZJ **

Application to serve Pandora Radio via a shoutcast-like protocol.

Provides a web interface to control various pandora functions.

Features:

* Fully supported by any media player that supports shoutcast!

* Supports "instant" station change, or two types of delayed

* Supports skipping of current OR next song

* Streams at 128kbs or 192kbs mp3 (depends on pandora one)

* Low system resource usage; no re-encoding music like shoutcast

* Supports using proxies, including SOCKS [tor]

* Instant buffering for newly connecting clients

* Fully supported under mono, and does not require an X server

* Can stream local only, over the network, or even over the internet

* Supports pandora feedback and implicit station change links

* Extensible Web interface w/ AJAX built-in, see below.

Behavior/Quirks:

Playback will be paused until a client connects, or if all connected clients are paused

Slow clients will finish their current song when overflow is reached then skip to realtime

Distribution agorithm is not quite perfect; multiple clients may overflow over time

Don't chain this with sproxy and enable turbo mode, bad things will happen.

DOES NOT SAVE MUSIC! NEVER WILL! DON'T ASK!

Requires:

.NET 2.0 or Mono

~64mb RAM, max

~300kb disk space (+log)

Any CPU

==== USAGE ====

Usage: (mono) Caster.exe [arguments]
---- ARGUMENTS --------------------------------
--[linux|wine|nossl]   - disable SSL for login, neccisary for WINE/mono (mono will be detected automatically)
-h, --help             - this
-l, --login            - change saved login info and validate interactively, then exit
-a, --promiscuous      - listen on all interfaces instead of loopback only
-p, --port [port]      - sets the server's listen port
-n, --name [name]      - sets server name (hostname/domain, or IP)
-d, --debug            - enables debug logging
-s, --save             - save settings set via cmd line (they are temporary otherwise)

By default, the caster only listens on localhost and will not allow external connections.

To change this, you can either use the command line options detailed above, or you can edit caster.conf

In the config, change LocalOnly to false, and set ServerName to a sensible value.

--OR--

Pass -n "name" -a -s to caster.exe, where name is a name that can be resolved to the current computer. an IP works as well.

The web interface is located at http://servername:port/ - nothing fancy to it.

If the status page is not automatically refreshing on song change, make sure JS is enabled and you only have one window open, as some browsers will not duplicate requests, but not return the duplicate when completed, so only one page will update. (I'm looking at you, firefox.)

==== caster.conf settings ====

An empty setting is stored as <string />, otherwise <string>value</string>

"" in the chart below means empty, not literal ""

---------------------------------------------------------------------------------------------
| NAME           |    DEFAULT        |    SHORT DESC
---------------------------------------------------------------------------------------------
StationChangeMode | 2               | behavior when changing station, see below
ProxyNeedAuth      | false        | proxy needs authentication
ProxyType      | ""              | blank=off, http, socks4, socks4a, socks5
ProxyAddr      | 127.0.0.1         | valid ipv4 IP addr for the proxy
ProxyPort      | 8080        | proxy port
ProxyUser      | ""               | plaintext user
ProxyPass      | ""              | plaintext pass
ProxyXMLOnly      | true        | only use proxy for XML data
CacheSize      | (256*1024)         | amount downloaded before a song is queued
Username      | ""              | encoded user, do not mess with this
Password      | ""              | encoded pass, use -login to change login
CurrentStation      | ""              | last station ID
ListenPort      | 3001        | server listen port
LocalOnly      | true        | only allow connections from localhost
ServerName      | Dns.GetHostName()     | server name, see below. domain, hostname, or ip
NoSSL          | false                 | don't use SSL, needed on mono/wine
Debug          | false                 | print additional debug output
ServeLocalFiles      | false         | serve files in LocalFilesPath
LocalFilesPath      | ./www/        | directory to serve files from
MaxListeners      | 32              | max # of listeners that can be connected
AnonWebControl      | true        | allow anyone to skip and rate songs
NoWebStyle      | false        | don't use the embedded stylesheet
LowMem          | false        | use disk backing for song data

Notes:

StationChangeMode

0 = play this song and the next song, station changes after that

1 = skip next song, if possible, new song will be of the new station

2 = skip this song and next song (change to new station "immediately")

ServerName

needs to be a domain, ip, or hostname

which the listenport connects to _this_ system

unimportant if in localhost only mode.

AnonWebControl

On by default, when disabled, to perform any actions that affect the broadcast you must log in using the same password used to log in to pandora.

==== Web interface ====

If AnonWebControl is false, you need to log in prior to using web interface functions like skip, song ratings, station changing, etc.

The web interface's extensibility is enabled by default. To disable, set ServeLocalFiles to false.

The path served is set via LocalFilesPath.

File starting with . will not be served, or .., though relative paths are supported within a url (http://server/dir/../otherdir/file.ext)

Edit LocalFilesPath/.links to add new navigation links.

If a file's name matches a built in URL, the built in page will be overridden.

Create a file named index.htm (or html) to replace the status page (/).

If you have a file with extension html, htm, txt, xml, or css in the dat file, a few variables will be replaced when it is displayed:

%statusjson% -> json formatted status (same as /status request)

%stationsjson% -> json formatted stations (same as /stations request)

%listenport% -> server listen port

%servername% -> server name

%serverurl% -> full server url (http://servername:serverip/)

%nowplaying% -> now playing song (artist - title)

%station% -> current station

%style% -> embedded stylesheet

%authenticated% -> is current requestor authenticated? (true | false)

%header% -> standard header with nav

%footer% -> standard footer

Built-in paths:

/             | built in status page
/;             | serves audio stream (shoutcast emulation requirement)
/listen            | serves audio stream
/listen/id        | attempts to change station to ID and serves audio stream
/station/id        | sets station to ID
/lovecurrent        | loves the now playing song
/hatecurrent        | hates the now playing song
/skip            | skips the now playing song
/skipnext        | skips the next song
/listeners        | table of listeners and various info for debugging
/listeners/ar        | auto-refresh listeners page
/newstation        | new station ajax page
/history        | returns song history
/historyjson        | returns song history as a json object
/status            | returns a JSON object with various now playing info
/stations        | returns a JSON object with stations
/changestations        | page to change stations for non-js browsers
/nextsong        | returns the next song artist-title. blocks until next song ready.
/7.html            | now playing in shoutcast's 7.html format
/nowplaying         | returns now playing song's artist-title
/autocomplete/query    | queries pandora's autocomplete server
/loadsharedstation/sid    | loads a shared station from sid and changes to it (returns 0|1)
/createstation/id    | trie creates a new station from ID and switches to it (returns 0|1)
/createstation/id/redir | tries to create a station then redirects to status page
/songchanged        | blocks until song changes, then returns status
/blank.png        | 1 x 1px transparent png
/listen.m3u        | returns listen playlist in m3u format
/listen.pls        | returns listen playlist in pls format
/favicon.ico        | returns (static) favicon
/setstationchgmode/0-2  | sets station change mode (0-2, see above for details)
/clearmaxdata        | clear each listener's maxdata variable (for listeners page)

Most of the actions like skip return a 302 redirect back to the status page.

==== WARRANTY (or lack therof) ====

No function is implied or in any way guaranteed. Try not to hurt yourself.

==== THANKS ====

pegasus, from IRC: giving me the intial idea and helping with the broadcast "algorithm"

NAudio project: for the Id3v2Tag code and some random mp3 reading bits

lizthethegray, lassiter, promyloph, and the rest of #saver2: getting pandora's new keys

==== MISC ====

Just shy of 5k lines of source. Pretty much the culmination of SProxy and the Pandora client, go go code reuse! Plus bits from saver2, and other utilities I wrote.

Not open source atm, and honestly not likely to become so without VERY good reason.

----------------------------------------------------------------

*** Pandora is a registered trademark of Pandora Media, Inc. ***

----------------------------------------------------------------

Edited by ZigZagJoe
Link to comment
Share on other sites

Updated. Now working again.

I've got it installed and running (via mono), however, I'm unable to actually play any streams.

The daemon appears to be running without issue:

** Now broadcasting S335219: In Flames - Colony (4:37)
--- Listening on all interfaces, on port 3001 ---
Server Name = box
Broadcaster: Waiting for a listener to connect...
S335219: Download completed
S131197: Get URL http://audio-sjl-t3-1.pandora.com/access [...]
S131197: File size 6327065; length 4:23
** Coming up next: Lamb of God - Blood Junkie
S131197: Download completed
Server: Loaded web resources; 4 entries
WEBRES: A flash stream player is available at /res
[192.168.40.179:55849]   GET /res/
[192.168.40.179:55849]   GET /favico.ico
[192.168.40.179:55849]   GET /res/minicaster.xml

I can connect to the web interface, however, when I try to use the flash player -- nothing happens. The player loads, but it doesn't play anything.

I've also tried playing the .pls file, again, nothing.

* Edit: I've also tried on a WinXP machine. Same result.

Edited by undertow
Link to comment
Share on other sites

So far, this is working perfectly! I'm interfacing with the API and controlling it via a custom front end. I do, however, have a couple of a suggestions I hope you could consider:

* Local audio. The ability the hear audio on the machine hosting PandoraCaster (without streaming)

- Ideally, I would like to toss this onto a headless machine and connect the line-out to my A/V receiver -- i.e. a Pandora appliance.

* Play/pause support. Provided Local audio could be supported, the ability to play/pause the current media would be fantastic, perhaps via a /playpause call.

* A JSON call for /history

Functionality wise, so far things are solid. :)

Edited by undertow
Link to comment
Share on other sites

Glad to hear it.

Play pause is actually supported in a manner already - if all connected clients are not accepting data (or none are connected), playback is essentially paused. Winamp and mplayer stop accepting data when their buffer is full, so there you are.

Local playback would honestly be a pain to implement, especially in a cross-platform compatible way. I have a library called naudio on windows, but it doesn't support mono in any way shape or form due to using dllimports to the windows api. Would be possible to add an auto-launch command, at least... putting a full built in player would be a pain.

Yeah, setting the correct server name is important. I'm going to add a sanity check to make sure it can connect to itself via it for the future.

Can put in a history call, yeah. Will do that shortly.

Link to comment
Share on other sites

Local playback would honestly be a pain to implement, especially in a cross-platform compatible way. I have a library called naudio on windows, but it doesn't support mono in any way shape or form due to using dllimports to the windows api. Would be possible to add an auto-launch command, at least... putting a full built in player would be a pain.

Understood. I can always just feed the stream into mplayer to get local output.

Play pause is actually supported in a manner already - if all connected clients are not accepting data (or none are connected), playback is essentially paused. Winamp and mplayer stop accepting data when their buffer is full, so there you are.

This works fine for the current implementation for sure. However (as mentioned above), I'll be streaming via mplayer on the PandoraCaster machine to simulate local audio out. This will always be connected, thus there will always be a client connected. My intent is to control PandoraCaster via a home automation system, so from the wireless remotes it would be ideal to issue a 'play/pause' to the stream, rather than having to hack together a shell interface to kill/execute mplayer.

Can put in a history call, yeah. Will do that shortly.

Fantastic. Thanks!

Link to comment
Share on other sites

I've updated to add /historyjson and a sanity check for server name. (and uploaded)

Hmm, i see your point. Will think about solutions. I suppose i could put in an artificial starve of data, but mplayer will disconnect after about a minute of that.

Edited by ZigZagJoe
Link to comment
Share on other sites

Wow. I've had an old web radio kicking about that can't do much except play shoutcast streams and thanks to this I can now use it to play pandora in my garden.

I don't suppose there's any way that this functionality could be included in the standalone player is there? It kind of makes sense to have a desktop player that has options as to whether it's output is local audio / broadcast via shoutcast / both. It would allow some kind of audio sync between what's playing on your computer and what's on your other players if you wanted to stream to multiple devices at the same time as I generally do.

Having said that it also makes sense to have the http control so it may also mean that the web side of things would need integrating into the player too (In my opinion another good thing but I appreciate it would bloat the app).

It would be great to have a single app that played pandora locally, could stream to other devices in sync and had web control. Failing that just the ability to kick off caster.exe from within the player but have the streams sync'ed would be a great benefit.

Once again thanks for this - made an old man very happy down in Australia!

Link to comment
Share on other sites

The player actually has http control already, but only when run as part of SProxy which provides the HTTP parts.

Eventually it could be possible to have the client output a stream as well, but it would require certain restrictions to be applied to the client; and currently the structure of the two is too different to support it. Might be able to do it if it sent silence between songs, though... the problem lies in that the caster aggressively prefetches a song while the client waits until the current one ends. If the caster ran out of data, it would be bad, hence. Sending silence would fix that, though.

Can't do streams sync'd, the pandora URLs expire after one usage generally, and it'd stick out if they check the logs. So only one program can request a song at a time.

Glad to hear it's useful for someone besides me!

Link to comment
Share on other sites

Day 2. Left it streaming overnight (Something pianobar cannot do) and it's flawless!

A few more things to add to the wish list:

- Album and CoverUrl for NextTrack and History entries.

- A daemon/background mode. :)

Edited by undertow
Link to comment
Share on other sites

Day 2. Left it streaming overnight (Something pianobar cannot do) and it's flawless!

A few more things to add to the wish list:

- Album and CoverUrl for NextTrack and History entries.

- A daemon/background mode. :)

Updated.

Added ComingUpNext, NextArtist, NextAlbum, NextTitle, NextArtUrl to status. Not present if status->canSkip = false

Added non-js station change and station creation

Added LowMem (use disk backing for song data) and NoWebStyle (disable embedded stylesheet)

Added song info for history entries

history json format:

"3:23 PM;Smells Like Teen Spirit (Butch Vig Mix);Nirvana;With The Lights Out;S199619;http://images-sjl-t2-1.pandora.com/images/public/amz/9/4/6/6/602498646649_130W_130H.jpg",

Date;Name;Artist;Album;Id;ArtUrl

semicolons in name/artist/album replaced with %3B

Station entries are just Date;Station: (name) still

Updated web interface further: removed webres.dat in favor of improved local file server. Excerpt from readme:

==== Web interface ====

If AnonWebControl is false, you need to log in prior to using web interface functions like skip, song ratings, station changing, etc.

The web interface's extensibility is enabled by default. To disable, set ServeLocalFiles to false.

The path served is set via LocalFilesPath.

File starting with . will not be served, or .., though relative paths are supported within a url (http://server/dir/../otherdir/file.ext)

Edit LocalFilesPath/.links to add new navigation links. Each new entry should be on one line. Format:

URL;Name[;requires auth true|false]

3rd arg is optional.

If a file's name matches a built in URL, the built in page will be overridden.

Create a file named index.htm (or html) to replace the status page (/).

==== PAGE PARSING ====

If you have a file with extension html, htm, txt, xml, or css, a few variables will be replaced when it is displayed:

--- global server info --
%statusjson%         | json formatted status (same as /status request)
%stationsjson%         | json formatted stations (same as /stations request)
%historyjson%        | json formatted history (same as /historyjson)
%listenport%         | server listen port
%servername%         | server name
%serverurl%        | full server url (http://servername:serverip/)
%style%            | embedded stylesheet 
%authenticated%     | is current requestor authenticated?  (true | false)
%header%        | standard header with nav
%footer%        | standard footer
%navbar%        | nav bar
%stationchangejs%    | collapsable station change list. fallback to /changestations if no js
%stationchgstatic%    | static station change list (no JS)
%uptime%        | Uptime (nice string)

--- now playing info ----
%nowplaying%        | now playing song (artist - title)
%nextsong%        | next song if available otherwise ""
%CanSkip%        | if next song is available  (true | false)
%station%        | current station
%stationID%        | current station ID
%MusicID%        | current song ID
%AlbumDetailUrl%    | pandora albumDetailURL
%SongDetailUrl%        | pandora songDetailURL
%ArtistDetailUrl%    | pandora artistDetailURL
%SongRatedPos%        | current song rated positively  (true | false)
%ArtURL%        | current song AArtUrl
%Artist%        | current song Artist
%Album%            | current song Album
%Title%            | current song Title
%SongLength%        | current song Length (m:ss)
%ComingUpNext%        | if CanSkip ? Parent.ComingUpNext else ""
%NextArtist%        | if CanSkip ? Parent.NextSongInfo.Artist else ""
%NextAlbum%        | if CanSkip ? Parent.NextSongInfo.Album else ""
%NextTitle%        | if CanSkip ? Parent.NextSongInfo.Title else ""
%NextArtUrl%        | if CanSkip ? Parent.NextSongInfo.AArtUrl else ""

Edited by ZigZagJoe
Link to comment
Share on other sites

Update is working great, thanks for the additions!

Since the album and artwork have been added to next artist in /status and /history, could we have it added to /nextsong as well? (or, alternatively, the option to have /status block until "Next*" populates as well? See below.)

From what I can tell thus far, the "Next" information returned in /songchange never populates. I always have to poll /nextsong on /songchange. I'm not suprised, I imagine it takes a second or two for the /nextsong data to populate from the server -- however, if we could optionally have /songchange block until the "next" data is populated as well (or until a timeout is reached) it could effectively eliminate the need for /nextsong altogether.

Just a thought. :)

Thanks again for this, it's been a life saver!

Link to comment
Share on other sites

Update is working great, thanks for the additions!

Since the album and artwork have been added to next artist in /status and /history, could we have it added to /nextsong as well? (or, alternatively, the option to have /status block until "Next*" populates as well? See below.)

From what I can tell thus far, the "Next" information returned in /songchange never populates. I always have to poll /nextsong on /songchange. I'm not suprised, I imagine it takes a second or two for the /nextsong data to populate from the server -- however, if we could optionally have /songchange block until the "next" data is populated as well (or until a timeout is reached) it could effectively eliminate the need for /nextsong altogether.

Just a thought. :)

Thanks again for this, it's been a life saver!

Could add the full song info, yeah.

That happens because it's fetching the first 256k or so next song (so the current song could end/be skipped without having no data avialable) when that returns. Could add an option to block until next song info is available also (though fetching /nextsong additionally after /songchanged would provide faster updates if the connection is slow)

I'm also implementing a language similarish to SSI to allow better replacement of built in pages.

Link to comment
Share on other sites

Updated.

Added the SSI-like language and replacements for all of the built-in pages (built in pages are retained but not used unless serving is disabled)

Added /nextsongex, returns full next song info

Added /autocompleteex, returns a formatted set of links for station creation

Link to comment
Share on other sites

Updated.

Added some more SSI directives and updated the web interface.

Added --fork, which launches in the background on windows. Unknown if it works on linux or not. It *should*, though it is not forking in the usual sense.

Added --kill, which attempts to kill (nicely) the caster listening on the port specified in config. Can be used to kill an instance on another/port IP if it uses the same pandora login. (-n "ip" -p port --kill)

Added writing a playlist containing station listen urls - see details

Station playlist writing is only supported if you have AnonWebControl set to true - otherwise the connecting winamp/etc would be unable to change stations (because it isn't authenticated). Otherwise, you just get the normal listen playlist.

Link to comment
Share on other sites

Updated.

Added function logout to SSI to allow for logging out an ip

Updated function login to allow to log in an ip

As long as an IP has authenticated, it will be served a station change playlist.

Implicit station changes (/listen/SID) are now checked against authenticated IPs if authentication is required

Implicit station changes now change after the current song is done, rather than immediately - there is no way to truely instantly change stations. This is more smooth.

Added "Admin" page

Added StartedTime to status json

Added starttime SSI var and %% replace

Added a number of server stats to SSI vars - use <!--#printenv--> to see them all.

Link to comment
Share on other sites

As usual, updates are running great. Solid as a rock! I've been away for the past week, so I haven't been able to continue or development on my interface until I got back. Thanks again for the updates!

Added --fork, which launches in the background on windows. Unknown if it works on linux or not. It *should*, though it is not forking in the usual sense.

Nope (in Linux).

Unhandled Exception: System.ComponentModel.Win32Exception: ApplicationName='/mnt/hda1/caster/caster/caster.exe', CommandLine='', CurrentDirectory='' 
  at System.Diagnostics.Process.Start_noshell (System.Diagnostics.ProcessStartInfo startInfo, System.Diagnostics.Process process) [0x00000] in &lt;filename unknown&gt;:0 
  at System.Diagnostics.Process.Start_common (System.Diagnostics.ProcessStartInfo startInfo, System.Diagnostics.Process process) [0x00000] in &lt;filename unknown&gt;:0
  at System Diagnostics.Process.Start (System.Diagnostics.ProcessStartInfo startInfo) [0x00000] in &lt;filename unknown&gt;:0
  at Caster.CORE.Main (System.String[] args) [0x00000] in &lt;filename unknown&gt;:0

Added /nextsongex, returns full next song info

Would it be possible to get this in JSON format? Pretty-please? ;)

A couple of things for the wish list

-

Ability to determine account level, (ie. free, premium, etc.) -- This information is provided in the stdout for Caster.exe, but being able to pull it externally would be nice (perhaps via /status ?)

And, of course, local audio out would put this over the top. That's currently my crutch. Sure, streaming through mpg123 is great and all, except controlling mpg123 remotely is a PITA (and not very graceful). I understand it's not a simple task, I just wanted to keep it out there.. ;)

* Edit: After thinking about it, if there's truly no way to get local audio output -- if an ability to 'pause' was added, this would eliminate the need to "control" mpg123 (or mplayer, etc) since it will ALWAYS be streaming.

Thanks again! :D

Edited by undertow
Link to comment
Share on other sites

As usual, updates are running great. Solid as a rock! I've been away for the past week, so I haven't been able to continue or development on my interface until I got back. Thanks again for the updates!

Nope (in Linux).

Would it be possible to get this in JSON format? Pretty-please? ;)

A couple of things for the wish list

-

Ability to determine account level, (ie. free, premium, etc.) -- This information is provided in the stdout for Caster.exe, but being able to pull it externally would be nice (perhaps via /status ?)

And, of course, local audio out would put this over the top. That's currently my crutch. Sure, streaming through mpg123 is great and all, except controlling mpg123 remotely is a PITA (and not very graceful). I understand it's not a simple task, I just wanted to keep it out there.. ;)

* Edit: After thinking about it, if there's truly no way to get local audio output -- if an ability to 'pause' was added, this would eliminate the need to "control" mpg123 (or mplayer, etc) since it will ALWAYS be streaming.

Thanks again! :D

--fork is no longer shown under mono

i've added a way for you to control mplayer remotely:

mkfifo /tmp/mplayerfifio

mplayer -slave -input file=/tmp/mplayerfifio (listen url)

okay, now you have local output. Now, make a page called, say, playpause.html, in the www directory.

In it, write <!--#exec write "/tmp/mplayerfifo|pause\n"-->

Then open (url)/playpause.html

With any luck, mplayer should now pause, and resume if you load it again.

/nextsongex is now json, as you wanted

Added IsPremium to status

more changelog:

added functions for station change mode and static/js station change blocks

should have fixed the ERROR NO DATA AVAILABLE on launch now

nav entries except for listen now and login/logout can be added and removed at will

navbar variable was replaced with makenav function

allowed string arguments for exec

halved backbuffer amount

added ajax station change mode selection

fixed firefox being stupid with ajax requests

added super-ajax flash player popup

nextsongex now uses json

added "write" function to ssi, to write a file's contents. supports fifos!

included files wil now have %% tags parsed

added minicontrols and miniplayer htmls

added ministations

\n,\r,\t,\0 are now parsed in arguments to exec

index.html: updated to use ajax station mode change

changestations.html: updated to use stationchangejs

login.html: added redirect if login is not needed

header.txt: updated to use makenav func

flashp.html: added iframe for navigation while playing and frame busting

.links: added other nav entrie, status, etc.

Edited by ZigZagJoe
Link to comment
Share on other sites

i've added a way for you to control mplayer remotely:[/b]

mkfifo /tmp/mplayerfifio

mplayer -slave -input file=/tmp/mplayerfifio (listen url)

okay, now you have local output. Now, make a page called, say, playpause.html, in the www directory.

In it, write <!--#exec write "/tmp/mplayerfifo|pause\n"-->

Then open (url)/playpause.html

With any luck, mplayer should now pause, and resume if you load it again.

Perfect!

Although I don't use mplayer, I use mpg123 -- the usage for this (for anyone else attempting this) is:

mpg123 -R --fifo /tmp/fifo

/nextsongex is now json, as you wanted

Added IsPremium to status

You're the best! Thanks again.

I'm not sure if this is by design, but a /hatecurrent request does not skip to the next track, it continues to let the negatively rated track play. Is it possible to modify this behavior (much like /station/id) and allow the configuration file to determine the behavior?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...