User:Dz/ShoutCast
From OpenSimulator
(→Building the Board: simple walkthrough added) |
(→Group Land add-on) |
||
(4 intermediate revisions by one user not shown) | |||
Line 45: | Line 45: | ||
=== The BETA script === | === The BETA script === | ||
− | I will attempt to document the changes to the script as I upgrade from BETA release status. This is a copy of a board i have been using in SL for a couple years... it is not bug free but it has been useful and reliable. | + | I will attempt to document the changes to the script as I upgrade from BETA release status. This is a copy of a board i have been using in SL for a couple years... it is not bug free but it has been useful and reliable. When it was first posted it included SL specific addresses for returning profile images by UUID... these will be removed.... |
<source lang = "lsl"> | <source lang = "lsl"> | ||
Line 51: | Line 51: | ||
// Viewer 3 Dynamic HTML Shoutcast stream controller | // Viewer 3 Dynamic HTML Shoutcast stream controller | ||
− | // version 2.5 (c) | + | // version 2.5 (c) D Osborn 4/25/2011 |
− | // version 3 (c) | + | // version 3 (c) D Osborn 2015 |
// Converted existing script to use Floating Text and dynamic HTML display | // Converted existing script to use Floating Text and dynamic HTML display | ||
Line 1,058: | Line 1,058: | ||
// dzShoutCast group land add on BETA | // dzShoutCast group land add on BETA | ||
− | // | + | // D Osborn MOSES grid 9/17/2014 |
− | // Copyright (c) 2014, 2015 | + | // Copyright (c) 2014, 2015 D Osborn |
// All rights reserved. | // All rights reserved. | ||
Line 1,139: | Line 1,139: | ||
<source lang = "lsl"> | <source lang = "lsl"> | ||
+ | # New format Shoutcast - radio controller config notecard | ||
+ | # The format is divided in 4 sections: | ||
+ | # 1 - Access configuration | ||
+ | # 2 - Ban list (keys) | ||
+ | # 3 - Categories (same as genres) | ||
+ | # 4 - Station info (category, name, desc, url) | ||
+ | # The '#' character defines the start of a comment and can be anywhere on a line. | ||
+ | # Everything after the '#' incuding the '#' itself is ignored. | ||
+ | # Case is not important (but information on display will show as entered) | ||
+ | # Spaces before and/or after fields are trimmed. | ||
+ | # Empty lines are ignored. | ||
+ | [ACCESS] | ||
+ | dj=no # yes, true,1 means dj's has access. everything else not. | ||
+ | group=no # ,, ,, ,, means group has acess. ,, ,, ,, | ||
+ | public=yes # ,, ,, ,, means public has access. ,, ,, ,, | ||
+ | #Avatar UUID with access | ||
+ | 12a18155-403d-496f-9dd1-a28a98b98342 | ||
+ | #Avatar UUID with access | ||
+ | 85fbed46-ba6e-4740-acdd-867dae792bf4 | ||
+ | |||
+ | |||
+ | [BANNED] | ||
+ | # Avatar UUID BANNED from access | ||
+ | 014852ac-0260-4ecc-84fd-9f653b8b841c | ||
+ | |||
+ | [GENRE] | ||
+ | # genre name -- Use short genre names as the the length of menu buttons is short! | ||
+ | Blues | ||
+ | Classic Rock | ||
+ | Country | ||
+ | Dance | ||
+ | DJ | ||
+ | Jazz | ||
+ | Live | ||
+ | Reggae | ||
+ | Romance | ||
+ | # etc. | ||
+ | [STATION] | ||
+ | # Shoutcast radio stations | ||
+ | # Use a '|' between each field - don't mind spaces before and after fields, they will be trimmed | ||
+ | # field1:genre, field2:station name, field3: station description, field4: station http://<ip>:<port> or https://<ip>:<port>, field5 : dj UUID | ||
+ | # Note that genre must match case-insensitive a value previously entered in section [GENRE] | ||
+ | # Only DJ lines required a DJ ID.. if one is entered it will enable access to, and display the Profile picture of, the UUID provided. | ||
+ | Classic Rock | Absolute | Absolute Radio | http://205.188.215.226:8018 | ||
+ | Classic Rock | 181fm | 181fm Classic | http://uplink.181.fm:8058 | ||
+ | Blues | KOQX | KOQX | http://69.94.112.216:8000 | ||
+ | Blues | One.FM | ONE.FM Stepchilds club | http://64.62.252.130:8030 | ||
+ | Blues | BU4B | Belly Up 4 Blues | http://64.62.252.134:5100 | ||
+ | Jazz | SwissGrv | Swiss Groove | http://82.197.165.135:80 | ||
+ | Jazz | Sky.fm | Smooth Jazz | http://scfire-dll-aa05.stream.aol.com:80/stream/1010 | ||
+ | Dance | Fox | Fox | http://87.98.169.191:11418 | ||
+ | Dance | Hitz | Hitz | http://scfire-mtc-aa04.stream.aol.com:80/stream/1014 | ||
+ | Romance | SlowDance | Slowdance | http://216.155.137.149:8006 | ||
+ | Reggae | Roots | BigUpRadio Roots | http://173.236.79.22:8005 | ||
+ | Reggae | Ska | BigUpRadio Ska | http://167.114.38.75:8017 | ||
+ | DJ | Autumn | DJ Autumn | http://srv8.karadio.biz:56303 | 6f666533-80ee-4d68-922b-6253ed50b973 | ||
+ | DJ | Dohran | DJ Dohran | http://dozo.primcast.com:8940 | aa61d79c-244b-4167-be70-a9a575b12c5c | ||
+ | DJ | Jada1 | DJ Jada1 | http://85.214.250.196:8212 | 76f7f2e3-01bf-4ccd-b3b0-3083551e2c87 | ||
+ | DJ | P.Parks | DJ Parallel | http://srv3.karadio.biz:18705 | ce735cfe-43fd-45d6-9ba2-8e314c30af4a | ||
+ | DJ | Sesha | Sesha | http://74.222.1.105:8962 | 788fc597-d7bb-4ef8-a27e-7e2ba564c40c | ||
+ | DJ | Troi | Dj Troi | http://srv10.karadio.biz:17959 | 93c39f70-bfd3-4e5f-a4a0-be54bd9ce320 | ||
+ | |||
+ | # etc. | ||
</source > | </source > |
Latest revision as of 08:58, 1 July 2022
Contents |
[edit] dzShoutCast
As with all OpenSimian projects posted here, You are free to copy/modify/and IMPROVE the functionality to fit your needs.
If you have feedback on script errors, please post it to the page discussion User_talk:Dz/ShoutCast
[edit] Feature List
Note card configuration.
MOAP style information display with Texture slide show option for non MOAP viewers.
Configurable Access by owner, group, or individuals (DJs)
Default Texture override.
10 user defined GENRE Categories.
Unlimited URLs per GENRE.
Usable on Group owned land.
[edit] Building the Board
I recommend at least 2 prims to construct the board.
First, a Display prim to hold the script and display the info.
I like to size it 1H and 2W so 256x512 & 512x1024 textures display without distortion. So, rez a box and resize it X: 0.05 meter Y: 2 meter and Z: 1 meter.
Create a note card named "Radio Control config", paste the example text from the section below, and put it in the prim.
Create a new script in the prim. Paste a copy of the script from below over the default script and save it.
When you touch the prim you will either trigger the MOAP display, or a menu to turn the radio on. You might notice that touching the MOAP surface again does NOT generate a menu. That is why we link the second prim, to act as a frame you can touch from the front of the board to get a menu.
I like to texture all of the non-display sides of the Display prim black.
Create a second prim , slightly larger than the first, color and texture as you prefer, and then position it so that it frames the display prim. Select the Display prim LAST , to make sure it is linked as root, and link the 2 prims. Now you should be able to touch the frame to get access to the menus.
[edit] The BETA script
I will attempt to document the changes to the script as I upgrade from BETA release status. This is a copy of a board i have been using in SL for a couple years... it is not bug free but it has been useful and reliable. When it was first posted it included SL specific addresses for returning profile images by UUID... these will be removed....
// Viewer 3 Dynamic HTML Shoutcast stream controller // version 2.5 (c) D Osborn 4/25/2011 // version 3 (c) D Osborn 2015 // Converted existing script to use Floating Text and dynamic HTML display // Corrected multiple menu display feature // Added UUID access list feature // Tested for Opensim release // This script is a modification of the script described below // It is released in accordance to the license granted, and is released under the same license // Script: Shoutcast - radio controller // Version: 0.3 - released 10-2-2011 // Logic Scripts (Flennan Roffo) // (c) 2010 - Flennan Roffo (Logic Scripts) // LICENCE INFO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. /////////////////////////////////////////////////////////////////////////////////////////// float update_time=30.0; /////// EDITABLE \\\\\\ string info_notecard="Radio Control info"; /////// EDITABLE \\\\\\ string config_notecard="Radio Control config"; /////// EDITABLE \\\\\\ string comment_char="#"; /////// EDITABLE \\\\\\ list sep_char_list= ["|"]; /////// EDITABLE \\\\\\ string no_title_info="(no title info available)"; /////// EDITABLE \\\\\\ // Buttons string button_MAIN = "MAIN"; /////// EDITABLE \\\\\\ string button_HELP = "HELP"; /////// EDITABLE \\\\\\ string button_NEXT = ">>"; /////// EDITABLE \\\\\\ string button_PREV = "<<"; /////// EDITABLE \\\\\\ string button_ON = "ON"; /////// EDITABLE \\\\\\ string button_OFF = "OFF"; /////// EDITABLE \\\\\\ // Profile picture particle parameters float PictSize = 1.0; key ppReqID = NULL_KEY; key ProfilePic = NULL_KEY; key lastWho = NULL_KEY; key MyOwner = NULL_KEY; // List of categories (=genres) list category_list=[]; // List of stations. KEEP THESE LISTS IN SYNCH! list station_category=[]; list station_name=[]; list station_desc=[]; list station_url=[]; list dj_UUID = []; list UUID_access = []; string StationId = ""; string GenreType = ""; string ThisTitle = ""; string LastTitle = ""; integer radio_status=0; // 0 - OFF 1 - ON string parcel_url=""; integer lineno=0; key reqid=NULL_KEY; key httpreq_id=NULL_KEY; integer config_error=FALSE; integer flag; integer section=0; integer displayface = 4; integer imagesfound = 0; integer whichimage = 0; // Access values. Note that users who are banned can not access the device even when access is public integer dj_access=TRUE; integer group_access=TRUE; integer public_access=FALSE; list banned_keys=[]; // Channels for menu and user input integer menu_channel; integer listen_handle; // Channel for deeded object integer DeedChannel = -142356; string ParcelName = ""; // Menu integer menu_type=0; // 0 - Main menu (genres) 1 - Station menu (stations) integer menu_num=0; // When more menu options need to be selectable then can be displayed on a menu (12), this is the menu number - menu number 0 is the first menu. // Genres and stations integer category_index=0; // Current index in category_list (genre) integer station_index=0; // Current index in station_* (station) integer num_categories=0; integer num_stations=0; ///// DEFINE the script Functions ///////// // Make request for title info using HTTP request retrieve_titelinfo() { string url=llList2String(station_url,station_index); // httpreq_id=llHTTPRequest(url + "/7.html HTTP/1.0\nUser-Agent: LSL Script (Mozilla Compatible)\n\n",[],""); // llSay(0," send to : " + url + "/7.html"); httpreq_id=llHTTPRequest(url + "/7.html HTTP/1.0\nUser-Agent: LSL Script (Mozilla Compatible)\n\n",[],""); // httpreq_id=llHTTPRequest(url + "/7.html",[],""); } // Make request for profile picture using metatag info getProfilePic(key AvatarKey) { ppReqID = llHTTPRequest( "http://world.secondlife.com/resident/" + (string)AvatarKey,[HTTP_METHOD,"GET"],""); } // Set particle system setProfilePic(key Texture) { if (Texture != NULL_KEY) { vector resizer = llGetScale(); PictSize = resizer.z / 0.75; llParticleSystem([PSYS_PART_FLAGS, PSYS_PART_EMISSIVE_MASK, PSYS_SRC_PATTERN, 4, PSYS_PART_START_ALPHA, 0.5, PSYS_PART_END_ALPHA, 0.5, PSYS_PART_START_COLOR, <1.0, 1.0, 1.0>, PSYS_PART_END_COLOR, <1.0, 1.0, 1.0>, PSYS_PART_START_SCALE, <PictSize, PictSize, 0.0>, PSYS_PART_END_SCALE, <PictSize, PictSize, 0.0>, PSYS_PART_MAX_AGE, 1.2, PSYS_SRC_MAX_AGE, 0.0, PSYS_SRC_ACCEL, <0.0, 0.0, 0.0>, PSYS_SRC_ANGLE_BEGIN, 0.0, PSYS_SRC_ANGLE_END, 0.0, PSYS_SRC_BURST_PART_COUNT, 8, PSYS_SRC_BURST_RADIUS, PictSize, PSYS_SRC_BURST_RATE, 0.1, PSYS_SRC_BURST_SPEED_MIN, 0.0, PSYS_SRC_BURST_SPEED_MAX, 0.0, PSYS_SRC_OMEGA, <0.0, 0.0, 0.0>, PSYS_SRC_TEXTURE, Texture]); } } // Update text displays with new info updateText(string line, string message) { if(line == "1") StationId = message; if (line == "2") GenreType = message; if (line == "3") { ThisTitle = message; // llSay(0," height " + (string) llGetPrimMediaParams(displayface,[PRIM_MEDIA_HEIGHT_PIXELS] )); // llSay(0," width " + (string) llGetPrimMediaParams(displayface,[PRIM_MEDIA_WIDTH_PIXELS] )); llSetText( StationId + " \n(" + GenreType + ") \nNow Playing :" + ThisTitle + "\n \nLast Song: " + LastTitle, <0.0, 1.0, 0.0>, 1.0); string WebText = "<body bgcolor='black'><div style='text-align: center;'><font size='7'><font color ='blue'><i><h1>" + StationId +"</i> ( " + GenreType + " )</h1><p><i><h3>On NOW</h3></i><p><h1>"+ ThisTitle + "<p><p></h1><h3><i>Last</i></h3><p><h1>" + LastTitle + "</h1></div></body>"; llSetPrimMediaParams(displayface,[PRIM_MEDIA_HEIGHT_PIXELS, 1024, PRIM_MEDIA_WIDTH_PIXELS, 2048, PRIM_MEDIA_CURRENT_URL, "data:text/html," + WebText]); LastTitle = ThisTitle; } } // Display a line display_line(string line, string message) { // llSay(0,message); updateText(line, message); } // Set the text display to default clear_display() { // Clears the display display_line("1","Radio Station ID"); display_line("2","Music Genre...."); display_line("3","Now Playing...."); } // Make a menu / dialog make_menu(key id) { menu_channel=random_channel(); if (radio_status == 0) { menu_type=0; menu_num=0; llDialog(id,"Menu: Status\n\nRadio is OFF", [ "ON", "HELP" ],menu_channel); } else { if (menu_type ==0) { llDialog(id,"Menu: Genres", category_menu(menu_num),menu_channel); } else { llDialog(id,"Menu: Stations\nGenre: " + llList2String(category_list,category_index), station_menu(menu_num),menu_channel); } } if (listen_handle != 0) llListenRemove(listen_handle); listen_handle=llListen(menu_channel,"",id,""); } // Make the menu option list for menu: catagories (genres) list category_menu(integer num) { integer len=llGetListLength(category_list); list menu=[]; if (len > 9) // If more then 9 items (12 minus the 3 buttons for MAIN/HELP and PREV, NEXT) { integer last_sub=(len-1)/9; // submenus start at 0. 9th entry is in submenu 0, 10th in 1, etc. if (num > last_sub) { llWhisper(0,"error: wrong submenu number: " + (string) num + "."); return [ "MAIN" ]; } else { integer first=9*num; while (--len >= first) menu+=(list)llList2String(category_list,len); if (num == 0) menu+=(list)button_HELP; else menu+=(list)button_MAIN; if (num == 0) menu+=(list)button_OFF; else menu+=(list)button_PREV; if (num != last_sub) menu+=(list)button_NEXT; } } else { while (--len >= 0) menu+=(list)llList2String(category_list,len); menu+=(list)button_OFF; menu+=(list)button_HELP; } return menu; // order_buttons(menu); } // Returns the number of stations in a certain category integer stations_in_category(integer cat) { integer count=0; integer i; integer len=llGetListLength(station_category); string category=llList2String(category_list,cat); for (i=0; i < len; i++) if (category == llList2String(category_list,i)) count++; return count; } // Returns a list of station names in a certain category (genre) list station_list(integer category) { list s=[]; integer i; integer catcnt = llGetListLength(station_name); string cname=llList2String(category_list,category_index); for (i = 0; i < catcnt; i++) if (llList2String(station_category,i) == cname) s+=(list)llList2String(station_name,i); return s; } // Returns the list of stations for the station menu, depending on the submenu number list station_menu(integer num) { list stations=station_list(category_index); integer len=llGetListLength(stations); list menu=[]; if (len > 11) // 12 - 1 for MAIN menu { integer last_sub=(len-1)/9; if (num > last_sub) { llWhisper(0,"error: wrong submenu number: " + (string) num + "."); return [ "MAIN" ]; } else { integer first=9*num; integer last=9*num+8; // llSay(0,"delivering menu for stations" + (string) first + " to " + (string) last); menu+=(list)button_MAIN; if (num > 0) menu+=(list)button_PREV; if (num < last_sub) menu+=(list)button_NEXT; if (len > last) len =last; while (--len >= first) menu+=(list)llList2String(stations,len); } } else { menu+=(list)button_MAIN; while (--len >= 0) menu+=(list)llList2String(stations,len); } return menu; // order_buttons(menu); } // Returns whether av with key id has access integer has_access(key id) { if (id == MyOwner) return TRUE; if (llListFindList(banned_keys,(list)id) != -1) return FALSE; if (llListFindList(UUID_access,(list)id) != -1) return TRUE; if (dj_access && llListFindList(dj_UUID,(list)id) != -1) return TRUE; if (group_access && llSameGroup(id)) return TRUE; if (public_access) return TRUE; return FALSE; } // Gets a random channel -- uses a wide range of big negative channel numbers seldomly used integer random_channel() { integer min=-2147483647; integer max=-1000; return (integer) (min + llFrand(max-min)); } // Returns a true value depending on the first character in input - anything else is assumed false. integer true_value(string input) { string value=llToLower(llGetSubString(input,0,0)); if (value == "y" || value == "t" || value =="1") return TRUE; return FALSE; } // Return if more input should be processed (if not at EOF) - sets ConfigError if any config error found. Reading config card stops at the first error. integer process_line(string dataline) { string line=llStringTrim(dataline,STRING_TRIM); integer index=llSubStringIndex(line,comment_char); if (index==0) // line starts with comment - ignore line return TRUE; if (index!=-1) line=llStringTrim(llGetSubString(line,0,index-1),STRING_TRIM_TAIL); // skip everything after comment_char and trim tail if (line=="") // Ignore blank lines return TRUE; if (llToLower(line) == "[access]") { section = 1; return TRUE; } else if (llToLower(line) == "[banned]") { section = 2; return TRUE; } else if (llToLower(line) == "[genre]") { section = 3; return TRUE; } else if (llToLower(line) == "[station]") { section = 4; return TRUE; } else if (llGetSubString(line,0,0) == "[" && llGetSubString(line,-1,-1) == "]") { llWhisper(0,"error: malformed section found at line " + (string)lineno + ".\n" + dataline); config_error=TRUE; return FALSE; } if (section == 0) { llWhisper(0,"error: no section found on line: " + (string) lineno); config_error = TRUE; return FALSE; } list breakup=llParseString2List(line,["="],[]); string field=llStringTrim(llList2String(breakup,0),STRING_TRIM); string values=llStringTrim(llList2String(breakup,1),STRING_TRIM); if (section == 1) // access { field=llToLower(field); if (field=="dj") { dj_access=true_value(values); return TRUE; } else if (field=="group") { group_access=true_value(values); return TRUE; } else if (field=="public") { public_access=true_value(values); return TRUE; } else if (llStringLength(field) == 36 ) { UUID_access += [(key)field]; return TRUE; } else { llWhisper(0,"error: invalid option on line: " + (string)lineno + ".\n" + dataline); config_error=TRUE; return FALSE; } } else if (section == 2) // ban list { key try=(key) field; if (try) { banned_keys+=(list)((key) field); return TRUE; } else return FALSE; } else if (section == 3) // categories { if (llListFindList(category_list,(list)field) == -1) { category_list+=(list)field; } else llWhisper(0,"genre: '" + field + "' already entered; double entry skipped."); return TRUE; } else if (section == 4) // stations { list parse=llParseString2List(line,sep_char_list, []); string category=llStringTrim(llList2String(parse,0),STRING_TRIM); string name=llStringTrim(llList2String(parse,1),STRING_TRIM); string desc=llStringTrim(llList2String(parse,2),STRING_TRIM); string url=llStringTrim(llToLower(llList2String(parse,3)),STRING_TRIM); string AviUUID=llStringTrim(llToLower(llList2String(parse,4)),STRING_TRIM); if (!available_category(category)) { llWhisper(0,"error: unknown genre on line: " + (string)lineno + ".\n" + dataline); config_error=TRUE; return FALSE; } if (llListFindList(station_url,(list)url) == -1 || llListFindList(station_category,(list)category) == -1 || llListFindList(station_name,(list)name) == -1) { num_stations++; station_category+=(list)category; station_name+=(list)name; station_desc+=(list)desc; station_url+=(list)url; dj_UUID += [(key)AviUUID]; return TRUE; } else { llWhisper(0,"This station is already entered under the same genre and same url and is skipped.\nStation: " + name + "\nGenre: '" + category + "'\nURL: " + url); return TRUE; } } return FALSE; } // Sets the parcel URL and updates the display set_parcel_url(string url) { parcel_url=url; llSetParcelMusicURL(parcel_url); llRegionSay(DeedChannel,ParcelName + "|" + parcel_url); llSay(0," changinf url for " + ParcelName); if (parcel_url=="") { clear_display(); display_line("1","Radio is OFF"); display_line("2",""); display_line("3",""); } else { llWhisper(0,"station now set to " + llList2String(station_desc,station_index) + "."); display_line("1","Station: " + llList2String(station_desc,station_index)); display_line("2", llList2String(category_list,category_index)); display_line("3","Now playing....."); llSetTimerEvent(update_time); } } // Returns if a category (genre) exists. integer available_category(string category) { integer i; integer len=llGetListLength(category_list); for (i=0;i<len;i++) if (llToLower(category) == llToLower(llList2String(category_list,i))) return TRUE; return FALSE; } // Returns if a category (genre) is empty (i.e. there are no stations for this catagory (genre)) integer empty_category(string category) { integer i; integer len=llGetListLength(station_category); for (i=0; i < len; i++) if (llToLower(category) == llToLower(llList2String(station_category,i))) return FALSE; return TRUE; } // Removes categories (genres) for which no station is known. skip_empty_categories() { integer i=0; while (i<llGetListLength(category_list)) { if (empty_category(llList2String(category_list,i))) { llWhisper(0,"Warning: Genre '" + llList2String(category_list,i) + "' contains no stations and is deleted."); category_list=llDeleteSubList(category_list,i,i); } else i++; } num_categories=llGetListLength(category_list); } ///////////////////////////////////////////// // state default //////////////////////////////////////////// default { state_entry() { flag=FALSE; lineno=0; config_error=FALSE; num_stations=0; num_categories=0; radio_status=0; menu_num=0; menu_type=0; MyOwner = llGetOwner(); list details = llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_NAME]); ParcelName = llList2String(details ,0); imagesfound = llGetInventoryNumber(INVENTORY_TEXTURE); llParticleSystem([]); if (imagesfound < 1) { llSetPrimitiveParams([PRIM_FULLBRIGHT,displayface,TRUE, PRIM_TEXTURE, displayface, "adbff0cd-09c0-54b1-aeaa-ffd6eabfcab09",<1.0,1.0,0.0>, <0.0,0.0,0.0>, 0.0]); } else { llSetPrimitiveParams([PRIM_FULLBRIGHT,displayface,TRUE, PRIM_TEXTURE, displayface, llGetInventoryName(INVENTORY_TEXTURE,0),<1.0,1.0,0.0>, <0.0,0.0,0.0>, 0.0]); } llSetPrimMediaParams(displayface,[PRIM_MEDIA_HEIGHT_PIXELS, 1024, PRIM_MEDIA_WIDTH_PIXELS, 2048, PRIM_MEDIA_PERMS_CONTROL , PRIM_MEDIA_PERM_NONE]); if (llGetInventoryType(config_notecard) == INVENTORY_NOTECARD) { reqid=llGetNotecardLine(config_notecard,lineno++); llWhisper(0, "Reading config notecard..."); display_line("1","Reading configuration."); display_line("2","Wait...."); display_line("3",""); } else { llWhisper(0,"No config notecard '" + config_notecard + "' present."); state offline; } } on_rez(integer param) { llResetScript(); } dataserver(key id, string data) { if (reqid==id) { if (data==EOF) { skip_empty_categories(); llWhisper(0,"Configuration ok.\n" + (string)num_categories + " genres and " + (string)num_stations + " stations."); display_line("1","Configuration OK"); display_line("2","Genres : " + (string)num_categories); display_line("3","Stations: " + (string)num_stations); state menu; } else { if (process_line(data)) reqid=llGetNotecardLine(config_notecard,lineno++); else if (config_error) { llWhisper(0,"errors found in configuration. please correct them."); state offline; } } } } changed(integer ch) { if (ch & CHANGED_INVENTORY) llResetScript(); } } ///////////////////////////////////////////// // state offline //////////////////////////////////////////// state offline { state_entry() { llWhisper(0,"Reset on owner touch or when notecard updated."); } touch_start(integer t) { if (llDetectedKey(0)==llGetOwner()) llResetScript(); } changed(integer ch) { if (ch & CHANGED_INVENTORY) llResetScript(); } } ///////////////////////////////////////////// // state menu //////////////////////////////////////////// state menu { state_entry() { menu_type=0; menu_num=0; listen_handle=0; } on_rez(integer param) { llResetScript(); } touch_start(integer total_num) { key toucher=llDetectedKey(0); if (has_access(toucher)) { make_menu(toucher); } else llWhisper(0,"sorry, no access."); } listen(integer chan, string name,key id,string msg) { integer index; if (menu_type == 0) // main menu { if (msg == button_MAIN) { menu_type=0; menu_num =0; make_menu(id); } else if (msg == button_NEXT) { menu_num++; make_menu(id); } else if (msg == button_PREV) { menu_num--; make_menu(id); } else if (msg == button_ON) { radio_status=1; display_line("1","Radio is ON"); menu_num=0; llWhisper(0,"Radio now turned on."); make_menu(id); } else if (msg == button_OFF) { radio_status=0; set_parcel_url(""); llSetTimerEvent(0.0); llWhisper(0,"Radio now turned off."); } else if (msg == button_HELP) { if (llGetInventoryType(info_notecard) == INVENTORY_NOTECARD) { llGiveInventory(id,info_notecard); } else llWhisper(0,"sorry, help not available."); } else if (radio_status == 1) { index = llListFindList(category_list, (list)msg); if (index == -1) llWhisper(0,"error: genre not found: " + msg); else { category_index=index; llWhisper(0,"Genre now set to " + llList2String(category_list,category_index) + "."); menu_type=1; menu_num=0; make_menu(id); } } } else if (menu_type == 1 && radio_status == 1) // station menu { if (msg == button_MAIN) { menu_type=0; menu_num =0; make_menu(id); } else if (msg == button_NEXT) { menu_num++; make_menu(id); } else if (msg == button_PREV) { menu_num--; make_menu(id); } else { index = llListFindList(station_name, (list)msg); if (index == -1) llWhisper(0,"error: station not found: " + msg); else { station_index=index; string new_url=llList2String(station_url,station_index); string new_avi=llList2String(dj_UUID,station_index); llSay(0, llList2String(category_list,category_index) + " - found profile ID " + new_avi); if (new_url != parcel_url) { if (llList2String(category_list,category_index) == "DJ") { // getProfilePic(new_avi); } else { llParticleSystem([]); } set_parcel_url(new_url); } } } } } timer() { retrieve_titelinfo(); if (imagesfound > 0 ) { llSetPrimitiveParams([PRIM_COLOR,displayface,<1.0,1.0,1.0>, 0.0]); llSetPrimitiveParams([PRIM_FULLBRIGHT,displayface,TRUE, PRIM_TEXTURE, displayface, llGetInventoryName(INVENTORY_TEXTURE,whichimage),<1.0,1.0,0.0>, <0.0,0.0,0.0>, 0.0]); llSleep(1.5); llSetPrimitiveParams([PRIM_COLOR,displayface,<1.0,1.0,1.0>, 1.0]); whichimage +=1; if (whichimage == imagesfound) whichimage = 0; } llSetTimerEvent(update_time); } http_response(key id, integer status, list meta, string body) { if (id == httpreq_id) { if (status == 200) { // llSay(0," http response : " + body); string feed = llGetSubString(body,llSubStringIndex(body, "<body>") + llStringLength("<body>"), llSubStringIndex(body,"</body>") - 1); list feed_list = llParseString2List(feed,[","],[]); string current_title_info= llList2String(feed_list,6); integer length = llGetListLength(feed_list); if(llList2String(feed_list,7)) { integer a = 7; for(; a<length; ++a) { current_title_info += ", " + llList2String(feed_list,a); } } if (current_title_info != LastTitle) { display_line("3", current_title_info); } } else { display_line("3", no_title_info); } } if (id == ppReqID) { llSay(0,"profile pic request returned"); integer s1 = llSubStringIndex(body,"<meta name=\"imageid\" content=\""); integer s1l = llStringLength("<meta name=\"imageid\" content=\""); integer s2 = llSubStringIndex(body,"\" /> \n <meta name=\"agentid\"") - 1; if(s1 == -1) { ProfilePic = NULL_KEY; } else { ProfilePic = (key)llGetSubString(body,s1+s1l,s2); if ( ProfilePic != "00000000-0000-0000-0000-000000000000") { if (lastWho == ProfilePic) { setProfilePic("a2769d9c-5733-dec8-ef86-a1bf27334de9"); lastWho = NULL_KEY; } else { setProfilePic(ProfilePic); lastWho = ProfilePic; } } } } } changed(integer ch) { if (ch & CHANGED_INVENTORY) llResetScript(); } } ////////////////////////////// // end of script //////////////////////////////
[edit] Group Land add-on
When you try and change the URL on group owned land, you need to use an object deeded to the group.. If you deed the ShoutCast board, you will probably lose the ability to edit the config note card...
Place this script in a small prim somewhere on the parcel and then deed the prim to the proper group. It will relay the original ShoutCast board message....
// dzShoutCast group land add on BETA // D Osborn MOSES grid 9/17/2014 // Copyright (c) 2014, 2015 D Osborn // All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This scipt is designed to listen for a shoutcast board on the current parcel // to send a "Change URL" message. It is useful on group owned land. // Instead of deeding the shoutcast board to the group,.. Place this script in a prim, // leave the prim somewhere on the parcel, and then deed the prim to the group. integer listen_handle = 0; // Initialize handler to register listener string ParcelName = ""; integer listen_channel = -142356; default { state_entry() // Set the parcel Name and start listening for messages on the designated channel { ParcelName = llList2String(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_NAME]),0); listen_handle = llListen( listen_channel, "", "", ""); } listen( integer channel, string name, key id, string message ) // Wait for incoming messages... { if(llGetSubString(name, 0, 6) == "dzShout") // Check to see they are from an object name we expect { list msg_list = llParseString2List(message,["|"],[""]); // parse the listen message string msgParcel = llList2String(msg_list ,0); // Looking for the parcel name it was sent from string msgHttp = llList2String(msg_list ,1); // and double checking it sent us an URL if (llGetSubString(msgHttp, 0, 3) == "http" && msgParcel == ParcelName) { llSetParcelMusicURL(msgHttp); // if everyhting looks good, set the new URL // llSay(0,"Setting url to : " + msgHttp); // and squawk about it Or not } } } on_rez(integer param) //Triggered when the object is rezed { llResetScript(); // By resetting the script on rez it forces the listen to re-register. } changed(integer mask) { if(mask & CHANGED_OWNER) // Triggered when the object containing this script changes owner. { llResetScript(); // should reset when deeded to group in place. } } }
[edit] Configuration Notecard
This is how URLs are added to the menus and grouped into Genres.
# New format Shoutcast - radio controller config notecard # The format is divided in 4 sections: # 1 - Access configuration # 2 - Ban list (keys) # 3 - Categories (same as genres) # 4 - Station info (category, name, desc, url) # The '#' character defines the start of a comment and can be anywhere on a line. # Everything after the '#' incuding the '#' itself is ignored. # Case is not important (but information on display will show as entered) # Spaces before and/or after fields are trimmed. # Empty lines are ignored. [ACCESS] dj=no # yes, true,1 means dj's has access. everything else not. group=no # ,, ,, ,, means group has acess. ,, ,, ,, public=yes # ,, ,, ,, means public has access. ,, ,, ,, #Avatar UUID with access 12a18155-403d-496f-9dd1-a28a98b98342 #Avatar UUID with access 85fbed46-ba6e-4740-acdd-867dae792bf4 [BANNED] # Avatar UUID BANNED from access 014852ac-0260-4ecc-84fd-9f653b8b841c [GENRE] # genre name -- Use short genre names as the the length of menu buttons is short! Blues Classic Rock Country Dance DJ Jazz Live Reggae Romance # etc. [STATION] # Shoutcast radio stations # Use a '|' between each field - don't mind spaces before and after fields, they will be trimmed # field1:genre, field2:station name, field3: station description, field4: station http://<ip>:<port> or https://<ip>:<port>, field5 : dj UUID # Note that genre must match case-insensitive a value previously entered in section [GENRE] # Only DJ lines required a DJ ID.. if one is entered it will enable access to, and display the Profile picture of, the UUID provided. Classic Rock | Absolute | Absolute Radio | http://205.188.215.226:8018 Classic Rock | 181fm | 181fm Classic | http://uplink.181.fm:8058 Blues | KOQX | KOQX | http://69.94.112.216:8000 Blues | One.FM | ONE.FM Stepchilds club | http://64.62.252.130:8030 Blues | BU4B | Belly Up 4 Blues | http://64.62.252.134:5100 Jazz | SwissGrv | Swiss Groove | http://82.197.165.135:80 Jazz | Sky.fm | Smooth Jazz | http://scfire-dll-aa05.stream.aol.com:80/stream/1010 Dance | Fox | Fox | http://87.98.169.191:11418 Dance | Hitz | Hitz | http://scfire-mtc-aa04.stream.aol.com:80/stream/1014 Romance | SlowDance | Slowdance | http://216.155.137.149:8006 Reggae | Roots | BigUpRadio Roots | http://173.236.79.22:8005 Reggae | Ska | BigUpRadio Ska | http://167.114.38.75:8017 DJ | Autumn | DJ Autumn | http://srv8.karadio.biz:56303 | 6f666533-80ee-4d68-922b-6253ed50b973 DJ | Dohran | DJ Dohran | http://dozo.primcast.com:8940 | aa61d79c-244b-4167-be70-a9a575b12c5c DJ | Jada1 | DJ Jada1 | http://85.214.250.196:8212 | 76f7f2e3-01bf-4ccd-b3b0-3083551e2c87 DJ | P.Parks | DJ Parallel | http://srv3.karadio.biz:18705 | ce735cfe-43fd-45d6-9ba2-8e314c30af4a DJ | Sesha | Sesha | http://74.222.1.105:8962 | 788fc597-d7bb-4ef8-a27e-7e2ba564c40c DJ | Troi | Dj Troi | http://srv10.karadio.biz:17959 | 93c39f70-bfd3-4e5f-a4a0-be54bd9ce320 # etc.