source: trunk/as3/com/jeroenwijering/player/Controller.as @ 168

Revision 168, 13.0 KB checked in by jeroen, 4 years ago (diff)

added autostarter

  • Property svn:executable set to *
Line 
1/**
2* Process all input from the views and modifies the model.
3**/
4package com.jeroenwijering.player {
5
6
7import com.jeroenwijering.events.*;
8import com.jeroenwijering.parsers.*;
9import com.jeroenwijering.utils.Configger;
10import com.jeroenwijering.utils.Randomizer;
11import com.jeroenwijering.utils.Strings;
12
13import flash.display.MovieClip;
14import flash.events.*;
15import flash.geom.*;
16import flash.net.*;
17import flash.system.Capabilities;
18
19
20public class Controller extends EventDispatcher {
21
22
23        /** Configuration object **/
24        private var config:Object;
25        /** Reference to the skin (for stage access). **/
26        private var skin:MovieClip;
27        /** Reference to the SPLoader (for layouting plugins). **/
28        private var sploader:SPLoader;
29        /** Playlist of the player. **/
30        public var playlist:Array;
31        /** Reference to the player's model. **/
32        private var model:Model;
33        /** Reference to the player's view. **/
34        private var view:View;
35        /** Object that manages loading of XML playlists. **/
36        private var loader:URLLoader;
37        /** object that provides randomization. **/
38        private var randomizer:Randomizer;
39        /** File extensions of all supported mediatypes. **/
40        private var EXTENSIONS:Object = {
41                '3g2':'video',
42                '3gp':'video',
43                'aac':'video',
44                'f4b':'video',
45                'f4p':'video',
46                'f4v':'video',
47                'flv':'video',
48                'gif':'image',
49                'jpg':'image',
50                'm4a':'video',
51                'm4v':'video',
52                'mov':'video',
53                'mp3':'sound',
54                'mp4':'video',
55                'png':'image',
56                'rbs':'sound',
57                'sdp':'video',
58                'swf':'image',
59                'vp6':'video'
60        };
61        /** Elements of config that are part of the playlist. **/
62        private var ELEMENTS:Object = {
63                author:undefined,
64                date:undefined,
65                description:undefined,
66                duration:0,
67                file:undefined,
68                image:undefined,
69                link:undefined,
70                start:0,
71                streamer:undefined,
72                tags:undefined,
73                title:undefined,
74                type:undefined
75        };
76
77
78        /** Constructor, set up stage and playlist listeners. **/
79        public function Controller(cfg:Object,skn:MovieClip,ldr:SPLoader):void {
80                config = cfg;
81                skin = skn;
82                sploader = ldr;
83                loader = new URLLoader();
84                loader.addEventListener(Event.COMPLETE,loaderHandler);
85                loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,errorHandler);
86                loader.addEventListener(IOErrorEvent.IO_ERROR,errorHandler);
87        };
88
89
90        /** Register view and model with controller, start loading playlist. **/
91        public function closeMVC(mdl:Model,vie:View):void {
92                model= mdl;
93                model.addEventListener(ModelEvent.META,metaHandler);
94                model.addEventListener(ModelEvent.STATE,stateHandler);
95                view = vie;
96                view.addEventListener(ViewEvent.FULLSCREEN,fullscreenHandler);
97                view.addEventListener(ViewEvent.ITEM,itemHandler);
98                view.addEventListener(ViewEvent.LINK,linkHandler);
99                view.addEventListener(ViewEvent.LOAD,loadHandler);
100                view.addEventListener(ViewEvent.MUTE,muteHandler);
101                view.addEventListener(ViewEvent.NEXT,nextHandler);
102                view.addEventListener(ViewEvent.PLAY,playHandler);
103                view.addEventListener(ViewEvent.PREV,prevHandler);
104                view.addEventListener(ViewEvent.REDRAW,redrawHandler);
105                view.addEventListener(ViewEvent.SEEK,seekHandler);
106                view.addEventListener(ViewEvent.STOP,stopHandler);
107                view.addEventListener(ViewEvent.VOLUME,volumeHandler);
108        };
109
110
111        /** Catch errors dispatched by the playlister. **/
112        private function errorHandler(evt:ErrorEvent):void {
113                dispatchEvent(new ControllerEvent(ControllerEvent.ERROR,{message:evt.text}));
114        };
115
116
117        /** Switch fullscreen state. **/
118        private function fullscreenHandler(evt:ViewEvent):void {
119                if(skin.stage['displayState'] == 'fullScreen') {
120                        skin.stage['displayState'] = 'normal';
121                } else {
122                        try { fullscreenrect(); } catch (err:Error) {}
123                        skin.stage['displayState'] = 'fullScreen';
124                }
125        };
126
127
128        /** Set the fullscreen rectangle **/
129        private function fullscreenrect():void {
130                var srx:Number = Capabilities.screenResolutionX;
131                var asr:Number = srx/Capabilities.screenResolutionY;
132                var pnt:Point = skin.parent.localToGlobal(new Point(skin.x,skin.y));
133                try {
134                        var wid:Number = playlist[config['item']]['width'];
135                } catch (err:Error) {}
136                if(wid && wid > srx) {
137                        skin.stage["fullScreenSourceRect"] = new Rectangle(pnt.x,pnt.y,srx,Capabilities.screenResolutionY);
138                } else if(wid && wid > srx/2) {
139                        skin.stage["fullScreenSourceRect"] = new Rectangle(pnt.x,pnt.y,wid,Math.round(wid/asr));
140                } else {
141                        skin.stage["fullScreenSourceRect"] = new Rectangle(pnt.x,pnt.y,srx/2,Capabilities.screenResolutionY/2);
142                }
143        };
144
145
146        /** Return the type of specific playlistitem (the Model to play it with) **/
147        private function getModelType(itm:Object,sub:Boolean):String {
148                if(!itm['file']) {
149                        return null;
150                } else if(itm['type']) {
151                        return itm['type'];
152                } else if (itm['streamer'] && sub) {
153                        if(itm['streamer'].substr(0,4) == 'rtmp') {
154                                return 'rtmp';
155                        } else if(itm['streamer'].substr(0,4) == 'http') {
156                                return 'http';
157                        } else {
158                                return itm['streamer'];
159                        }
160                // this is a small hack to enable youtube playlists to work by default.
161                } else if(itm['file'].indexOf('youtube.com/w') > -1 || itm['file'].indexOf('youtube.com/v') > -1) {
162                        return 'youtube';
163                } else {
164                         return EXTENSIONS[itm['file'].substr(-3).toLowerCase()];
165                }
166        };
167
168
169        /** Jump to a userdefined item in the playlist. **/
170        private function itemHandler(evt:ViewEvent):void {
171                var itm:Number = evt.data.index;
172                if (itm < 0) {
173                        playItem(0);
174                } else if (itm > playlist.length-1) {
175                        playItem(playlist.length-1);
176                } else if (!isNaN(itm)) {
177                        playItem(itm);
178                }
179        };
180
181
182        /** Jump to the link of a playlistitem. **/
183        private function linkHandler(evt:ViewEvent):void {
184                var itm:Number = config['item'];
185                if(evt.data.index) { itm = evt.data.index; }
186                var lnk:String = playlist[itm]['link'];
187                if(lnk != null) {
188                        navigateToURL(new URLRequest(lnk),config['linktarget']);
189                }
190                playHandler(new ViewEvent(ViewEvent.PLAY,{state:false}));
191        };
192
193
194        /** Load a new playlist. **/
195        private function loadHandler(evt:ViewEvent):void {
196                if(config['state'] != 'IDLE') {
197                        stopHandler();
198                }
199                var obj:Object = new Object();
200                if(typeof(evt.data.object) == 'string') {
201                        obj['file'] = evt.data.object;
202                } else {
203                        for(var itm:String in ELEMENTS) {
204                                obj[itm] = Strings.serialize(evt.data.object[itm]);
205                        }
206                }
207                if(obj['file']) {
208                        if(getModelType(obj,false) == null) {
209                                loader.load(new URLRequest(obj['file']));
210                                return;
211                        } else {
212                                playlistHandler(new Array(obj));
213                        }
214                } else {
215                        var arr:Array = new Array();
216                        for each(var val:Object in obj) {
217                                arr.push(val);
218                        }
219                        playlistHandler(arr);
220                }
221        };
222
223
224        /** Translate the XML object to the feed array. **/
225        private function loaderHandler(evt:Event):void {
226                try {
227                        var dat:XML = XML(evt.target.data);
228                        var fmt:String = dat.localName().toLowerCase();
229                } catch (err:Error) {
230                        dispatchEvent(new ControllerEvent(ControllerEvent.ERROR,{message:'This playlist is not a valid XML file.'}));
231                        return;
232                }
233                switch (fmt) {
234                        case 'rss':
235                                playlistHandler(RSSParser.parse(dat));
236                                break;
237                        case 'playlist':
238                                playlistHandler(XSPFParser.parse(dat));
239                                break;
240                        case 'asx':
241                                playlistHandler(ASXParser.parse(dat));
242                                break;
243                        case 'smil':
244                                playlistHandler(SMILParser.parse(dat));
245                                break;
246                        case 'feed':
247                                playlistHandler(ATOMParser.parse(dat));
248                                break;
249                        default:
250                                dispatchEvent(new ControllerEvent(ControllerEvent.ERROR,{message:'Unknown playlist format: '+fmt}));
251                                return;
252                }
253        };
254
255
256        /** Update playlist item duration. **/
257        private function metaHandler(evt:ModelEvent):void {
258                if(evt.data.duration && playlist[config['item']]['duration'] == 0) {
259                        playlist[config['item']]['duration'] = evt.data.duration;
260                }
261                if(evt.data.width) {
262                        playlist[config['item']]['width'] = evt.data.width;
263                        playlist[config['item']]['height'] = evt.data.height;
264                }
265        };
266
267
268        /** Save new state of the mute switch and send volume. **/
269        private function muteHandler(evt:ViewEvent):void {
270                if(evt.data.state) {
271                        if(evt.data.state == config['mute']) {
272                                return;
273                        } else {
274                                config['mute'] = evt.data.state;
275                        }
276                } else {
277                        config['mute'] = !config['mute'];
278                }
279                Configger.saveCookie('mute',config['mute']);
280                dispatchEvent(new ControllerEvent(ControllerEvent.MUTE,{state:config['mute']}));
281        };
282
283
284        /** Jump to the next item in the playlist. **/
285        private function nextHandler(evt:ViewEvent=null):void {
286                if(playlist && config['shuffle'] == true) {
287                        playItem(randomizer.pick());
288                } else if (playlist && config['item'] == playlist.length-1) {
289                        playItem(0);
290                } else if (playlist) {
291                        playItem(config['item']+1);
292                }
293        };
294
295
296        /** Change the playback state. **/
297        private function playHandler(evt:ViewEvent):void {
298                if(playlist) {
299                        if(evt.data.state != false && config['state'] == ModelStates.PAUSED) {
300                                dispatchEvent(new ControllerEvent(ControllerEvent.PLAY,{state:true}));
301                        } else if (evt.data.state != false && config['state'] == ModelStates.COMPLETED) {
302                                dispatchEvent(new ControllerEvent(ControllerEvent.SEEK,{position:0}));
303                        } else if(evt.data.state != false && config['state'] == ModelStates.IDLE) {
304                                playItem();
305                        } else if (evt.data.state != true &&
306                                (config['state'] == ModelStates.PLAYING || config['state'] == ModelStates.BUFFERING)) {
307                                dispatchEvent(new ControllerEvent(ControllerEvent.PLAY,{state:false}));
308                        }
309                }
310        };
311
312
313        /** Direct the model to play a new item. **/
314        private function playItem(nbr:Number=undefined):void {
315                if(!isNaN(nbr)) {
316                        config['item'] = nbr;
317                }
318                dispatchEvent(new ControllerEvent(ControllerEvent.ITEM,{index:config['item']}));
319        };
320
321
322        /** Check new playlist for playeable files and setup randomizing/autostart. **/
323        private function playlistHandler(ply:Array):void {
324                for(var i:Number = ply.length-1; i > -1; i--) {
325                        if(!ply[i]['duration']) { ply[i]['duration'] = 0; }
326                        if(!ply[i]['start']) { ply[i]['start'] = 0; }
327                        if(!ply[i]['streamer']) { ply[i]['streamer'] = config['streamer']; }
328                        if(config['replace']) {
329                                var arr:Array = config['replace'].split('|');
330                                ply[i]['file'] = ply[i]['file'].replace(RegExp(arr[0]),arr[1]);
331                        }
332                        ply[i]['type'] = getModelType(ply[i],true);
333                        if(!ply[i]['type']) {ply.splice(i,1);}
334                }
335                if(ply.length > 0) {
336                        playlist = ply;
337                } else {
338                        dispatchEvent(new ControllerEvent(ControllerEvent.ERROR,{message:'No valid filetypes found in this playlist'}));
339                        return;
340                }
341                if(config['shuffle'] == true) {
342                        randomizer = new Randomizer(playlist.length);
343                        config['item'] = randomizer.pick();
344                } else if (config['item'] > playlist.length) {
345                        config['item'] = playlist.length-1;
346                }
347                dispatchEvent(new ControllerEvent(ControllerEvent.PLAYLIST,{playlist:playlist}));
348                if(config['autostart'] == true) {
349                        playItem();
350                }
351        };
352
353
354        /** Jump to the previous item in the playlist. **/
355        private function prevHandler(evt:ViewEvent):void {
356                if (config['item'] == 0) {
357                        playItem(playlist.length-1);
358                } else {
359                        playItem(config['item']-1);
360                }
361        };
362
363
364        /** Manage a resizing of the stage. **/
365        private function redrawHandler(evt:ViewEvent=null):void {
366                try {
367                        var dps:String = skin.stage['displayState'];
368                } catch (err:Error) {}
369                if(dps == 'fullScreen' && config['resizing']) {
370                        config['fullscreen'] = true;
371                        sploader.layoutFullscreen();
372                } else {
373                        if(config['resizing']) {
374                                config['width'] = skin.stage.stageWidth;
375                                config['height'] = skin.stage.stageHeight;
376                        }
377                        config['fullscreen'] = false;
378                        sploader.layoutNormal();
379                }
380                dispatchEvent(new ControllerEvent(ControllerEvent.RESIZE,{
381                        fullscreen:config['fullscreen'],width:config['width'],height:config['height']}));
382        };
383
384
385        /** Seek to a specific part in a mediafile. **/
386        private function seekHandler(evt:ViewEvent):void {
387                if(config['state'] != ModelStates.IDLE && playlist[config['item']]['duration'] > 0) {
388                        var pos:Number = evt.data.position;
389                        if(pos < 1) {
390                                pos = 0;
391                        } else if (pos > playlist[config['item']]['duration']-1) {
392                                pos = playlist[config['item']]['duration']-1;
393                        }
394                        dispatchEvent(new ControllerEvent(ControllerEvent.SEEK,{position:pos}));
395                }
396        };
397
398
399        /** Stop all playback and buffering. **/
400        private function stopHandler(evt:ViewEvent=undefined):void {
401                dispatchEvent(new ControllerEvent(ControllerEvent.STOP));
402        };
403
404
405        /** Manage playback state changes. **/
406        private function stateHandler(evt:ModelEvent):void {
407                if(evt.data.newstate == ModelStates.COMPLETED) {
408                        switch (config['repeat']) {
409                                case 'single':
410                                        playHandler(new ViewEvent(ViewEvent.PLAY,{state:true}));
411                                        break;
412                                case 'always':
413                                        if(playlist.length == 1) {
414                                                playHandler(new ViewEvent(ViewEvent.PLAY,{state:true}));
415                                        } else {
416                                                nextHandler();
417                                        }
418                                        break;
419                                case 'list':
420                                        if((config['shuffle'] == true && randomizer.length > 0) ||
421                                                (config['shuffle'] == false && config['item'] < playlist.length-1)) {
422                                                nextHandler();
423                                        }
424                                        break;
425                        }
426                }
427        };
428
429
430        /** Save new state of the mute switch and send volume. **/
431        private function volumeHandler(evt:ViewEvent):void {
432                var vol:Number = evt.data.percentage;
433                if (vol < 1) {
434                        muteHandler(new ViewEvent(ViewEvent.MUTE,{state:true}));
435                } else if (!isNaN(vol) && vol < 101) {
436                        if(config['mute'] == true) {
437                                muteHandler(new ViewEvent(ViewEvent.MUTE,{state:false}));
438                        }
439                        config['volume'] = vol;
440                        Configger.saveCookie('volume',config['volume']);
441                        dispatchEvent(new ControllerEvent(ControllerEvent.VOLUME,{percentage:vol}));
442                }
443        };
444
445
446}
447
448
449}
Note: See TracBrowser for help on using the repository browser.