diff --git a/legacy/application/assets.json b/legacy/application/assets.json index ad0eaa857..05739af05 100644 --- a/legacy/application/assets.json +++ b/legacy/application/assets.json @@ -26,7 +26,7 @@ "css/media_library.css": "e1982d1f673543f7730898fb49450f8b", "css/player-form.css": "e08a4545715fc56b75c845b44a5b2a1c", "css/player.css": "904bc7aede4d5f0372468528d88094f1", - "css/playlist_builder.css": "e92ef56ddffca440a7741934edbb7f7f", + "css/playlist_builder.css": "9e35f1b7a1e79a7a73e7e9666d5a711f", "css/playouthistory.css": "983cc1bac566b18b745b6e0da9ef3c0c", "css/plupload.queue.css": "0acfb6b54c18654452727d4abf297394", "css/pro_dropdown_3.css": "9848a27dad960c2218751c1656e9206a", @@ -43,7 +43,7 @@ "css/show_analytics.css": "4393c521308277447afabe8791779bf1", "css/showbuilder.css": "4421c01b5c2dfb03f8d06dd6023b4bd7", "css/station_podcast.css": "88e9b38ead71eddc69ef50bfc8cb2d0d", - "css/styles.css": "6890a553402f44cefc7c6915f38aa657", + "css/styles.css": "29ce2292b2c007e86d192d0deeaf88ad", "css/tipsy/jquery.tipsy.css": "b13517583583f83ed7d5fc067a0c9372", "css/tracktypes.css": "94c94817a8505ff4dfcd090987859a7e", "css/users.css": "94c94817a8505ff4dfcd090987859a7e", @@ -63,7 +63,7 @@ "js/airtime/library/plupload.js": "0f6be5b133650828b9ffc74e7852dc89", "js/airtime/library/podcast.js": "4dedd84cb571cdba2401bfb8ba621e69", "js/airtime/library/publish.js": "ab3a1452dd332cdb0773241a1c17b7e0", - "js/airtime/library/spl.js": "c4cbac0c237b548064685a2cb16d3fa2", + "js/airtime/library/spl.js": "5bddd886303ff15e8b78e79b30a9e56f", "js/airtime/listenerstat/listenerstat.js": "a3733dae8f9549668125ec9852d356ed", "js/airtime/listenerstat/showlistenerstat.js": "7cf0c375420f1c8471d304bc8758b2cd", "js/airtime/login/login.js": "7278cf49618791d75bacce38dd1b1d46", @@ -180,5 +180,16 @@ "js/waveformplaylist/playout.js": "7dfc5fe760f3c6739e38499df7b61e47", "js/waveformplaylist/time_scale.js": "74e0e17e1c8cd597449220c98de408ba", "js/waveformplaylist/track.js": "5456e6081ffedf55a9e38571bc178781", - "js/waveformplaylist/track_render.js": "e371b582b23e4b618e039f096d2f0570" + "js/waveformplaylist/track_render.js": "e371b582b23e4b618e039f096d2f0570", + "js/wavesurfer/cursor.js": "8ed17a7437f3ec84972d15d0073249b2", + "js/wavesurfer/cursor.min.js": "831165862b629e615cf59112fa00d963", + "js/wavesurfer/libretime.js": "17133cacf09fc204a572b56c99d44278", + "js/wavesurfer/minimap.js": "c17dd315386006bb3cffdabb5f715c7b", + "js/wavesurfer/minimap.min.js": "90b2f2d1d1b4eb189d1a9c3c27dcb4f7", + "js/wavesurfer/regions.js": "aafe4f696d3da50c976d11e472fd56d1", + "js/wavesurfer/regions.min.js": "2ed2f8b5880beee568942000a6139e85", + "js/wavesurfer/timeline.js": "0bd70779070513c2a4f34237a0f9f573", + "js/wavesurfer/timeline.min.js": "90ea16b23cacebfad10cad42f94403d0", + "js/wavesurfer/wavesurfer.js": "9e2ced8a136449f4fd78911b0f01f6ed", + "js/wavesurfer/wavesurfer.min.js": "42ebd7fdd574dfe8cae587145751a1f2" } diff --git a/legacy/application/controllers/LibraryController.php b/legacy/application/controllers/LibraryController.php index f2311f532..dfd50f570 100644 --- a/legacy/application/controllers/LibraryController.php +++ b/legacy/application/controllers/LibraryController.php @@ -400,6 +400,12 @@ class LibraryController extends Zend_Controller_Action $this->view->artist_name = $file->getPropelOrm()->getDbArtistName(); $this->view->filePath = $file->getPropelOrm()->getDbFilepath(); $this->view->artwork = $file->getPropelOrm()->getDbArtwork(); + $this->view->replay_gain = $file->getPropelOrm()->getDbReplayGain(); + $this->view->cuein = $file->getPropelOrm()->getDbCuein(); + $this->view->cueout = $file->getPropelOrm()->getDbCueout(); + $this->view->format = $file->getPropelOrm()->getDbFormat(); + $this->view->bit_rate = $file->getPropelOrm()->getDbBitRate(); + $this->view->sample_rate = $file->getPropelOrm()->getDbSampleRate(); $this->view->html = $this->view->render('library/edit-file-md.phtml'); } diff --git a/legacy/application/controllers/plugins/PageLayoutInitPlugin.php b/legacy/application/controllers/plugins/PageLayoutInitPlugin.php index 991493aa1..76dc771e2 100644 --- a/legacy/application/controllers/plugins/PageLayoutInitPlugin.php +++ b/legacy/application/controllers/plugins/PageLayoutInitPlugin.php @@ -217,6 +217,13 @@ class PageLayoutInitPlugin extends Zend_Controller_Plugin_Abstract ->appendFile(Assets::url('js/airtime/common/common.js'), 'text/javascript') ->appendFile(Assets::url('js/airtime/common/audioplaytest.js'), 'text/javascript'); + // include wavesurfer.js for waveform display + $view->headScript()->appendFile(Assets::url('js/wavesurfer/wavesurfer.min.js'), 'text/javascript') + ->appendFile(Assets::url('js/wavesurfer/timeline.min.js'), 'text/javascript') + ->appendFile(Assets::url('js/wavesurfer/regions.min.js'), 'text/javascript') + ->appendFile(Assets::url('js/wavesurfer/cursor.min.js'), 'text/javascript') + ->appendFile(Assets::url('js/wavesurfer/libretime.js'), 'text/javascript'); + $user = Application_Model_User::getCurrentUser(); if (!is_null($user)) { $userType = $user->getType(); diff --git a/legacy/application/forms/EditAudioMD.php b/legacy/application/forms/EditAudioMD.php index fc3539ae6..2247e2d21 100644 --- a/legacy/application/forms/EditAudioMD.php +++ b/legacy/application/forms/EditAudioMD.php @@ -8,9 +8,11 @@ class Application_Form_EditAudioMD extends Zend_Form { // Set the method for the display form to POST $this->setMethod('post'); + $this->setAttrib('id', 'track_edit_' . $p_id); $file_id = new Zend_Form_Element_Hidden('file_id'); $file_id->setValue($p_id); + $file_id->setDecorators(['ViewHelper']); $file_id->addDecorator('HtmlTag', ['tag' => 'div', 'style' => 'display:none']); $file_id->removeDecorator('Label'); $file_id->setAttrib('class', 'obj_id'); @@ -188,6 +190,14 @@ class Application_Form_EditAudioMD extends Zend_Form ]); $this->addElement($mood); + // Add replay gain field + $replay_gain = new Zend_Form_Element_Hidden('replay_gain'); + $replay_gain->class = 'input_text replay_gain_' . $p_id; + $replay_gain->setLabel(_('Replay Gain:')) + ->addDecorator('HtmlTag', ['tag' => 'div', 'style' => 'display:none']) + ->removeDecorator('Label'); + $this->addElement($replay_gain); + // Add bmp field $bpm = new Zend_Form_Element_Text('bpm'); $bpm->class = 'input_text'; @@ -242,7 +252,7 @@ class Application_Form_EditAudioMD extends Zend_Form $validCuePattern = '/^(?:[0-9]{1,2}:)?(?:[0-9]{1,2}:)?[0-9]{1,6}(\.\d{1,6})?$/'; $cueIn = new Zend_Form_Element_Text('cuein'); - $cueIn->class = 'input_text'; + $cueIn->class = 'input_text cuein_' . $p_id; $cueIn->setLabel('Cue In:'); $cueInValidator = Application_Form_Helper_ValidationTypes::overrideRegexValidator( $validCuePattern, @@ -252,7 +262,7 @@ class Application_Form_EditAudioMD extends Zend_Form $this->addElement($cueIn); $cueOut = new Zend_Form_Element_Text('cueout'); - $cueOut->class = 'input_text'; + $cueOut->class = 'input_text cueout_' . $p_id; $cueOut->setLabel('Cue Out:'); $cueOutValidator = Application_Form_Helper_ValidationTypes::overrideRegexValidator( $validCuePattern, diff --git a/legacy/application/views/scripts/library/edit-file-md.phtml b/legacy/application/views/scripts/library/edit-file-md.phtml index fa26736d6..06a7ebd9f 100644 --- a/legacy/application/views/scripts/library/edit-file-md.phtml +++ b/legacy/application/views/scripts/library/edit-file-md.phtml @@ -1,39 +1,186 @@ -artwork, 256); ?> +artwork, 256); + +$baseUrl = Config::getBasePath(); + +$get_replay_gain = Application_Model_Preference::getReplayGainModifier(); +if (!Application_Model_Preference::GetEnableReplayGain() ) { + $get_replay_gain = 0; +} +$analogMeter = true; +?>
-
- permissionDenied) { ?>

- permissionDenied) { - echo(_("Viewing ")); - } else { - echo(_("Editing ")); - } */ - ?> -
-
-
-

title); ?>

-

artist_name); ?>

-
-
-
-
-
-
- - -
-
-
-
-
-
-
- Remove +
+ permissionDenied) { ?>

+ permissionDenied) { + echo(_("Viewing ")); + } else { + echo(_("Editing ")); + } */ + ?> +
+
+
+

title); ?>

+

artist_name); ?>

-
- form; ?> +
+
+
+ + +
+
+
+
+
+
+
+ Remove +
+
+
+
+ form; ?> +
+
+
+
+
+
+
+ + +
+
+
+ +
+
+ +
+ + + +
+ +
+ + + +
+

+
+
+
+
+
+
+
+ +
+
+
+ diff --git a/legacy/public/css/images/slider.png b/legacy/public/css/images/slider.png new file mode 100755 index 000000000..89bbe2556 Binary files /dev/null and b/legacy/public/css/images/slider.png differ diff --git a/legacy/public/css/playlist_builder.css b/legacy/public/css/playlist_builder.css index e074436e3..0dc07ee2e 100644 --- a/legacy/public/css/playlist_builder.css +++ b/legacy/public/css/playlist_builder.css @@ -658,7 +658,7 @@ li.spl_empty { .collapsible-header.visible .arrow-icon { transform: rotate(90deg); } -.smart-block-advanced { +.smart-block-advanced, .visual-waveform-editor { display: none; } .smart-block-form .smart-block-advanced dt { diff --git a/legacy/public/css/styles.css b/legacy/public/css/styles.css index 9120cd33b..6e97b930d 100644 --- a/legacy/public/css/styles.css +++ b/legacy/public/css/styles.css @@ -4350,3 +4350,95 @@ body.droppable .artwork-upload .artwork-preview { border: 2px dashed lightblue; z-index: 9999; } + +/* Cue editor GUI starts here */ + +.track-file-details { + font-size: 14px; + line-height: 2; +} + +.track-window { + top: 285px; + width: 480px; +} + +/* -webkit-appearance: none; +-moz-appearance: none; +-ms-appearance: none; +appearance: none; */ +textarea, +input { + outline: none; +} + +input:focus, +select:focus, +textarea:focus, +button:focus { + outline: none; +} + +.navtt { + margin-bottom: 18px; + margin-left: 0; + list-style: none; +} + +.navtt > li > a { + display: block; +} + +li { + line-height: 18px; +} + +.navtt-content.active { + display: block; +} + +.navtt-content.hide { + display: none; +} + +.btn-control-player { + padding: 5px 7px 5px 10px; + font-size: 14px; + line-height: 18px; +} + +.zoom-container { + margin-top: 15px; +} + +.track-toolbar { + padding: 0 !important; +} + +.track-timer { + border-radius: 5px; + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.2); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.2); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.2); + /*background-color: #727272;*/ + outline: 0; + border-top-color: #333333; + /*background: #246B86;*/ + + width: 65px; + /*padding: 5px 7px 5px 10px;*/ + font-size: 14px; + line-height: 18px; + background-color: #666666; +} + +.track-timer-input { + background-color: transparent; + border: none; + width: 75px; + font-size: 14px; + color: #ffffff; +} + +/* Track Edit - Cue and Gain editor GUI ends here */ diff --git a/legacy/public/js/airtime/library/spl.js b/legacy/public/js/airtime/library/spl.js index 2802b9480..2d4bda2b1 100644 --- a/legacy/public/js/airtime/library/spl.js +++ b/legacy/public/js/airtime/library/spl.js @@ -1160,8 +1160,8 @@ var AIRTIME = (function (AIRTIME) { }); newTab.wrapper.find(".edit-md-dialog").on("keyup", function (event) { - // Don't submit if the user hits enter in a textarea (description) - if ($(event.target).is("input") && event.keyCode === 13) { + // Don't submit if the user hits enter in a textarea (description) or in the waveform editor + if ($(event.target).is("form input") && event.keyCode === 13) { newTab.wrapper.find(".md-save").click(); } }); diff --git a/legacy/public/js/wavesurfer/cursor.js b/legacy/public/js/wavesurfer/cursor.js new file mode 100644 index 000000000..06e7edc85 --- /dev/null +++ b/legacy/public/js/wavesurfer/cursor.js @@ -0,0 +1,404 @@ +/*! + * wavesurfer.js cursor plugin 4.6.0 (2024-02-05) + * https://wavesurfer-js.org + * @license BSD-3-Clause + */ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define("WaveSurfer", [], factory); + else if(typeof exports === 'object') + exports["WaveSurfer"] = factory(); + else + root["WaveSurfer"] = root["WaveSurfer"] || {}, root["WaveSurfer"]["cursor"] = factory(); +})(this, () => { +return /******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./src/plugin/cursor/index.js": +/*!************************************!*\ + !*** ./src/plugin/cursor/index.js ***! + \************************************/ +/***/ ((module, exports) => { + + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +/** + * @typedef {Object} CursorPluginParams + * @property {?boolean} deferInit Set to true to stop auto init in `addPlugin()` + * @property {boolean} hideOnBlur=true Hide the cursor when the mouse leaves the + * waveform + * @property {string} width='1px' The width of the cursor + * @property {string} color='black' The color of the cursor + * @property {string} opacity='0.25' The opacity of the cursor + * @property {string} style='solid' The border style of the cursor + * @property {number} zIndex=3 The z-index of the cursor element + * @property {object} customStyle An object with custom styles which are applied + * to the cursor element + * @property {boolean} showTime=false Show the time on the cursor. + * @property {object} customShowTimeStyle An object with custom styles which are + * applied to the cursor time element. + * @property {string} followCursorY=false Use `true` to make the time on + * the cursor follow the x and the y-position of the mouse. Use `false` to make the + * it only follow the x-position of the mouse. + * @property {function} formatTimeCallback Formats the timestamp on the cursor. + */ +/** + * Displays a thin line at the position of the cursor on the waveform. + * + * @implements {PluginClass} + * @extends {Observer} + * @example + * // es6 + * import CursorPlugin from 'wavesurfer.cursor.js'; + * + * // commonjs + * var CursorPlugin = require('wavesurfer.cursor.js'); + * + * // if you are using