1. 程式人生 > >百度UEditor編輯器視訊相關bug彙總和穩定解決方案

百度UEditor編輯器視訊相關bug彙總和穩定解決方案

百度UEditor編輯器的視訊是個很頭疼的問題,從昨晚到今天折騰了一天,也看了不少帖子,很多都是隻治標不治本,而且有很多改法也是忽略本質,不過受大神啟發,自己還是琢磨出來了。

百度UEditor編輯器的視訊主要容易出現幾個問題:

1、新增視訊之後,點選檢視html原始碼,結果丟失src後面的連結;

2、視屏編輯器預覽BUG;

3、新增視屏後百度編輯器預覽BUG;

這三個問題,本文將分別講解:

一、丟失src後面的連結問題


這個問題其實網上有很多大神已經找到關鍵點了:白名單!沒錯,就是這個!

這個問題可以參考這個兩個帖子:

其實就是在ueditor.config.js的白名單whitList:裡面加上src欄位,這樣就不會被過濾了。

img:    ['src', 'alt', 'title', 'width', 'height', 'id', '_src', '_url', 'loadingclass', 'class', 'data-latex'], 
//視訊部分白名單********
video:  ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width', 'class', 'style'],  
source: ['src', 'type'],  
embed:  ['type', 'class', 'pluginspage', 'id', 'src', 'width', 'height', 'align', 'bgcolor', 'style', 'wmode', 'play',  
	'loop', 'menu', 'allowscriptaccess', 'allowfullscreen'],
iframe: ['src', 'class', 'height', 'width', 'max-width', 'max-height', 'align', 'frameborder', 'allowfullscreen']
//********
img裡面為什麼要加上'_src'和'_url',第一篇部落格裡面說是因為編輯器在切換原始碼的過程中過濾掉img的_url屬性(用來儲存視訊url)_src/plugins/video.js裡處理的是_url,而不是_src。

然而,問題來了,居然沒有用!!!!罵人

折騰了半天,最後發現只要把 whitList: 改成 whiteList:就好了。原始碼中少了一個e,一口血吐出來。想問百度UEditor的開發大神麼英語六級過了麼?


二、視屏編輯器預覽BUG

關於視訊編輯器的問題個人沒找到有解釋清楚的,折騰了半天還是自己看原始碼搞明白了,這裡讓我慢慢說。

視屏編輯器的bug在這裡:


然而在確認之後,在UEditor的編譯器上,把滑鼠放在視訊位置,彈出選單,點選了下方的“修改”按鈕


開啟之後,奇蹟出現了。。


暈死,怎麼會這樣。我找了半天,終於在視訊編譯器對應的video.js中找到問題了,就是這個方法:

/**
     * 監聽url改變事件
     * @param url
     * @modify ie9以上使用oninput屬性監聽,onpropertychange不穩定
     */
    function addUrlChangeListener(url){
        if (browser.ie) {
        	url.onpropertychange = function () {
        			createPreviewVideo( this.value );
        } else {
            url.addEventListener( "input", function () {
                createPreviewVideo( this.value );
            }, false );
        }
    }


這個方法中,url變數是視訊編譯器上輸入“視訊網址”的html Dom物件,我這裡用的IE11測試的,然後onpropertychange這個方法就出現了問題。

這個方法的目的是監聽這個html Dom物件是否發生了value變化,但是這對於ie11而言第一次開啟卻不起效果?!就比如說首次載入的時候就不行,首次載入的程式碼在這裡(我只是想向大神證明我沒胡說):


由於IE11已經能支援HTML5的規範了,所以可以直接用oninput方法,這裡把上述函式改為:

/**
     * 監聽url改變事件
     * @param url
     * @modify ie9以上使用oninput屬性監聽,onpropertychange不穩定
     */
    function addUrlChangeListener(url){
        if (browser.ie) {
        	if(browser.ie11Compat==true
        			|| browser.ie9Compat == true){
        		url.oninput = function () {
        			createPreviewVideo( this.value );
        		}
        	}else{
        		//ie9以下
        		url.onpropertychange = function () {
        			createPreviewVideo( this.value );
        		}
        	}
        } else {
            url.addEventListener( "input", function () {
                createPreviewVideo( this.value );
            }, false );
        }
    }

這樣首次開啟就也能播放視訊了。


上述browser.ie11Compat==true和browser.ie9Compat == true請參照ueditor.all.js中這部分程式碼:

browser.ie11Compat = document.documentMode == 11;
        /**
         * @property { boolean } ie9Compat 檢測瀏覽器模式是否為 IE9 相容模式
         * @warning 如果瀏覽器不是IE, 則該值為undefined
         * @example
         * ```javascript
         * if ( UE.browser.ie9Compat ) {
         *     console.log( '當前瀏覽器執行在IE9相容模式下' );
         * }
         * ```
         */
        browser.ie9Compat = document.documentMode == 9;

好,這樣解決了一般網路視訊的播放問題,但是又有個問題來了,如果連線地址不是flash格式,而是普通的mp4檔案地址問麼辦?

修改上述函式,在flash流路徑下加上

type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"

這兩條屬性,就可以相容路徑字尾為.mp4的視訊流播放了。效果如下:


可以看到,這是字尾為.MP4的視屏流,也可以預覽播放成功了。
網上有些人直接去掉了這兩條屬性,我試過這樣對於flash流也能播放成功,但是很不穩定,電腦都宕機過幾回。。騰訊給的分享連結地址裡面也是有這兩條屬性的,所以我建議對於flash流最好還是也帶著吧。
三、新增視屏後百度編輯器預覽BUG
百度編輯器預覽BUG主要是因為在編譯器中顯示成了一張圖片而不是視訊。

關於這個我參考了一位作者的做法:

http://blog.csdn.net/belen_xue/article/details/73252802
個人認為其做法有點粗暴,甚至可能導致一部分問題,具體我慢慢細說。
首先,在引入js包的時候,請不要使用ueditor.all.min.js,因為這是壓縮過的js,原始碼為ueditor.all.js,可修改。



那麼接下來就要對ueditor.all.js進行操刀了!
先找到這個函式me.commands["insertvideo"] ,作者的做法直接將引數“image”改為了“embed”,認為這是視訊就應該這樣改:



改完之後,確實能確保編譯器中能看到視訊了,而不是圖片了,但是視窗卻不能關閉了,然後作者就又把上面那個for迴圈刪了,原因是因為改完後找不到 id 這個屬性了;

接下來在creatInsertStr方法中,switch的case 'embed': 分支中,str去掉了這兩個屬性:

type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"

這樣確實能保證絕大多數視訊都能正常顯示了,但是還是同video.js中的問題一樣,穩定性不好,另外還有個非常大的問題,如果點選檢視原始碼(那個html按鈕),再返回,又會變成圖片!
怎麼辦?

我在creatInsertStr方法下方直接發現了一個方法:

發現這個方法其實是在做圖片和視訊的切換。如果切換到檢視html,或者呼叫獲取編譯器內容的getContent()方法時,會切回成embed標籤,也就是嵌入視訊標籤,切換回預覽頁面時,又會切換回圖片顯示。也就是說,按照那位作者的做法,這個方法內容也必須註釋掉,且兩個IF都要註釋。

function switchImgAndVideo(root,img2video){
        utils.each(root.getNodesByTagName(img2video ? 'img' : 'embed video'),function(node){
        	//去掉轉換
            var className = node.getAttr('class');
//            if(className && className.indexOf('edui-faked-video') != -1){
//                var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'embed':'image');
//                node.parentNode.replaceChild(UE.uNode.createElement(html),node);
//            }
//            if(className && className.indexOf('edui-upload-video') != -1){
//                var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'video':'image');
//                node.parentNode.replaceChild(UE.uNode.createElement(html),node);
//            }
        });
    }
這樣實踐後,確實不會再出現切回圖片的問題了,但是我在想,為何百度UEditor的開發人員非要把視訊轉為圖片顯示呢?


看到這個了嗎,如果按照作者的做法換成視訊的話,這個功能選單自然就沒有了。
另外按照作者的做法,如果這裡是視訊流的話,在編譯器預覽頁面上刪除視訊,而視訊如果正在播放,有些播放器可能會這樣:



視訊是被刪了,可他附在頁面上還在播放呢!

接下來我說下個人的做法:
me.commands["insertvideo"]方法,將上傳和嵌入分開處理,for迴圈不需要刪除,因為下面有辦法解決(不就是少個id嗎?我加上不就完了?)。

me.commands["insertvideo"] = {
        execCommand: function (cmd, videoObjs, type){
            videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs];
            var html = [],id = 'tmpVedio', cl;
            for(var i=0,vi,len = videoObjs.length;i<len;i++){
                vi = videoObjs[i];
//                cl = (type == 'upload' ? 'edui-upload-video video-js vjs-default-skin':'edui-faked-video');
//                html.push(creatInsertStr( vi.url, vi.width || 420,  vi.height || 280, id + i, null, cl, 'image'));
                if(type == 'upload'){
                	cl = 'edui-upload-video video-js vjs-default-skin';
                	html.push(creatInsertStr( vi.url, vi.width || 420,  vi.height || 280, id + i, null, cl, 'video'));	
                }else{
                	cl = 'edui-faked-video';
                	html.push(creatInsertStr( vi.url, vi.width || 420,  vi.height || 280, id + i, null, cl, 'embed'));
                }
            }
            me.execCommand("inserthtml",html.join(""),true);
            var rng = this.selection.getRange();
            for(var i= 0,len=videoObjs.length;i<len;i++){
                var img = this.document.getElementById('tmpVedio'+i);
                domUtils.removeAttributes(img,'id');
                rng.selectNode(img).select();
                me.execCommand('imagefloat',videoObjs[i].align);
            }
        },
然後在creatInsertStr方法中,修改embed的處理方式:
case 'embed':
 	if(utils.html(url) && utils.html(url).indexOf('.swf') != -1){
            str = '<embed type="application/x-shockwave-flash" class="' + classname + '" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
                (id ? 'id="' + id+'"' : '') +' src="' +  utils.html(url) + '" width="' + width  + '" height="' + height  + '"'  + (align ? ' style="float:' + align + '"': '') +
                ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
        }else{
            //非flash流採用流播放模式,同上傳視訊一樣
            var ext = url.substr(url.lastIndexOf('.') + 1);
            if(ext == 'ogv') ext = 'ogg';
            str = '<video' + (id ? ' id="' + id + '"' : '') + ' class="' + classname + ' video-js" ' + (align ? ' style="float:' + align + '"': '') +
                ' controls autoplay="autoplay" width="' + width + '" height="' + height + '" src="' + url + '" data-setup="{}">' +
                '<source src="' + url + '" type="video/' + ext + '" /></video>';
        }
  	break;


這裡判斷一下,如果不是flash流的話就採用上傳檔案的方式去顯示內容,也就是用video標籤這種方式,這樣就不用會出現刪除視訊後還附在頁面上播放的情況(之前那種是和播放器有關係);

另外,如果是flash流的話,也就是embed標籤,我這裡將id屬性新增上了,然後再embed標籤的白名單裡面配置上id屬性就行了這樣就不需要刪除前面那個函式的for迴圈了。如果你是按我上面的那個白名單配置的話,就已經配好咯大笑

最後就是註釋掉轉換函式的內容:

function switchImgAndVideo(root,img2video){
        utils.each(root.getNodesByTagName(img2video ? 'img' : 'embed video'),function(node){
        	//去掉轉換
            var className = node.getAttr('class');
//            if(className && className.indexOf('edui-faked-video') != -1){
//                var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'embed':'image');
//                node.parentNode.replaceChild(UE.uNode.createElement(html),node);
//            }
//            if(className && className.indexOf('edui-upload-video') != -1){
//                var html = creatInsertStr( img2video ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',className,img2video ? 'video':'image');
//                node.parentNode.replaceChild(UE.uNode.createElement(html),node);
//            }
        });
    }

這種做法雖然失去了那些小選單的功能,但是測試之後,整體也是非常穩定的。


這樣刪除視訊後不會出現附在上面繼續播放的問題。

PS:很多屬性,比如:edui-faked-video、edui-upload-video這些,其實都是開發者早就設定好為了實現某些功能的,所以隨意的更改他們肯定會意味著不穩定或者功能確實等各式各樣的問題。我今天研究了一天,發現其實這兩個屬性的目的就是為了做圖片和視訊轉換,然後在預覽介面,圖片的話就可以使用那些選單功能。所以看,其實這些問題,百度UEditor的開發者們早就想到了,而我們非要畫蛇添足的去改,是不是有些“捨本逐末”的味道呢?

以前做專案遇到這種情況往往也總是不擇手段,能解決問題就好,可現在卻越來越重視程式的魯棒性。其實很多開源的工具,別人能放出來給大家用,自然是穩定的,若是需求不滿足,我們要改也不能改得太暴力,若不符合開發者的初衷,自然改出來的功能也容易出問題。

今天一晚上加班寫的文件,可能難免有些寫的不正確或者理解不到位的地方,也忘各位大神指正!