diff --git a/airtime_mvc/application/configs/constants.php b/airtime_mvc/application/configs/constants.php index b82bb65cf..e9e9a36fa 100644 --- a/airtime_mvc/application/configs/constants.php +++ b/airtime_mvc/application/configs/constants.php @@ -12,10 +12,7 @@ define('COMPANY_SITE' , 'libretime.org'); define('COMPANY_SITE_URL' , 'http://libretime.org'); define('SUPPORT_ADDRESS' , 'https://discourse.libretime.org/'); -define("AIRTIMEPRO_API_URL", "https://account.example.com:5001/api/"); - define('HELP_URL' , 'https://discourse.libretime.org/'); -define('FAQ_URL' , 'http://libretime.org/faq'); define('WHOS_USING_URL' , 'https://github.com/orgs/LibreTime/people'); define('TERMS_AND_CONDITIONS_URL' , 'https://github.com/LibreTime/libretime/blob/master/README.md'); define('PRIVACY_POLICY_URL' , 'https://github.com/LibreTime/code-of-conduct/blob/master/CODE_OF_CONDUCT.md'); diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index c6acf0c61..866a5b7b2 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -163,11 +163,6 @@ $pages[] = array( 'action' => 'help', 'resource' => 'dashboard' ), - array( - 'label' => _('FAQ'), - 'uri' => FAQ_URL, - 'target' => "_blank" - ), array( 'label' => _('User Manual'), 'uri' => USER_MANUAL_URL, diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index 54482da8c..fa6bdb2ab 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -90,7 +90,7 @@ class ScheduleController extends Zend_Controller_Action AirtimeTableView::injectTableJavaScriptDependencies($headScript, $baseUrl, $CC_CONFIG['airtime_version']); $this->view->headScript()->appendFile($baseUrl.'js/libs/moment.min.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'js/libs/moment-timezone-with-data-2010-2020.min.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'js/libs/moment-timezone.min.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/utilities/utilities.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index faf5e4f5f..4454560f0 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -284,8 +284,8 @@ class Application_Model_StreamSetting 'output' => 'icecast', 'user' => $config['stationId'], 'pass' => Application_Model_Preference::getDefaultIcecastPassword(), - // Kind of ugly... convert prefix int to ascii char - 'mount' => $config['stationId'] . '_' . chr($prefix[1] + 96), + // Manually setting default mountpoint + 'mount' => 'airtime_128', ); } diff --git a/airtime_mvc/application/views/scripts/embed/player.phtml b/airtime_mvc/application/views/scripts/embed/player.phtml index 6b4b2cfb4..75e7cd4de 100644 --- a/airtime_mvc/application/views/scripts/embed/player.phtml +++ b/airtime_mvc/application/views/scripts/embed/player.phtml @@ -21,9 +21,9 @@ this.settings = { 'elementId': id_element, // leave alone 'autoplay': false, // or true (only works on some browsers) - 'forceHTTPS': true, // or true if the stream is in SSL (beware of the listening port, usually 8000) - 'replacePort':false, // false for disabled or '8000' as the usual start port, forces to specify replacePortTo. - 'replacePortTo':'' // either '' to use the default port of the browser (80/http, 443/https) or '8443' to force the port of the stream. + 'forceHTTPS': false, // or true if the stream is in SSL (beware of the listening port, usually 8000) + 'replacePort': false, // false for disabled or '8000' as the usual start port, forces to specify replacePortTo. + 'replacePortTo': '' // either '' to use the default port of the browser (80/http, 443/https) or '8443' to force the port of the stream. }; if (this.playerMode == "manual") { @@ -109,7 +109,7 @@ Html5Player.prototype.play = function() { console.log('play'); - playerhtml5_audio.src = this.settings.url+'?'+Math.floor(Math.random() * Math.floor(100000)); + playerhtml5_audio.src = this.settings.url; playerhtml5_audio.play(); togglePlayStopButton(); }; @@ -267,4 +267,4 @@ - \ No newline at end of file + diff --git a/airtime_mvc/public/js/libs/moment-timezone-with-data-2010-2020.min.js b/airtime_mvc/public/js/libs/moment-timezone-with-data-2010-2020.min.js deleted file mode 100644 index 9f6b9c27f..000000000 --- a/airtime_mvc/public/js/libs/moment-timezone-with-data-2010-2020.min.js +++ /dev/null @@ -1,6 +0,0 @@ -//! moment-timezone.js -//! version : 0.4.0 -//! author : Tim Wood -//! license : MIT -//! github.com/moment/moment-timezone -!function(a,b){"use strict";"function"==typeof define&&define.amd?define(["moment"],b):"object"==typeof exports?module.exports=b(require("moment")):b(a.moment)}(this,function(a){"use strict";function b(a){return a>96?a-87:a>64?a-29:a-48}function c(a){var c,d=0,e=a.split("."),f=e[0],g=e[1]||"",h=1,i=0,j=1;for(45===a.charCodeAt(0)&&(d=1,j=-1),d;dc;c++)a[c]=Math.round((a[c-1]||0)+6e4*a[c]);a[b-1]=1/0}function f(a,b){var c,d=[];for(c=0;cz||2===z&&6>A)&&q("Moment Timezone requires Moment.js >= 2.6.0. You are using Moment.js "+a.version+". See momentjs.com"),h.prototype={_set:function(a){this.name=a.name,this.abbrs=a.abbrs,this.untils=a.untils,this.offsets=a.offsets},_index:function(a){var b,c=+a,d=this.untils;for(b=0;be;e++)if(b=g[e],c=g[e+1],d=g[e?e-1:e],c>b&&r.moveAmbiguousForward?b=c:b>d&&r.moveInvalidForward&&(b=d),fz||2===z&&9>A)&&q("Moment Timezone setDefault() requires Moment.js >= 2.9.0. You are using Moment.js "+a.version+"."),a.defaultZone=b?k(b):null,a};var C=a.momentProperties;return"[object Array]"===Object.prototype.toString.call(C)?(C.push("_z"),C.push("_a")):C&&(C._z=null),n({version:"2015d",zones:["Africa/Abidjan|GMT|0|0|","Africa/Addis_Ababa|EAT|-30|0|","Africa/Algiers|CET|-10|0|","Africa/Bangui|WAT|-10|0|","Africa/Blantyre|CAT|-20|0|","Africa/Cairo|EET EEST|-20 -30|010101010|1Cby0 Fb0 c10 8n0 8Nd0 gL0 e10 mn0","Africa/Casablanca|WET WEST|0 -10|01010101010101010101010101010101010101010|1Cco0 Db0 1zd0 Lz0 1Nf0 wM0 co0 go0 1o00 s00 dA0 vc0 11A0 A00 e00 y00 11A0 uo0 e00 DA0 11A0 rA0 e00 Jc0 WM0 m00 gM0 M00 WM0 jc0 e00 RA0 11A0 dA0 e00 Uo0 11A0 800 gM0 Xc0","Africa/Ceuta|CET CEST|-10 -20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Africa/Johannesburg|SAST|-20|0|","Africa/Tripoli|EET CET CEST|-20 -10 -20|0120|1IlA0 TA0 1o00","Africa/Windhoek|WAST WAT|-20 -10|01010101010101010101010|1C1c0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 11B0","America/Adak|HST HDT|a0 90|01010101010101010101010|1BR00 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Anchorage|AKST AKDT|90 80|01010101010101010101010|1BQX0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Anguilla|AST|40|0|","America/Araguaina|BRT BRST|30 20|010|1IdD0 Lz0","America/Argentina/Buenos_Aires|ART|30|0|","America/Asuncion|PYST PYT|30 40|01010101010101010101010|1C430 1a10 1fz0 1a10 1fz0 1cN0 17b0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0","America/Atikokan|EST|50|0|","America/Bahia|BRT BRST|30 20|010|1FJf0 Rb0","America/Bahia_Banderas|MST CDT CST|70 50 60|01212121212121212121212|1C1l0 1nW0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Belem|BRT|30|0|","America/Belize|CST|60|0|","America/Boa_Vista|AMT|40|0|","America/Bogota|COT|50|0|","America/Boise|MST MDT|70 60|01010101010101010101010|1BQV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Campo_Grande|AMST AMT|30 40|01010101010101010101010|1BIr0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10","America/Cancun|CST CDT EST|60 50 50|010101010102|1C1k0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 Dd0","America/Caracas|VET|4u|0|","America/Cayenne|GFT|30|0|","America/Chicago|CST CDT|60 50|01010101010101010101010|1BQU0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Chihuahua|MST MDT|70 60|01010101010101010101010|1C1l0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Creston|MST|70|0|","America/Dawson|PST PDT|80 70|01010101010101010101010|1BQW0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Detroit|EST EDT|50 40|01010101010101010101010|1BQT0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Eirunepe|AMT ACT|40 50|01|1KLE0","America/Glace_Bay|AST ADT|40 30|01010101010101010101010|1BQS0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Godthab|WGT WGST|30 20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","America/Goose_Bay|AST ADT|40 30|01010101010101010101010|1BQQ1 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Grand_Turk|EST EDT AST|50 40 40|0101010101012|1BQT0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Guayaquil|ECT|50|0|","America/Guyana|GYT|40|0|","America/Havana|CST CDT|50 40|01010101010101010101010|1BQR0 1wo0 U00 1zc0 U00 1qM0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0","America/La_Paz|BOT|40|0|","America/Lima|PET|50|0|","America/Merida|CST CDT|60 50|01010101010101010101010|1C1k0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Metlakatla|PST|80|0|","America/Miquelon|PMST PMDT|30 20|01010101010101010101010|1BQR0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Montevideo|UYST UYT|20 30|01010101010101010101010|1BQQ0 1ld0 14n0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10 11z0 1o10 14n0 1ld0 14n0 1ld0 14n0 1o10 11z0 1o10 11z0 1o10","America/Noronha|FNT|20|0|","America/North_Dakota/Beulah|MST MDT CST CDT|70 60 60 50|01232323232323232323232|1BQV0 1zb0 Oo0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Paramaribo|SRT|30|0|","America/Port-au-Prince|EST EDT|50 40|0101010101010101010|1GI70 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","America/Santa_Isabel|PST PDT|80 70|01010101010101010101010|1C1m0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0","America/Santiago|CLST CLT CLT|30 40 30|010101010102|1C1f0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","America/Sao_Paulo|BRST BRT|20 30|01010101010101010101010|1BIq0 1zd0 On0 1zd0 Rb0 1zd0 Lz0 1C10 Lz0 1C10 On0 1zd0 On0 1zd0 On0 1zd0 On0 1C10 Lz0 1C10 Lz0 1C10","America/Scoresbysund|EGT EGST|10 0|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","America/St_Johns|NST NDT|3u 2u|01010101010101010101010|1BQPv 1zb0 Op0 1zcX Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0","Antarctica/Casey|CAST AWST|-b0 -80|0101|1BN30 40P0 KL0","Antarctica/Davis|DAVT DAVT|-50 -70|0101|1BPw0 3Wn0 KN0","Antarctica/DumontDUrville|DDUT|-a0|0|","Antarctica/Macquarie|AEDT MIST|-b0 -b0|01|1C140","Antarctica/Mawson|MAWT|-50|0|","Antarctica/McMurdo|NZDT NZST|-d0 -c0|01010101010101010101010|1C120 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","Antarctica/Rothera|ROTT|30|0|","Antarctica/Syowa|SYOT|-30|0|","Antarctica/Troll|UTC CEST|0 -20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Antarctica/Vostok|VOST|-60|0|","Asia/Aden|AST|-30|0|","Asia/Almaty|ALMT|-60|0|","Asia/Amman|EET EEST|-20 -30|010101010101010101010|1BVy0 1qM0 11A0 1o00 11A0 4bX0 Dd0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0","Asia/Anadyr|ANAT ANAST ANAT|-c0 -c0 -b0|0120|1BWe0 1qN0 WM0","Asia/Aqtau|AQTT|-50|0|","Asia/Ashgabat|TMT|-50|0|","Asia/Baku|AZT AZST|-40 -50|01010101010101010101010|1BWo0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Bangkok|ICT|-70|0|","Asia/Beirut|EET EEST|-20 -30|01010101010101010101010|1BWm0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0","Asia/Bishkek|KGT|-60|0|","Asia/Brunei|BNT|-80|0|","Asia/Calcutta|IST|-5u|0|","Asia/Chita|YAKT YAKST YAKT IRKT|-90 -a0 -a0 -80|01023|1BWh0 1qM0 WM0 8Hz0","Asia/Choibalsan|CHOT CHOST|-80 -90|0101010101010|1O8G0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Chongqing|CST|-80|0|","Asia/Dacca|BDT|-60|0|","Asia/Damascus|EET EEST|-20 -30|01010101010101010101010|1C0m0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0","Asia/Dili|TLT|-90|0|","Asia/Dubai|GST|-40|0|","Asia/Dushanbe|TJT|-50|0|","Asia/Gaza|EET EEST|-20 -30|01010101010101010101010|1BVW1 SKX 1xd1 MKX 1AN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0","Asia/Hebron|EET EEST|-20 -30|0101010101010101010101010|1BVy0 Tb0 1xd1 MKX bB0 cn0 1cN0 1a00 1fA0 1cL0 1cN0 1nX0 1210 1nz0 1210 1nz0 14N0 1nz0 1210 1nz0 1210 1nz0 1210 1nz0","Asia/Hong_Kong|HKT|-80|0|","Asia/Hovd|HOVT HOVST|-70 -80|0101010101010|1O8H0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Irkutsk|IRKT IRKST IRKT|-80 -90 -90|01020|1BWi0 1qM0 WM0 8Hz0","Asia/Istanbul|EET EEST|-20 -30|01010101010101010101010|1BWp0 1qM0 Xc0 1qo0 WM0 1qM0 11A0 1o00 1200 1nA0 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Jakarta|WIB|-70|0|","Asia/Jayapura|WIT|-90|0|","Asia/Jerusalem|IST IDT|-20 -30|01010101010101010101010|1BVA0 17X0 1kp0 1dz0 1c10 1aL0 1eN0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0","Asia/Kabul|AFT|-4u|0|","Asia/Kamchatka|PETT PETST PETT|-c0 -c0 -b0|0120|1BWe0 1qN0 WM0","Asia/Karachi|PKT|-50|0|","Asia/Kashgar|XJT|-60|0|","Asia/Kathmandu|NPT|-5J|0|","Asia/Khandyga|VLAT VLAST VLAT YAKT YAKT|-a0 -b0 -b0 -a0 -90|010234|1BWg0 1qM0 WM0 17V0 7zD0","Asia/Krasnoyarsk|KRAT KRAST KRAT|-70 -80 -80|01020|1BWj0 1qM0 WM0 8Hz0","Asia/Kuala_Lumpur|MYT|-80|0|","Asia/Magadan|MAGT MAGST MAGT MAGT|-b0 -c0 -c0 -a0|01023|1BWf0 1qM0 WM0 8Hz0","Asia/Makassar|WITA|-80|0|","Asia/Manila|PHT|-80|0|","Asia/Nicosia|EET EEST|-20 -30|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Asia/Novokuznetsk|KRAT NOVST NOVT NOVT|-70 -70 -60 -70|01230|1BWj0 1qN0 WM0 8Hz0","Asia/Novosibirsk|NOVT NOVST NOVT|-60 -70 -70|01020|1BWk0 1qM0 WM0 8Hz0","Asia/Omsk|OMST OMSST OMST|-60 -70 -70|01020|1BWk0 1qM0 WM0 8Hz0","Asia/Oral|ORAT|-50|0|","Asia/Pyongyang|KST|-90|0|","Asia/Qyzylorda|QYZT|-60|0|","Asia/Rangoon|MMT|-6u|0|","Asia/Sakhalin|SAKT SAKST SAKT|-a0 -b0 -b0|01020|1BWg0 1qM0 WM0 8Hz0","Asia/Samarkand|UZT|-50|0|","Asia/Singapore|SGT|-80|0|","Asia/Srednekolymsk|MAGT MAGST MAGT SRET|-b0 -c0 -c0 -b0|01023|1BWf0 1qM0 WM0 8Hz0","Asia/Tbilisi|GET|-40|0|","Asia/Tehran|IRST IRDT|-3u -4u|01010101010101010101010|1BTUu 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0","Asia/Thimbu|BTT|-60|0|","Asia/Tokyo|JST|-90|0|","Asia/Ulaanbaatar|ULAT ULAST|-80 -90|0101010101010|1O8G0 1cJ0 1cP0 1cJ0 1cP0 1fx0 1cP0 1cJ0 1cP0 1cJ0 1cP0 1cJ0","Asia/Ust-Nera|MAGT MAGST MAGT VLAT VLAT|-b0 -c0 -c0 -b0 -a0|010234|1BWf0 1qM0 WM0 17V0 7zD0","Asia/Vladivostok|VLAT VLAST VLAT|-a0 -b0 -b0|01020|1BWg0 1qM0 WM0 8Hz0","Asia/Yakutsk|YAKT YAKST YAKT|-90 -a0 -a0|01020|1BWh0 1qM0 WM0 8Hz0","Asia/Yekaterinburg|YEKT YEKST YEKT|-50 -60 -60|01020|1BWl0 1qM0 WM0 8Hz0","Asia/Yerevan|AMT AMST|-40 -50|01010|1BWm0 1qM0 WM0 1qM0","Atlantic/Azores|AZOT AZOST|10 0|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Canary|WET WEST|0 -10|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Atlantic/Cape_Verde|CVT|10|0|","Atlantic/South_Georgia|GST|20|0|","Atlantic/Stanley|FKST FKT|30 40|010|1C6R0 U10","Australia/ACT|AEDT AEST|-b0 -a0|01010101010101010101010|1C140 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Adelaide|ACDT ACST|-au -9u|01010101010101010101010|1C14u 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0","Australia/Brisbane|AEST|-a0|0|","Australia/Darwin|ACST|-9u|0|","Australia/Eucla|ACWST|-8J|0|","Australia/LHI|LHDT LHST|-b0 -au|01010101010101010101010|1C130 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu","Australia/Perth|AWST|-80|0|","Chile/EasterIsland|EASST EAST EAST|50 60 50|010101010102|1C1f0 1fB0 1nX0 G10 1EL0 Op0 1zb0 Rd0 1wn0 Rd0 1wn0","Eire|GMT IST|0 -10|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Etc/GMT+1|GMT+1|10|0|","Etc/GMT+10|GMT+10|a0|0|","Etc/GMT+11|GMT+11|b0|0|","Etc/GMT+12|GMT+12|c0|0|","Etc/GMT+2|GMT+2|20|0|","Etc/GMT+3|GMT+3|30|0|","Etc/GMT+4|GMT+4|40|0|","Etc/GMT+5|GMT+5|50|0|","Etc/GMT+6|GMT+6|60|0|","Etc/GMT+7|GMT+7|70|0|","Etc/GMT+8|GMT+8|80|0|","Etc/GMT+9|GMT+9|90|0|","Etc/GMT-1|GMT-1|-10|0|","Etc/GMT-10|GMT-10|-a0|0|","Etc/GMT-11|GMT-11|-b0|0|","Etc/GMT-12|GMT-12|-c0|0|","Etc/GMT-13|GMT-13|-d0|0|","Etc/GMT-14|GMT-14|-e0|0|","Etc/GMT-2|GMT-2|-20|0|","Etc/GMT-3|GMT-3|-30|0|","Etc/GMT-4|GMT-4|-40|0|","Etc/GMT-5|GMT-5|-50|0|","Etc/GMT-6|GMT-6|-60|0|","Etc/GMT-7|GMT-7|-70|0|","Etc/GMT-8|GMT-8|-80|0|","Etc/GMT-9|GMT-9|-90|0|","Etc/UCT|UCT|0|0|","Etc/UTC|UTC|0|0|","Europe/Belfast|GMT BST|0 -10|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","Europe/Kaliningrad|EET EEST FET|-20 -30 -30|01020|1BWo0 1qM0 WM0 8Hz0","Europe/Minsk|EET EEST FET MSK|-20 -30 -30 -30|01023|1BWo0 1qM0 WM0 8Hy0","Europe/Moscow|MSK MSD MSK|-30 -40 -40|01020|1BWn0 1qM0 WM0 8Hz0","Europe/Samara|SAMT SAMST SAMT|-40 -40 -30|0120|1BWm0 1qN0 WM0","Europe/Simferopol|EET EEST MSK MSK|-20 -30 -40 -30|01010101023|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11z0 1nW0","HST|HST|a0|0|","Indian/Chagos|IOT|-60|0|","Indian/Christmas|CXT|-70|0|","Indian/Cocos|CCT|-6u|0|","Indian/Kerguelen|TFT|-50|0|","Indian/Mahe|SCT|-40|0|","Indian/Maldives|MVT|-50|0|","Indian/Mauritius|MUT|-40|0|","Indian/Reunion|RET|-40|0|","Kwajalein|MHT|-c0|0|","MET|MET MEST|-10 -20|01010101010101010101010|1BWp0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00","NZ-CHAT|CHADT CHAST|-dJ -cJ|01010101010101010101010|1C120 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","Pacific/Apia|SST SDT WSDT WSST|b0 a0 -e0 -d0|01012323232323232323232|1Dbn0 1ff0 1a00 CI0 AQ0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00","Pacific/Bougainville|PGT BST|-a0 -b0|01|1NwE0","Pacific/Chuuk|CHUT|-a0|0|","Pacific/Efate|VUT|-b0|0|","Pacific/Enderbury|PHOT|-d0|0|","Pacific/Fakaofo|TKT TKT|b0 -d0|01|1Gfn0","Pacific/Fiji|FJST FJT|-d0 -c0|01010101010101010101010|1BWe0 1o00 Rc0 1wo0 Ao0 1Nc0 Ao0 1Q00 xz0 1SN0 uM0 1SM0 xA0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1SM0 uM0 1SM0","Pacific/Funafuti|TVT|-c0|0|","Pacific/Galapagos|GALT|60|0|","Pacific/Gambier|GAMT|90|0|","Pacific/Guadalcanal|SBT|-b0|0|","Pacific/Guam|ChST|-a0|0|","Pacific/Kiritimati|LINT|-e0|0|","Pacific/Kosrae|KOST|-b0|0|","Pacific/Marquesas|MART|9u|0|","Pacific/Midway|SST|b0|0|","Pacific/Nauru|NRT|-c0|0|","Pacific/Niue|NUT|b0|0|","Pacific/Norfolk|NFT|-bu|0|","Pacific/Noumea|NCT|-b0|0|","Pacific/Palau|PWT|-90|0|","Pacific/Pohnpei|PONT|-b0|0|","Pacific/Port_Moresby|PGT|-a0|0|","Pacific/Rarotonga|CKT|a0|0|","Pacific/Tahiti|TAHT|a0|0|","Pacific/Tarawa|GILT|-c0|0|","Pacific/Tongatapu|TOT|-d0|0|","Pacific/Wake|WAKT|-c0|0|","Pacific/Wallis|WFT|-c0|0|"],links:["Africa/Abidjan|Africa/Accra","Africa/Abidjan|Africa/Bamako","Africa/Abidjan|Africa/Banjul","Africa/Abidjan|Africa/Bissau","Africa/Abidjan|Africa/Conakry","Africa/Abidjan|Africa/Dakar","Africa/Abidjan|Africa/Freetown","Africa/Abidjan|Africa/Lome","Africa/Abidjan|Africa/Monrovia","Africa/Abidjan|Africa/Nouakchott","Africa/Abidjan|Africa/Ouagadougou","Africa/Abidjan|Africa/Sao_Tome","Africa/Abidjan|Africa/Timbuktu","Africa/Abidjan|America/Danmarkshavn","Africa/Abidjan|Atlantic/Reykjavik","Africa/Abidjan|Atlantic/St_Helena","Africa/Abidjan|Etc/GMT","Africa/Abidjan|Etc/GMT+0","Africa/Abidjan|Etc/GMT-0","Africa/Abidjan|Etc/GMT0","Africa/Abidjan|Etc/Greenwich","Africa/Abidjan|GMT","Africa/Abidjan|GMT+0","Africa/Abidjan|GMT-0","Africa/Abidjan|GMT0","Africa/Abidjan|Greenwich","Africa/Abidjan|Iceland","Africa/Addis_Ababa|Africa/Asmara","Africa/Addis_Ababa|Africa/Asmera","Africa/Addis_Ababa|Africa/Dar_es_Salaam","Africa/Addis_Ababa|Africa/Djibouti","Africa/Addis_Ababa|Africa/Juba","Africa/Addis_Ababa|Africa/Kampala","Africa/Addis_Ababa|Africa/Khartoum","Africa/Addis_Ababa|Africa/Mogadishu","Africa/Addis_Ababa|Africa/Nairobi","Africa/Addis_Ababa|Indian/Antananarivo","Africa/Addis_Ababa|Indian/Comoro","Africa/Addis_Ababa|Indian/Mayotte","Africa/Algiers|Africa/Tunis","Africa/Bangui|Africa/Brazzaville","Africa/Bangui|Africa/Douala","Africa/Bangui|Africa/Kinshasa","Africa/Bangui|Africa/Lagos","Africa/Bangui|Africa/Libreville","Africa/Bangui|Africa/Luanda","Africa/Bangui|Africa/Malabo","Africa/Bangui|Africa/Ndjamena","Africa/Bangui|Africa/Niamey","Africa/Bangui|Africa/Porto-Novo","Africa/Blantyre|Africa/Bujumbura","Africa/Blantyre|Africa/Gaborone","Africa/Blantyre|Africa/Harare","Africa/Blantyre|Africa/Kigali","Africa/Blantyre|Africa/Lubumbashi","Africa/Blantyre|Africa/Lusaka","Africa/Blantyre|Africa/Maputo","Africa/Cairo|Egypt","Africa/Casablanca|Africa/El_Aaiun","Africa/Ceuta|Arctic/Longyearbyen","Africa/Ceuta|Atlantic/Jan_Mayen","Africa/Ceuta|CET","Africa/Ceuta|Europe/Amsterdam","Africa/Ceuta|Europe/Andorra","Africa/Ceuta|Europe/Belgrade","Africa/Ceuta|Europe/Berlin","Africa/Ceuta|Europe/Bratislava","Africa/Ceuta|Europe/Brussels","Africa/Ceuta|Europe/Budapest","Africa/Ceuta|Europe/Busingen","Africa/Ceuta|Europe/Copenhagen","Africa/Ceuta|Europe/Gibraltar","Africa/Ceuta|Europe/Ljubljana","Africa/Ceuta|Europe/Luxembourg","Africa/Ceuta|Europe/Madrid","Africa/Ceuta|Europe/Malta","Africa/Ceuta|Europe/Monaco","Africa/Ceuta|Europe/Oslo","Africa/Ceuta|Europe/Paris","Africa/Ceuta|Europe/Podgorica","Africa/Ceuta|Europe/Prague","Africa/Ceuta|Europe/Rome","Africa/Ceuta|Europe/San_Marino","Africa/Ceuta|Europe/Sarajevo","Africa/Ceuta|Europe/Skopje","Africa/Ceuta|Europe/Stockholm","Africa/Ceuta|Europe/Tirane","Africa/Ceuta|Europe/Vaduz","Africa/Ceuta|Europe/Vatican","Africa/Ceuta|Europe/Vienna","Africa/Ceuta|Europe/Warsaw","Africa/Ceuta|Europe/Zagreb","Africa/Ceuta|Europe/Zurich","Africa/Ceuta|Poland","Africa/Johannesburg|Africa/Maseru","Africa/Johannesburg|Africa/Mbabane","Africa/Tripoli|Libya","America/Adak|America/Atka","America/Adak|US/Aleutian","America/Anchorage|America/Juneau","America/Anchorage|America/Nome","America/Anchorage|America/Sitka","America/Anchorage|America/Yakutat","America/Anchorage|US/Alaska","America/Anguilla|America/Antigua","America/Anguilla|America/Aruba","America/Anguilla|America/Barbados","America/Anguilla|America/Blanc-Sablon","America/Anguilla|America/Curacao","America/Anguilla|America/Dominica","America/Anguilla|America/Grenada","America/Anguilla|America/Guadeloupe","America/Anguilla|America/Kralendijk","America/Anguilla|America/Lower_Princes","America/Anguilla|America/Marigot","America/Anguilla|America/Martinique","America/Anguilla|America/Montserrat","America/Anguilla|America/Port_of_Spain","America/Anguilla|America/Puerto_Rico","America/Anguilla|America/Santo_Domingo","America/Anguilla|America/St_Barthelemy","America/Anguilla|America/St_Kitts","America/Anguilla|America/St_Lucia","America/Anguilla|America/St_Thomas","America/Anguilla|America/St_Vincent","America/Anguilla|America/Tortola","America/Anguilla|America/Virgin","America/Argentina/Buenos_Aires|America/Argentina/Catamarca","America/Argentina/Buenos_Aires|America/Argentina/ComodRivadavia","America/Argentina/Buenos_Aires|America/Argentina/Cordoba","America/Argentina/Buenos_Aires|America/Argentina/Jujuy","America/Argentina/Buenos_Aires|America/Argentina/La_Rioja","America/Argentina/Buenos_Aires|America/Argentina/Mendoza","America/Argentina/Buenos_Aires|America/Argentina/Rio_Gallegos","America/Argentina/Buenos_Aires|America/Argentina/Salta","America/Argentina/Buenos_Aires|America/Argentina/San_Juan","America/Argentina/Buenos_Aires|America/Argentina/San_Luis","America/Argentina/Buenos_Aires|America/Argentina/Tucuman","America/Argentina/Buenos_Aires|America/Argentina/Ushuaia","America/Argentina/Buenos_Aires|America/Buenos_Aires","America/Argentina/Buenos_Aires|America/Catamarca","America/Argentina/Buenos_Aires|America/Cordoba","America/Argentina/Buenos_Aires|America/Jujuy","America/Argentina/Buenos_Aires|America/Mendoza","America/Argentina/Buenos_Aires|America/Rosario","America/Atikokan|America/Cayman","America/Atikokan|America/Coral_Harbour","America/Atikokan|America/Jamaica","America/Atikokan|America/Panama","America/Atikokan|EST","America/Atikokan|Jamaica","America/Belem|America/Fortaleza","America/Belem|America/Maceio","America/Belem|America/Recife","America/Belem|America/Santarem","America/Belize|America/Costa_Rica","America/Belize|America/El_Salvador","America/Belize|America/Guatemala","America/Belize|America/Managua","America/Belize|America/Regina","America/Belize|America/Swift_Current","America/Belize|America/Tegucigalpa","America/Belize|Canada/East-Saskatchewan","America/Belize|Canada/Saskatchewan","America/Boa_Vista|America/Manaus","America/Boa_Vista|America/Porto_Velho","America/Boa_Vista|Brazil/West","America/Boise|America/Cambridge_Bay","America/Boise|America/Denver","America/Boise|America/Edmonton","America/Boise|America/Inuvik","America/Boise|America/Ojinaga","America/Boise|America/Shiprock","America/Boise|America/Yellowknife","America/Boise|Canada/Mountain","America/Boise|MST7MDT","America/Boise|Navajo","America/Boise|US/Mountain","America/Campo_Grande|America/Cuiaba","America/Chicago|America/Indiana/Knox","America/Chicago|America/Indiana/Tell_City","America/Chicago|America/Knox_IN","America/Chicago|America/Matamoros","America/Chicago|America/Menominee","America/Chicago|America/North_Dakota/Center","America/Chicago|America/North_Dakota/New_Salem","America/Chicago|America/Rainy_River","America/Chicago|America/Rankin_Inlet","America/Chicago|America/Resolute","America/Chicago|America/Winnipeg","America/Chicago|CST6CDT","America/Chicago|Canada/Central","America/Chicago|US/Central","America/Chicago|US/Indiana-Starke","America/Chihuahua|America/Mazatlan","America/Chihuahua|Mexico/BajaSur","America/Creston|America/Dawson_Creek","America/Creston|America/Hermosillo","America/Creston|America/Phoenix","America/Creston|MST","America/Creston|US/Arizona","America/Dawson|America/Ensenada","America/Dawson|America/Los_Angeles","America/Dawson|America/Tijuana","America/Dawson|America/Vancouver","America/Dawson|America/Whitehorse","America/Dawson|Canada/Pacific","America/Dawson|Canada/Yukon","America/Dawson|Mexico/BajaNorte","America/Dawson|PST8PDT","America/Dawson|US/Pacific","America/Dawson|US/Pacific-New","America/Detroit|America/Fort_Wayne","America/Detroit|America/Indiana/Indianapolis","America/Detroit|America/Indiana/Marengo","America/Detroit|America/Indiana/Petersburg","America/Detroit|America/Indiana/Vevay","America/Detroit|America/Indiana/Vincennes","America/Detroit|America/Indiana/Winamac","America/Detroit|America/Indianapolis","America/Detroit|America/Iqaluit","America/Detroit|America/Kentucky/Louisville","America/Detroit|America/Kentucky/Monticello","America/Detroit|America/Louisville","America/Detroit|America/Montreal","America/Detroit|America/Nassau","America/Detroit|America/New_York","America/Detroit|America/Nipigon","America/Detroit|America/Pangnirtung","America/Detroit|America/Thunder_Bay","America/Detroit|America/Toronto","America/Detroit|Canada/Eastern","America/Detroit|EST5EDT","America/Detroit|US/East-Indiana","America/Detroit|US/Eastern","America/Detroit|US/Michigan","America/Eirunepe|America/Porto_Acre","America/Eirunepe|America/Rio_Branco","America/Eirunepe|Brazil/Acre","America/Glace_Bay|America/Halifax","America/Glace_Bay|America/Moncton","America/Glace_Bay|America/Thule","America/Glace_Bay|Atlantic/Bermuda","America/Glace_Bay|Canada/Atlantic","America/Havana|Cuba","America/Merida|America/Mexico_City","America/Merida|America/Monterrey","America/Merida|Mexico/General","America/Metlakatla|Pacific/Pitcairn","America/Noronha|Brazil/DeNoronha","America/Santiago|Antarctica/Palmer","America/Santiago|Chile/Continental","America/Sao_Paulo|Brazil/East","America/St_Johns|Canada/Newfoundland","Antarctica/McMurdo|Antarctica/South_Pole","Antarctica/McMurdo|NZ","Antarctica/McMurdo|Pacific/Auckland","Asia/Aden|Asia/Baghdad","Asia/Aden|Asia/Bahrain","Asia/Aden|Asia/Kuwait","Asia/Aden|Asia/Qatar","Asia/Aden|Asia/Riyadh","Asia/Aqtau|Asia/Aqtobe","Asia/Ashgabat|Asia/Ashkhabad","Asia/Bangkok|Asia/Ho_Chi_Minh","Asia/Bangkok|Asia/Phnom_Penh","Asia/Bangkok|Asia/Saigon","Asia/Bangkok|Asia/Vientiane","Asia/Calcutta|Asia/Colombo","Asia/Calcutta|Asia/Kolkata","Asia/Chongqing|Asia/Chungking","Asia/Chongqing|Asia/Harbin","Asia/Chongqing|Asia/Macao","Asia/Chongqing|Asia/Macau","Asia/Chongqing|Asia/Shanghai","Asia/Chongqing|Asia/Taipei","Asia/Chongqing|PRC","Asia/Chongqing|ROC","Asia/Dacca|Asia/Dhaka","Asia/Dubai|Asia/Muscat","Asia/Hong_Kong|Hongkong","Asia/Istanbul|Europe/Istanbul","Asia/Istanbul|Turkey","Asia/Jakarta|Asia/Pontianak","Asia/Jerusalem|Asia/Tel_Aviv","Asia/Jerusalem|Israel","Asia/Kashgar|Asia/Urumqi","Asia/Kathmandu|Asia/Katmandu","Asia/Kuala_Lumpur|Asia/Kuching","Asia/Makassar|Asia/Ujung_Pandang","Asia/Nicosia|EET","Asia/Nicosia|Europe/Athens","Asia/Nicosia|Europe/Bucharest","Asia/Nicosia|Europe/Chisinau","Asia/Nicosia|Europe/Helsinki","Asia/Nicosia|Europe/Kiev","Asia/Nicosia|Europe/Mariehamn","Asia/Nicosia|Europe/Nicosia","Asia/Nicosia|Europe/Riga","Asia/Nicosia|Europe/Sofia","Asia/Nicosia|Europe/Tallinn","Asia/Nicosia|Europe/Tiraspol","Asia/Nicosia|Europe/Uzhgorod","Asia/Nicosia|Europe/Vilnius","Asia/Nicosia|Europe/Zaporozhye","Asia/Pyongyang|Asia/Seoul","Asia/Pyongyang|ROK","Asia/Samarkand|Asia/Tashkent","Asia/Singapore|Singapore","Asia/Tehran|Iran","Asia/Thimbu|Asia/Thimphu","Asia/Tokyo|Japan","Asia/Ulaanbaatar|Asia/Ulan_Bator","Atlantic/Canary|Atlantic/Faeroe","Atlantic/Canary|Atlantic/Faroe","Atlantic/Canary|Atlantic/Madeira","Atlantic/Canary|Europe/Lisbon","Atlantic/Canary|Portugal","Atlantic/Canary|WET","Australia/ACT|Australia/Canberra","Australia/ACT|Australia/Currie","Australia/ACT|Australia/Hobart","Australia/ACT|Australia/Melbourne","Australia/ACT|Australia/NSW","Australia/ACT|Australia/Sydney","Australia/ACT|Australia/Tasmania","Australia/ACT|Australia/Victoria","Australia/Adelaide|Australia/Broken_Hill","Australia/Adelaide|Australia/South","Australia/Adelaide|Australia/Yancowinna","Australia/Brisbane|Australia/Lindeman","Australia/Brisbane|Australia/Queensland","Australia/Darwin|Australia/North","Australia/LHI|Australia/Lord_Howe","Australia/Perth|Australia/West","Chile/EasterIsland|Pacific/Easter","Eire|Europe/Dublin","Etc/UCT|UCT","Etc/UTC|Etc/Universal","Etc/UTC|Etc/Zulu","Etc/UTC|UTC","Etc/UTC|Universal","Etc/UTC|Zulu","Europe/Belfast|Europe/Guernsey","Europe/Belfast|Europe/Isle_of_Man","Europe/Belfast|Europe/Jersey","Europe/Belfast|Europe/London","Europe/Belfast|GB","Europe/Belfast|GB-Eire","Europe/Moscow|Europe/Volgograd","Europe/Moscow|W-SU","HST|Pacific/Honolulu","HST|Pacific/Johnston","HST|US/Hawaii","Kwajalein|Pacific/Kwajalein","Kwajalein|Pacific/Majuro","NZ-CHAT|Pacific/Chatham","Pacific/Chuuk|Pacific/Truk","Pacific/Chuuk|Pacific/Yap","Pacific/Guam|Pacific/Saipan","Pacific/Midway|Pacific/Pago_Pago","Pacific/Midway|Pacific/Samoa","Pacific/Midway|US/Samoa","Pacific/Pohnpei|Pacific/Ponape"]}),a}); \ No newline at end of file diff --git a/docs/_docs/troubleshooting.md b/docs/_docs/troubleshooting.md index bcaebbda5..800fb202c 100644 --- a/docs/_docs/troubleshooting.md +++ b/docs/_docs/troubleshooting.md @@ -36,11 +36,12 @@ If the service isn't wanting to restart, look at its status for clues as to why > If you find yourself constantly needing to restart a service, there's a chance it was never set to autostart on system boot. Use `sudo systemctl enable servicename` to fix this problem. -## 3. Basic troubleshooting +## 3. Known problems If you have one of these issues, please try to resolve it with the instructions below before moving on in the troubleshooting checklist. +- **Streaming player on Microsite and Listen player on Dashboard not working?** The problem could be caused by a bug in writing to the database during the setup wizard. This can be fixed by going to **Settings** -> **Stream Settings** and toggling the **Default Streaming** and **Custom/ 3rd Party Streaming** option, accepting the popup dialogues, and clicking **Save** at the top of the settings page. - **File not importing successfully?** Libretime has been known to work with MP3 and WAV files, encoded using 41,100 Hz. Variable Bit Rate (VBR) files are currently hit or miss with the importer. Please convert your file to an MP3 or WAV at 41,100 Hz. and try uploading again. - **Podcast hosted by Anchor.fm not importing?** There is no known work-around at this time. Ask your producers to provide their show files manually or check with the show's distributer. - **Tracks won't publish?** We know the Publish screen is broken and we're working on it. A potential work-around is to use an external podcast host like [Anchor.fm](https://www.anchor.fm) or [Blubrry](https://blubrry.com/). diff --git a/python_apps/pypo/liquidsoap/fdkaac.liq b/python_apps/pypo/liquidsoap/1.1/fdkaac.liq similarity index 100% rename from python_apps/pypo/liquidsoap/fdkaac.liq rename to python_apps/pypo/liquidsoap/1.1/fdkaac.liq diff --git a/python_apps/pypo/liquidsoap/ls_lib_legacy.liq b/python_apps/pypo/liquidsoap/1.1/ls_lib.liq similarity index 100% rename from python_apps/pypo/liquidsoap/ls_lib_legacy.liq rename to python_apps/pypo/liquidsoap/1.1/ls_lib.liq diff --git a/python_apps/pypo/liquidsoap/ls_script_legacy.liq b/python_apps/pypo/liquidsoap/1.1/ls_script.liq similarity index 99% rename from python_apps/pypo/liquidsoap/ls_script_legacy.liq rename to python_apps/pypo/liquidsoap/1.1/ls_script.liq index c7bf90a5a..59bcb4498 100644 --- a/python_apps/pypo/liquidsoap/ls_script_legacy.liq +++ b/python_apps/pypo/liquidsoap/1.1/ls_script.liq @@ -34,7 +34,7 @@ s2_namespace = ref '' s3_namespace = ref '' just_switched = ref false -%include "ls_lib_legacy.liq" +%include "ls_lib.liq" sources = ref [] source_id = ref 0 diff --git a/python_apps/pypo/liquidsoap/mp3.liq b/python_apps/pypo/liquidsoap/1.1/mp3.liq similarity index 100% rename from python_apps/pypo/liquidsoap/mp3.liq rename to python_apps/pypo/liquidsoap/1.1/mp3.liq diff --git a/python_apps/pypo/liquidsoap/ogg.liq b/python_apps/pypo/liquidsoap/1.1/ogg.liq similarity index 100% rename from python_apps/pypo/liquidsoap/ogg.liq rename to python_apps/pypo/liquidsoap/1.1/ogg.liq diff --git a/python_apps/pypo/liquidsoap/opus.liq b/python_apps/pypo/liquidsoap/1.1/opus.liq similarity index 100% rename from python_apps/pypo/liquidsoap/opus.liq rename to python_apps/pypo/liquidsoap/1.1/opus.liq diff --git a/python_apps/pypo/liquidsoap/1.3/fdkaac.liq b/python_apps/pypo/liquidsoap/1.3/fdkaac.liq new file mode 100644 index 000000000..3387c9340 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.3/fdkaac.liq @@ -0,0 +1,24 @@ + if bitrate == 24 then + ignore(output_stereo(%fdkaac(bitrate = 24, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 32 then + ignore(output_stereo(%fdkaac(bitrate = 32, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 48 then + ignore(output_stereo(%fdkaac(bitrate = 48, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 64 then + ignore(output_stereo(%fdkaac(bitrate = 64, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 96 then + ignore(output_stereo(%fdkaac(bitrate = 96, aot="mpeg4_aac_lc", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 128 then + ignore(output_stereo(%fdkaac(bitrate = 128, aot="mpeg4_aac_lc", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 160 then + ignore(output_stereo(%fdkaac(bitrate = 160, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 192 then + ignore(output_stereo(%fdkaac(bitrate = 192, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 224 then + ignore(output_stereo(%fdkaac(bitrate = 224, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 256 then + ignore(output_stereo(%fdkaac(bitrate = 256, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 320 then + ignore(output_stereo(%fdkaac(bitrate = 320, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + end + diff --git a/python_apps/pypo/liquidsoap/ls_lib.liq b/python_apps/pypo/liquidsoap/1.3/ls_lib.liq similarity index 100% rename from python_apps/pypo/liquidsoap/ls_lib.liq rename to python_apps/pypo/liquidsoap/1.3/ls_lib.liq diff --git a/python_apps/pypo/liquidsoap/ls_script.liq b/python_apps/pypo/liquidsoap/1.3/ls_script.liq similarity index 100% rename from python_apps/pypo/liquidsoap/ls_script.liq rename to python_apps/pypo/liquidsoap/1.3/ls_script.liq diff --git a/python_apps/pypo/liquidsoap/1.3/mp3.liq b/python_apps/pypo/liquidsoap/1.3/mp3.liq new file mode 100644 index 000000000..d403f54d1 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.3/mp3.liq @@ -0,0 +1,68 @@ + if bitrate == 24 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 24, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 24, stereo = false), mean(!source))) + end + elsif bitrate == 32 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 32, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 32, stereo = false), mean(!source))) + end + elsif bitrate == 48 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 48, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 48, stereo = false), mean(!source))) + end + elsif bitrate == 64 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 64, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 64, stereo = false), mean(!source))) + end + elsif bitrate == 96 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 96, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 96, stereo = false), mean(!source))) + end + elsif bitrate == 128 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 128, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 128, stereo = false), mean(!source))) + end + elsif bitrate == 160 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 160, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 160, stereo = false), mean(!source))) + end + elsif bitrate == 192 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 192, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 192, stereo = false), mean(!source))) + end + elsif bitrate == 224 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 224, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 224, stereo = false), mean(!source))) + end + elsif bitrate == 256 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 256, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 256, stereo = false), mean(!source))) + end + elsif bitrate == 320 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 320, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 320, stereo = false), mean(!source))) + end + end + diff --git a/python_apps/pypo/liquidsoap/1.3/ogg.liq b/python_apps/pypo/liquidsoap/1.3/ogg.liq new file mode 100644 index 000000000..178bdf531 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.3/ogg.liq @@ -0,0 +1,60 @@ + if not icecast_vorbis_metadata then + source := add(normalize=false, [amplify(0.00001, noise()), !source]) + end + + if bitrate == 24 or bitrate == 32 or bitrate == 48 then + if stereo then + ignore(output_stereo(%vorbis(quality=-0.1, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=-0.1, channels = 1), mean(!source))) + end + elsif bitrate == 64 then + if stereo then + ignore(output_stereo(%vorbis(quality=0, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0, channels = 1), mean(!source))) + end + elsif bitrate == 96 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.2, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.2, channels = 1), mean(!source))) + end + elsif bitrate == 128 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.4, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.4, channels = 1), mean(!source))) + end + elsif bitrate == 160 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.5, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.5, channels = 1), mean(!source))) + end + elsif bitrate == 192 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.6, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.6, channels = 1), mean(!source))) + end + elsif bitrate == 224 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.7, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.7, channels = 1), mean(!source))) + end + elsif bitrate == 256 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.8, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.8, channels = 1), mean(!source))) + end + elsif bitrate == 320 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.9, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.9, channels = 1), mean(!source))) + end + end + diff --git a/python_apps/pypo/liquidsoap/1.3/opus.liq b/python_apps/pypo/liquidsoap/1.3/opus.liq new file mode 100644 index 000000000..3ad6f6c55 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.3/opus.liq @@ -0,0 +1,68 @@ + if bitrate == 24 then + if stereo then + ignore(output_stereo(%opus(bitrate = 24, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 24, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 32 then + if stereo then + ignore(output_stereo(%opus(bitrate = 32, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 32, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 48 then + if stereo then + ignore(output_stereo(%opus(bitrate = 48, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 48, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 64 then + if stereo then + ignore(output_stereo(%opus(bitrate = 64, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 64, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 96 then + if stereo then + ignore(output_stereo(%opus(bitrate = 96, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 96, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 128 then + if stereo then + ignore(output_stereo(%opus(bitrate = 128, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 128, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 160 then + if stereo then + ignore(output_stereo(%opus(bitrate = 160, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 160, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 192 then + if stereo then + ignore(output_stereo(%opus(bitrate = 192, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 192, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 224 then + if stereo then + ignore(output_stereo(%opus(bitrate = 224, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 224, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 256 then + if stereo then + ignore(output_stereo(%opus(bitrate = 256, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 256, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 320 then + if stereo then + ignore(output_stereo(%opus(bitrate = 320, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 320, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + end + diff --git a/python_apps/pypo/liquidsoap/1.4/fdkaac.liq b/python_apps/pypo/liquidsoap/1.4/fdkaac.liq new file mode 100644 index 000000000..3387c9340 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.4/fdkaac.liq @@ -0,0 +1,24 @@ + if bitrate == 24 then + ignore(output_stereo(%fdkaac(bitrate = 24, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 32 then + ignore(output_stereo(%fdkaac(bitrate = 32, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 48 then + ignore(output_stereo(%fdkaac(bitrate = 48, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 64 then + ignore(output_stereo(%fdkaac(bitrate = 64, aot="mpeg4_he_aac_v2", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 96 then + ignore(output_stereo(%fdkaac(bitrate = 96, aot="mpeg4_aac_lc", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 128 then + ignore(output_stereo(%fdkaac(bitrate = 128, aot="mpeg4_aac_lc", afterburner=false, sbr_mode=true), !source)) + elsif bitrate == 160 then + ignore(output_stereo(%fdkaac(bitrate = 160, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 192 then + ignore(output_stereo(%fdkaac(bitrate = 192, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 224 then + ignore(output_stereo(%fdkaac(bitrate = 224, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 256 then + ignore(output_stereo(%fdkaac(bitrate = 256, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + elsif bitrate == 320 then + ignore(output_stereo(%fdkaac(bitrate = 320, aot="mpeg4_aac_lc", afterburner=true, sbr_mode=true), !source)) + end + diff --git a/python_apps/pypo/liquidsoap/1.4/ls_lib.liq b/python_apps/pypo/liquidsoap/1.4/ls_lib.liq new file mode 100644 index 000000000..ee32f4871 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.4/ls_lib.liq @@ -0,0 +1,389 @@ +def notify(m) + command = "timeout --signal=KILL 45 pyponotify --media-id=#{m['schedule_table_id']} &" + log(command) + system(command) +end + +def notify_queue(m) + f = !dynamic_metadata_callback + ignore(f(m)) + notify(m) +end + +def notify_stream(m) + json_str = string.replace(pattern="\n",(fun (s) -> ""), json_of(m)) + #if a string has a single apostrophe in it, let's comment it out by ending the string before right before it + #escaping the apostrophe, and then starting a new string right after it. This is why we use 3 apostrophes. + json_str = string.replace(pattern="'",(fun (s) -> "'\''"), json_str) + command = "timeout --signal=KILL 45 pyponotify --webstream='#{json_str}' --media-id=#{!current_dyn_id} &" + + if !current_dyn_id != "-1" then + log(command) + system(command) + end +end + +# A function applied to each metadata chunk +def append_title(m) = + log("Using stream_format #{!stream_metadata_type}") + + if list.mem_assoc("mapped", m) then + #protection against applying this function twice. It shouldn't be happening + #and bug file with Liquidsoap. + m + else + if !stream_metadata_type == 1 then + [("title", "#{!show_name} - #{m['artist']} - #{m['title']}"), ("mapped", "true")] + elsif !stream_metadata_type == 2 then + [("title", "#{!station_name} - #{!show_name}"), ("mapped", "true")] + else + if "#{m['artist']}" == "" then + [("title", "#{m['title']}"), ("mapped", "true")] + else + [("title", "#{m['artist']} - #{m['title']}"), ("mapped", "true")] + end + end + end +end + +def transition(a,b) = + log("transition called...") + add(normalize=false, + [ sequence([ blank(duration=0.01), + fade.initial(duration=!default_dj_fade, b) ]), + fade.final(duration=!default_dj_fade, a) ]) +end + +# we need this function for special transition case(from default to queue) +# we don't want the trasition fade to have effect on the first song that would +# be played siwtching out of the default(silent) source +def transition_default(a,b) = + log("transition called...") + if !just_switched then + just_switched := false + add(normalize=false, + [ sequence([ blank(duration=0.01), + fade.initial(duration=!default_dj_fade, b) ]), + fade.final(duration=!default_dj_fade, a) ]) + else + just_switched := false + b + end +end + + +# Define a transition that fades out the +# old source, adds a single, and then +# plays the new source +def to_live(old,new) = + # Fade out old source + old = fade.final(old) + # Compose this in sequence with + # the new source + sequence([old,new]) +end + + +def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, description, genre, user, s, stream, connected, name, channels) = + source = ref s + def on_error(msg) + connected := "false" + command = "timeout --signal=KILL 45 pyponotify --error='#{msg}' --stream-id=#{stream} --time=#{!time} &" + system(command) + log(command) + 5. + end + def on_connect() + connected := "true" + command = "timeout --signal=KILL 45 pyponotify --connect --stream-id=#{stream} --time=#{!time} &" + system(command) + log(command) + end + + stereo = (channels == "stereo") + + if output_type == "icecast" then + user_ref = ref user + if user == "" then + user_ref := "source" + end + output_mono = output.icecast(host = host, + port = port, + password = pass, + mount = mount_point, + fallible = true, + url = url, + description = description, + name = name, + genre = genre, + user = !user_ref, + on_error = on_error, + on_connect = on_connect) + + output_stereo = output.icecast(host = host, + port = port, + password = pass, + mount = mount_point, + fallible = true, + url = url, + description = description, + name = name, + genre = genre, + user = !user_ref, + on_error = on_error, + on_connect = on_connect) + if type == "mp3" then + %include "mp3.liq" + end + if type == "ogg" then + %include "ogg.liq" + end + + %ifencoder %opus + if type == "opus" then + %include "opus.liq" + end + %endif + + %ifencoder %fdkaac + if type == "aac" then + %include "fdkaac.liq" + end + %endif + else + user_ref = ref user + if user == "" then + user_ref := "source" + end + + output_mono = output.shoutcast(id = "shoutcast_stream_#{stream}", + host = host, + port = port, + password = pass, + fallible = true, + url = url, + genre = genre, + name = description, + user = !user_ref, + on_error = on_error, + on_connect = on_connect) + + output_stereo = output.shoutcast(id = "shoutcast_stream_#{stream}", + host = host, + port = port, + password = pass, + fallible = true, + url = url, + genre = genre, + name = description, + user = !user_ref, + on_error = on_error, + on_connect = on_connect) + + if type == "mp3" then + %include "mp3.liq" + end + + %ifencoder %fdkaac + if type == "aac" then + %include "fdkaac.liq" + end + %endif + end +end + +# Add a skip function to a source +# when it does not have one +# by default +#def add_skip_command(s) +# # A command to skip +# def skip(_) +# # get playing (active) queue and flush it +# l = list.hd(server.execute("queue.secondary_queue")) +# l = string.split(separator=" ",l) +# list.iter(fun (rid) -> ignore(server.execute("queue.remove #{rid}")), l) +# +# l = list.hd(server.execute("queue.primary_queue")) +# l = string.split(separator=" ", l) +# if list.length(l) > 0 then +# source.skip(s) +# "Skipped" +# else +# "Not skipped" +# end +# end +# # Register the command: +# server.register(namespace="source", +# usage="skip", +# description="Skip the current song.", +# "skip",fun(s) -> begin log("source.skip") skip(s) end) +#end + +def clear_queue(s) + source.skip(s) +end + +def set_dynamic_source_id(id) = + current_dyn_id := id + string_of(!current_dyn_id) +end + +def get_dynamic_source_id() = + string_of(!current_dyn_id) +end + +#cc-4633 + + +# NOTE +# A few values are hardcoded and may be dependent: +# - the delay in gracetime is linked with the buffer duration of input.http +# (delay should be a bit less than buffer) +# - crossing duration should be less than buffer length +# (at best, a higher duration will be ineffective) + +# HTTP input with "restart" command that waits for "stop" to be effected +# before "start" command is issued. Optionally it takes a new URL to play, +# which makes it a convenient replacement for "url". +# In the future, this may become a core feature of the HTTP input. +# TODO If we stop and restart quickly several times in a row, +# the data bursts accumulate and create buffer overflow. +# Flushing the buffer on restart could be a good idea, but +# it would also create an interruptions while the buffer is +# refilling... on the other hand, this would avoid having to +# fade using both cross() and switch(). +def input.http_restart(~id,~initial_url="http://dummy/url") + + source = audio_to_stereo(input.http(buffer=5.,max=15.,id=id,autostart=false,initial_url)) + + def stopped() + "stopped" == list.hd(server.execute("#{id}.status"), default="") + end + + server.register(namespace=id, + "restart", + usage="restart [url]", + fun (url) -> begin + if url != "" then + log(string_of(server.execute("#{id}.url #{url}"))) + end + log(string_of(server.execute("#{id}.stop"))) + add_timeout(0.5, + { if stopped() then + log(string_of(server.execute("#{id}.start"))) ; + (-1.) + else 0.5 end}) + "OK" + end) + + # Dummy output should be useless if HTTP stream is meant + # to be listened to immediately. Otherwise, apply it. + # + # output.dummy(fallible=true,source) + + source + +end + +# Transitions between URL changes in HTTP streams. +def cross_http(~debug=true,~http_input_id,source) + + id = http_input_id + last_url = ref "" + change = ref false + + def on_m(m) + notify_stream(m) + changed = m["source_url"] != !last_url + log("URL now #{m['source_url']} (change: #{changed})") + if changed then + if !last_url != "" then change := true end + last_url := m["source_url"] + end + end + + # We use both metadata and status to know about the current URL. + # Using only metadata may be more precise is crazy corner cases, + # but it's also asking too much: the metadata may not pass through + # before the crosser is instantiated. + # Using only status in crosser misses some info, eg. on first URL. + source = on_metadata(on_m,source) + + cross_d = 3. + + def crosser(a,b,ma,mb,sa,sb) + url = list.hd(server.execute('#{id}.url'), default="") + status = list.hd(server.execute('#{id}.status')) + on_m([("source_url",url)]) + if debug then + log("New track inside HTTP stream") + log(" status: #{status}") + log(" need to cross: #{!change}") + log(" remaining #{source.remaining(sa)} sec before, \ + #{source.remaining(sb)} sec after") + end + if !change then + change := false + # In principle one should avoid crossing on a live stream + # it'd be okay to do it here (eg. use add instead of sequence) + # because it's only once per URL, but be cautious. + sequence([fade.out(duration=cross_d,sa),fade.in(sb)]) + else + # This is done on tracks inside a single stream. + # Do NOT cross here or you'll gradually empty the buffer! + sequence([sa,sb]) + end + end + + # Setting conservative=true would mess with the delayed switch below + cross(duration=cross_d,conservative=false,crosser,source) +end + +# Custom fallback between http and default source with fading of +# beginning and end of HTTP stream. +# It does not take potential URL changes into account, as long as +# they do not interrupt streaming (thanks to the HTTP buffer). +def http_fallback(~http_input_id,~http,~default) + + id = http_input_id + + # We use a custom switching predicate to trigger switching (and thus, + # transitions) before the end of a track (rather, end of HTTP stream). + # It is complexified because we don't want to trigger switching when + # HTTP disconnects for just an instant, when changing URL: for that + # we use gracetime below. + + def gracetime(~delay=3.,f) + last_true = ref 0. + { if f() then + last_true := gettimeofday() + true + else + gettimeofday() < !last_true+delay + end } + end + + def connected() + status = list.hd(server.execute("#{id}.status"), default="") + not(list.mem(status,["polling","stopped"])) + end + connected = gracetime(connected) + + def to_live(a,b) = + log("TRANSITION to live") + add(normalize=false, + [fade.initial(b),fade.final(a)]) + end + def to_static(a,b) = + log("TRANSITION to static") + sequence([fade.out(a),fade.initial(b)]) + end + + switch( + track_sensitive=false, + transitions=[to_live,to_static], + [(# make sure it is connected, and not buffering + {connected() and source.is_ready(http) and !webstream_enabled}, http), + ({true},default)]) + +end diff --git a/python_apps/pypo/liquidsoap/1.4/ls_script.liq b/python_apps/pypo/liquidsoap/1.4/ls_script.liq new file mode 100644 index 000000000..2a4033c5b --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.4/ls_script.liq @@ -0,0 +1,462 @@ +%include "/etc/airtime/liquidsoap.cfg" + +set("log.file.path", log_file) +set("server.telnet", true) +set("server.telnet.port", 1234) +# set("init.daemon.pidfile.path", "/var/run/airtime/airtime-liquidsoap.pid") + + +#Dynamic source list +#dyn_sources = ref [] +webstream_enabled = ref false + +time = ref string_of(gettimeofday()) + +#live stream setup +set("harbor.bind_addr", "0.0.0.0") + +current_dyn_id = ref '-1' + +pypo_data = ref '0' +stream_metadata_type = ref 0 +default_dj_fade = ref 0. +station_name = ref '' +show_name = ref '' + +dynamic_metadata_callback = ref fun (~new_track=false, s) -> begin () end + +s1_connected = ref '' +s2_connected = ref '' +s3_connected = ref '' +s4_connected = ref '' +s1_namespace = ref '' +s2_namespace = ref '' +s3_namespace = ref '' +just_switched = ref false + +%include "ls_lib.liq" + +sources = ref [] +source_id = ref 0 + +def check_version(~version=liquidsoap.version, major, minor) = + v = list.map(int_of_string, string.split(separator="\.", version)) + list.nth(v,0,default=0) > major or list.nth(v,0,default=0) == major and list.nth(v,1,default=0) >= minor +end + +# cue cut fix for liquidsoap <1.2.2 +# +# This was most likely broken on 1.1.1 (debian) as well. +# +# adapted from https://github.com/savonet/liquidsoap/issues/390#issuecomment-277562081 +# +def fix_cue_in(~cue_in_metadata='liq_cue_in', m) = + # 0.04 might need to be adjusted according to your frame size + if float_of_string(m[cue_in_metadata]) < 0.04 then + [(cue_in_metadata, "0")] + else + [] + end +end + +def create_source() + l = request.equeue(id="s#{!source_id}", length=0.5) + + l = audio_to_stereo(id="queue_src", l) + + l = if not check_version(1, 3) then + map_metadata(fix_cue_in, l) + else + l + end + l = cue_cut(l) + l = amplify(1., override="replay_gain", l) + + # the crossfade function controls fade in/out + l = crossfade(duration=0., smart=true, l) + + l = on_metadata(notify_queue, l) + + sources := list.append([l], !sources) + server.register(namespace="queues", + "s#{!source_id}_skip", + fun (s) -> begin log("queues.s#{!source_id}_skip") + clear_queue(l) + "Done" + end) + source_id := !source_id + 1 +end + +create_source() +create_source() +create_source() +create_source() + +create_source() +create_source() +create_source() +create_source() + +queue = add(!sources, normalize=false) +pair = insert_metadata(queue) +dynamic_metadata_callback := fst(pair) +queue = snd(pair) + +output.dummy(fallible=true, queue) + +http = input.http_restart(id="http") +http = cross_http(http_input_id="http",http) +output.dummy(fallible=true, http) +stream_queue = http_fallback(http_input_id="http", http=http, default=queue) +stream_queue = map_metadata(update=false, append_title, stream_queue) + +ignore(output.dummy(stream_queue, fallible=true)) + +server.register(namespace="vars", + "pypo_data", + fun (s) -> begin log("vars.pypo_data") pypo_data := s "Done" end) +server.register(namespace="vars", + "stream_metadata_type", + fun (s) -> begin log("vars.stream_metadata_type") stream_metadata_type := int_of_string(s) s end) +server.register(namespace="vars", + "show_name", + fun (s) -> begin log("vars.show_name") show_name := s s end) +server.register(namespace="vars", + "station_name", + fun (s) -> begin log("vars.station_name") station_name := s s end) +server.register(namespace="vars", + "bootup_time", + fun (s) -> begin log("vars.bootup_time") time := s s end) +server.register(namespace="streams", + "connection_status", + fun (s) -> begin log("streams.connection_status") "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected},4:#{!s4_connected}" end) +server.register(namespace="vars", + "default_dj_fade", + fun (s) -> begin log("vars.default_dj_fade") default_dj_fade := float_of_string(s) s end) + +server.register(namespace="dynamic_source", + description="Enable webstream output", + usage='start', + "output_start", + fun (s) -> begin log("dynamic_source.output_start") + notify([("schedule_table_id", !current_dyn_id)]) + webstream_enabled := true "enabled" end) +server.register(namespace="dynamic_source", + description="Enable webstream output", + usage='stop', + "output_stop", + fun (s) -> begin log("dynamic_source.output_stop") webstream_enabled := false "disabled" end) + +server.register(namespace="dynamic_source", + description="Set the streams cc_schedule row id", + usage="id ", + "id", + fun (s) -> begin log("dynamic_source.id") set_dynamic_source_id(s) end) + +server.register(namespace="dynamic_source", + description="Get the streams cc_schedule row id", + usage="get_id", + "get_id", + fun (s) -> begin log("dynamic_source.get_id") get_dynamic_source_id() end) + +#server.register(namespace="dynamic_source", +# description="Start a new dynamic source.", +# usage="start ", +# "read_start", +# fun (uri) -> begin log("dynamic_source.read_start") begin_stream_read(uri) end) +#server.register(namespace="dynamic_source", +# description="Stop a dynamic source.", +# usage="stop ", +# "read_stop", +# fun (s) -> begin log("dynamic_source.read_stop") stop_stream_read(s) end) + +#server.register(namespace="dynamic_source", +# description="Stop a dynamic source.", +# usage="stop ", +# "read_stop_all", +# fun (s) -> begin log("dynamic_source.read_stop") destroy_dynamic_source_all() end) + +default = amplify(id="silence_src", 0.00001, noise()) +ref_off_air_meta = ref off_air_meta +if !ref_off_air_meta == "" then + ref_off_air_meta := "LibreTime - offline" +end +def map_off_air_meta(m) = + [("title", !ref_off_air_meta)] +end +default = map_metadata(map_off_air_meta, default) +ignore(output.dummy(default, fallible=true)) + +master_dj_enabled = ref false +live_dj_enabled = ref false +scheduled_play_enabled = ref false + +def make_master_dj_available() + master_dj_enabled := true +end + +def make_master_dj_unavailable() + master_dj_enabled := false +end + +def make_live_dj_available() + live_dj_enabled := true +end + +def make_live_dj_unavailable() + live_dj_enabled := false +end + +def make_scheduled_play_available() + scheduled_play_enabled := true + just_switched := true +end + +def make_scheduled_play_unavailable() + scheduled_play_enabled := false +end + +def update_source_status(sourcename, status) = + command = "timeout --signal=KILL 45 pyponotify --source-name=#{sourcename} --source-status=#{status} &" + system(command) + log(command) +end + +def live_dj_connect(header) = + update_source_status("live_dj", true) +end + +def live_dj_disconnect() = + update_source_status("live_dj", false) +end + +def master_dj_connect(header) = + update_source_status("master_dj", true) +end + +def master_dj_disconnect() = + update_source_status("master_dj", false) +end + +# Auth function for live stream +# @Category LiveStream +# @param user Username to check against LibreTime API +# @param password Password to check against LibreTime API +# @param ~type Type of password to check, "dj" or "master, default: "master" +def check_auth(user="", password="", ~type="master") = + log("#{type} user #{user} connected",label="#{type}_source") + + # Check auth based on return value from auth script + ret = test_process("python3 #{auth_path} --#{type} #{user} #{password}") + + if ret then + log("#{type} user #{user} authenticated",label="#{type}_source") + else + log("#{type} user #{user} auth failed",label="#{type}_source",level=2) + end + + ret +end + +# Check master source auth +# @Category LiveStream +# @param user Username to check against LibreTime API +# @param password Password to check against LibreTime API +def check_master_dj_client(user, password) = + check_auth(user, password) +end + +# Check dj/show source auth +# @Category LiveStream +# @param user Username to check against LibreTime API +# @param password Password to check against LibreTime API +def check_dj_client(user, password) = + check_auth(user, password, type="dj") +end + +s = switch(id="schedule_noise_switch", + track_sensitive=false, + transitions=[transition_default, transition], + [({!scheduled_play_enabled}, stream_queue), ({true}, default)] + ) + +s = if dj_live_stream_port != 0 and dj_live_stream_mp != "" then + dj_live = + audio_to_stereo( + input.harbor(id="live_dj_harbor", + dj_live_stream_mp, + port=dj_live_stream_port, + auth=check_dj_client, + max=40., + on_connect=live_dj_connect, + on_disconnect=live_dj_disconnect)) + + ignore(output.dummy(dj_live, fallible=true)) + + switch(id="show_schedule_noise_switch", + track_sensitive=false, + transitions=[transition, transition], + [({!live_dj_enabled}, dj_live), ({true}, s)] + ) +else + s +end + +s = if master_live_stream_port != 0 and master_live_stream_mp != "" then + master_dj = + audio_to_stereo( + input.harbor(id="master_harbor", + master_live_stream_mp, + port=master_live_stream_port, + auth=check_master_dj_client, + max=40., + on_connect=master_dj_connect, + on_disconnect=master_dj_disconnect)) + + ignore(output.dummy(master_dj, fallible=true)) + + switch(id="master_show_schedule_noise_switch", + track_sensitive=false, + transitions=[transition, transition], + [({!master_dj_enabled}, master_dj), ({true}, s)] + ) +else + s +end + + +# Attach a skip command to the source s: +#add_skip_command(s) + +server.register(namespace="streams", + description="Stop Master DJ source.", + usage="master_dj_stop", + "master_dj_stop", + fun (s) -> begin log("streams.master_dj_stop") make_master_dj_unavailable() "Done." end) +server.register(namespace="streams", + description="Start Master DJ source.", + usage="master_dj_start", + "master_dj_start", + fun (s) -> begin log("streams.master_dj_start") make_master_dj_available() "Done." end) +server.register(namespace="streams", + description="Stop Live DJ source.", + usage="live_dj_stop", + "live_dj_stop", + fun (s) -> begin log("streams.live_dj_stop") make_live_dj_unavailable() "Done." end) +server.register(namespace="streams", + description="Start Live DJ source.", + usage="live_dj_start", + "live_dj_start", + fun (s) -> begin log("streams.live_dj_start") make_live_dj_available() "Done." end) +server.register(namespace="streams", + description="Stop Scheduled Play source.", + usage="scheduled_play_stop", + "scheduled_play_stop", + fun (s) -> begin log("streams.scheduled_play_stop") make_scheduled_play_unavailable() "Done." end) +server.register(namespace="streams", + description="Start Scheduled Play source.", + usage="scheduled_play_start", + "scheduled_play_start", + fun (s) -> begin log("streams.scheduled_play_start") make_scheduled_play_available() "Done." end) + +if output_sound_device then + success = ref false + + log(output_sound_device_type) + + %ifdef output.alsa + if output_sound_device_type == "ALSA" then + ignore(output.alsa(s)) + success := true + end + %endif + + %ifdef output.ao + if output_sound_device_type == "AO" then + ignore(output.ao(s)) + success := true + end + %endif + + %ifdef output.oss + if output_sound_device_type == "OSS" then + ignore(output.oss(s)) + success := true + end + %endif + + %ifdef output.portaudio + if output_sound_device_type == "Portaudio" then + ignore(output.portaudio(s)) + success := true + end + %endif + + %ifdef output.pulseaudio + if output_sound_device_type == "Pulseaudio" then + ignore(output.pulseaudio(s)) + success := true + end + %endif + + if (!success == false) then + ignore(output.prefered(s)) + end + +end + +if s1_enable == true then + if s1_output == 'shoutcast' then + s1_namespace := "shoutcast_stream_1" + else + s1_namespace := s1_mount + end + server.register(namespace=!s1_namespace, "connected", fun (s) -> begin log("#{!s1_namespace}.connected") !s1_connected end) + output_to(s1_output, s1_type, s1_bitrate, s1_host, s1_port, s1_pass, + s1_mount, s1_url, s1_description, s1_genre, s1_user, s, "1", + s1_connected, s1_name, s1_channels) +end + +if s2_enable == true then + if s2_output == 'shoutcast' then + s2_namespace := "shoutcast_stream_2" + else + s2_namespace := s2_mount + end + server.register(namespace=!s2_namespace, "connected", fun (s) -> begin log("#{!s2_namespace}.connected") !s2_connected end) + output_to(s2_output, s2_type, s2_bitrate, s2_host, s2_port, s2_pass, + s2_mount, s2_url, s2_description, s2_genre, s2_user, s, "2", + s2_connected, s2_name, s2_channels) + +end + +if s3_enable == true then + if s3_output == 'shoutcast' then + s3_namespace := "shoutcast_stream_3" + else + s3_namespace := s3_mount + end + server.register(namespace=!s3_namespace, "connected", fun (s) -> begin log("#{!s3_namespace}.connected") !s3_connected end) + output_to(s3_output, s3_type, s3_bitrate, s3_host, s3_port, s3_pass, + s3_mount, s3_url, s3_description, s3_genre, s3_user, s, "3", + s3_connected, s3_name, s3_channels) +end + +s4_namespace = ref '' +if s4_enable == true then + log("Stream 4 Enabled") + if s4_output == 'shoutcast' then + s4_namespace := "shoutcast_stream_4" + else + s4_namespace := s4_mount + end + server.register(namespace=!s4_namespace, "connected", fun (s) -> begin log("#{!s4_namespace}.connected") !s4_connected end) + output_to(s4_output, s4_type, s4_bitrate, s4_host, s4_port, s4_pass, + s4_mount, s4_url, s4_name, s4_genre, s4_user, s, "4", + s4_connected, s4_description, s4_channels) +end + + +command = "timeout --signal=KILL 45 pyponotify --liquidsoap-started &" +log(command) +system(command) diff --git a/python_apps/pypo/liquidsoap/1.4/mp3.liq b/python_apps/pypo/liquidsoap/1.4/mp3.liq new file mode 100644 index 000000000..d403f54d1 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.4/mp3.liq @@ -0,0 +1,68 @@ + if bitrate == 24 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 24, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 24, stereo = false), mean(!source))) + end + elsif bitrate == 32 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 32, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 32, stereo = false), mean(!source))) + end + elsif bitrate == 48 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 48, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 48, stereo = false), mean(!source))) + end + elsif bitrate == 64 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 64, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 64, stereo = false), mean(!source))) + end + elsif bitrate == 96 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 96, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 96, stereo = false), mean(!source))) + end + elsif bitrate == 128 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 128, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 128, stereo = false), mean(!source))) + end + elsif bitrate == 160 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 160, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 160, stereo = false), mean(!source))) + end + elsif bitrate == 192 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 192, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 192, stereo = false), mean(!source))) + end + elsif bitrate == 224 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 224, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 224, stereo = false), mean(!source))) + end + elsif bitrate == 256 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 256, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 256, stereo = false), mean(!source))) + end + elsif bitrate == 320 then + if stereo then + ignore(output_stereo(%mp3(bitrate = 320, stereo = true), !source)) + else + ignore(output_mono(%mp3(bitrate = 320, stereo = false), mean(!source))) + end + end + diff --git a/python_apps/pypo/liquidsoap/1.4/ogg.liq b/python_apps/pypo/liquidsoap/1.4/ogg.liq new file mode 100644 index 000000000..178bdf531 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.4/ogg.liq @@ -0,0 +1,60 @@ + if not icecast_vorbis_metadata then + source := add(normalize=false, [amplify(0.00001, noise()), !source]) + end + + if bitrate == 24 or bitrate == 32 or bitrate == 48 then + if stereo then + ignore(output_stereo(%vorbis(quality=-0.1, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=-0.1, channels = 1), mean(!source))) + end + elsif bitrate == 64 then + if stereo then + ignore(output_stereo(%vorbis(quality=0, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0, channels = 1), mean(!source))) + end + elsif bitrate == 96 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.2, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.2, channels = 1), mean(!source))) + end + elsif bitrate == 128 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.4, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.4, channels = 1), mean(!source))) + end + elsif bitrate == 160 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.5, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.5, channels = 1), mean(!source))) + end + elsif bitrate == 192 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.6, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.6, channels = 1), mean(!source))) + end + elsif bitrate == 224 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.7, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.7, channels = 1), mean(!source))) + end + elsif bitrate == 256 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.8, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.8, channels = 1), mean(!source))) + end + elsif bitrate == 320 then + if stereo then + ignore(output_stereo(%vorbis(quality=0.9, channels = 2), !source)) + else + ignore(output_mono(%vorbis(quality=0.9, channels = 1), mean(!source))) + end + end + diff --git a/python_apps/pypo/liquidsoap/1.4/opus.liq b/python_apps/pypo/liquidsoap/1.4/opus.liq new file mode 100644 index 000000000..3ad6f6c55 --- /dev/null +++ b/python_apps/pypo/liquidsoap/1.4/opus.liq @@ -0,0 +1,68 @@ + if bitrate == 24 then + if stereo then + ignore(output_stereo(%opus(bitrate = 24, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 24, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 32 then + if stereo then + ignore(output_stereo(%opus(bitrate = 32, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 32, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 48 then + if stereo then + ignore(output_stereo(%opus(bitrate = 48, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 48, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 64 then + if stereo then + ignore(output_stereo(%opus(bitrate = 64, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 64, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 96 then + if stereo then + ignore(output_stereo(%opus(bitrate = 96, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 96, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 128 then + if stereo then + ignore(output_stereo(%opus(bitrate = 128, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 128, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 160 then + if stereo then + ignore(output_stereo(%opus(bitrate = 160, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 160, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 192 then + if stereo then + ignore(output_stereo(%opus(bitrate = 192, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 192, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 224 then + if stereo then + ignore(output_stereo(%opus(bitrate = 224, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 224, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 256 then + if stereo then + ignore(output_stereo(%opus(bitrate = 256, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 256, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + elsif bitrate == 320 then + if stereo then + ignore(output_stereo(%opus(bitrate = 320, channels = 2, signal="music", application="audio", complexity=10, vbr="constrained"), !source)) + else + ignore(output_mono(%opus(bitrate = 320, channels = 1, signal="music", application="audio", complexity=10, vbr="constrained"), mean(!source))) + end + end + diff --git a/python_apps/pypo/liquidsoap/__main__.py b/python_apps/pypo/liquidsoap/__main__.py index 1377af109..af8145a79 100644 --- a/python_apps/pypo/liquidsoap/__main__.py +++ b/python_apps/pypo/liquidsoap/__main__.py @@ -7,10 +7,11 @@ import logging import subprocess from pypo import pure -PYPO_HOME = '/var/tmp/airtime/pypo/' +PYPO_HOME = "/var/tmp/airtime/pypo/" + def run(): - '''Entry-point for this application''' + """Entry-point for this application""" print("Airtime Liquidsoap") parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", help="run in debug mode", action="store_true") @@ -19,18 +20,29 @@ def run(): os.environ["HOME"] = PYPO_HOME if args.debug: - logging.basicConfig(level=getattr(logging, 'DEBUG', None)) + logging.basicConfig(level=getattr(logging, "DEBUG", None)) generate_liquidsoap_cfg.run() - ''' check liquidsoap version if less than 1.3 use legacy liquidsoap script ''' - liquidsoap_version = subprocess.check_output("liquidsoap --version", shell=True, universal_newlines=True) - if "1.1.1" not in liquidsoap_version: - script_path = os.path.join(os.path.dirname(__file__), 'ls_script.liq') - else: - script_path = os.path.join(os.path.dirname(__file__), 'ls_script_legacy.liq') + """ check liquidsoap version so we can run a scripts matching the liquidsoap minor version """ + liquidsoap_version = subprocess.check_output( + "liquidsoap --force-start 'print(liquidsoap.version) shutdown()'", + shell=True, + universal_newlines=True, + )[0:3] + script_path = os.path.join( + os.path.dirname(__file__), liquidsoap_version, "ls_script.liq" + ) + exec_args = [ + "/usr/bin/liquidsoap", + "airtime-liquidsoap", + script_path, + "--verbose", + "-f", + ] if args.debug: - os.execl('/usr/bin/liquidsoap', 'airtime-liquidsoap', script_path, '--verbose', '-f', '--debug') - else: - os.execl('/usr/bin/liquidsoap', 'airtime-liquidsoap', script_path, '--verbose', '-f') + print(f"Liquidsoap {liquidsoap_version} using script: {script_path}") + exec_args.append("--debug") + os.execl(*exec_args) + run() diff --git a/python_apps/pypo/setup.py b/python_apps/pypo/setup.py index a13073611..75d0e4e94 100644 --- a/python_apps/pypo/setup.py +++ b/python_apps/pypo/setup.py @@ -40,7 +40,7 @@ setup(name='airtime-playout', license='AGPLv3', packages=['pypo', 'pypo.media', 'pypo.media.update', 'liquidsoap'], - package_data={'': ['*.liq', '*.cfg', '*.types']}, + package_data={'': ['**/*.liq', '*.cfg', '*.types']}, scripts=[ 'bin/airtime-playout', 'bin/airtime-liquidsoap',