├── src ├── extensions │ └── info.txt ├── assets │ ├── home │ │ ├── album.png │ │ ├── artist.png │ │ ├── back.png │ │ ├── home.png │ │ ├── market.png │ │ ├── radio.png │ │ ├── song.png │ │ ├── video.png │ │ └── settings.png │ ├── images │ │ ├── info.png │ │ ├── music.png │ │ └── nocover.png │ ├── player │ │ ├── next.png │ │ ├── open.png │ │ ├── play.png │ │ ├── save.png │ │ ├── clear.png │ │ ├── pause.png │ │ ├── repeat.png │ │ ├── volume.png │ │ ├── previous.png │ │ ├── shuffle.png │ │ ├── playerfull.png │ │ └── playernormal.png │ ├── icons │ │ ├── lastfm.png │ │ └── facebook.png │ └── logo │ │ ├── logo-114.png │ │ ├── logo-128.png │ │ ├── logo-16.png │ │ ├── logo-256.png │ │ ├── logo-32.png │ │ ├── logo-36.png │ │ ├── logo-48.png │ │ ├── logo-480.png │ │ ├── logo-512.png │ │ ├── logo-64.png │ │ └── logo-72.png ├── plugins │ ├── ZMusicPlugin.swf │ ├── SoundcloudPlugin.swf │ └── VkontakteNLPlugin.swf ├── mielophone │ ├── extensions │ │ ├── IMUIExtension.as │ │ └── ExtensionsManager.as │ └── ui │ │ ├── home │ │ ├── AlbumButton.mxml │ │ ├── ArtistButton.mxml │ │ ├── VideoButton.mxml │ │ ├── SettingsButton.mxml │ │ ├── SongButton.mxml │ │ ├── LastFMButton.mxml │ │ ├── RadioStreamButton.mxml │ │ ├── MarketButton.mxml │ │ ├── OtherButton.mxml │ │ └── SupportButton.mxml │ │ ├── views │ │ ├── search │ │ │ ├── TagSearchView.mxml │ │ │ ├── MoodSearchView.mxml │ │ │ ├── AlbumSearchView.mxml │ │ │ ├── ArtistSearchView.mxml │ │ │ ├── SongSearchView.mxml │ │ │ ├── VideoSearchView.mxml │ │ │ └── SearchResultsView.mxml │ │ ├── general │ │ │ ├── MarketView.mxml │ │ │ ├── HomeView.mxml │ │ │ └── SettingsView.mxml │ │ └── info │ │ │ ├── AlbumInfoView.mxml │ │ │ ├── RadioView.mxml │ │ │ └── ArtistInfoView.mxml │ │ ├── itemrenderers │ │ ├── RadioItem.mxml │ │ ├── MoodItem.mxml │ │ ├── TagItem.mxml │ │ ├── PluginItem.mxml │ │ ├── SongItem.mxml │ │ ├── FullSongItem.mxml │ │ ├── VideoItem.mxml │ │ ├── ArtistItem.mxml │ │ ├── AlbumItem.mxml │ │ └── PlaylistSongItem.mxml │ │ ├── controls │ │ └── NavigationBar.mxml │ │ ├── skins │ │ └── MielophoneApplication.mxml │ │ └── player │ │ └── MusicPlayer.mxml ├── logic │ ├── Vars.as │ ├── views │ │ ├── general │ │ │ ├── SupportOverlay.as │ │ │ ├── HomeView.as │ │ │ ├── SettingsView.as │ │ │ └── MarketView.as │ │ ├── search │ │ │ ├── SearchResultView.as │ │ │ ├── MoodSearchView.as │ │ │ ├── AlbumSearchView.as │ │ │ ├── TagSearchView.as │ │ │ ├── ArtistSearchView.as │ │ │ ├── VideoSearchView.as │ │ │ └── SongSearchView.as │ │ └── info │ │ │ ├── AlbumInfoView.as │ │ │ ├── ArtistInfoView.as │ │ │ └── RadioView.as │ ├── ui │ │ ├── UIExtensions.as │ │ └── SystemTray.as │ ├── Autoupdate.as │ ├── Hotkeys.as │ ├── player │ │ ├── ProgressiveSearch.as │ │ ├── Vars.as │ │ ├── UIActions.as │ │ ├── Playlists.as │ │ ├── Events.as │ │ └── Player.as │ ├── Facebook.as │ └── ViewHandling.as ├── Mielophone.as ├── Mielophone.mxml └── Mielophone-app.xml ├── libs ├── greensock.swc └── GraphAPI_Desktop_1_8_1.swc ├── .settings └── org.eclipse.core.resources.prefs ├── README.md ├── .flexProperties ├── .project └── .actionScriptProperties /src/extensions/info.txt: -------------------------------------------------------------------------------- 1 | UI extensions put here 2 | -------------------------------------------------------------------------------- /libs/greensock.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/libs/greensock.swc -------------------------------------------------------------------------------- /src/assets/home/album.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/album.png -------------------------------------------------------------------------------- /src/assets/home/artist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/artist.png -------------------------------------------------------------------------------- /src/assets/home/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/back.png -------------------------------------------------------------------------------- /src/assets/home/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/home.png -------------------------------------------------------------------------------- /src/assets/home/market.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/market.png -------------------------------------------------------------------------------- /src/assets/home/radio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/radio.png -------------------------------------------------------------------------------- /src/assets/home/song.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/song.png -------------------------------------------------------------------------------- /src/assets/home/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/video.png -------------------------------------------------------------------------------- /src/assets/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/images/info.png -------------------------------------------------------------------------------- /src/assets/player/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/next.png -------------------------------------------------------------------------------- /src/assets/player/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/open.png -------------------------------------------------------------------------------- /src/assets/player/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/play.png -------------------------------------------------------------------------------- /src/assets/player/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/save.png -------------------------------------------------------------------------------- /src/assets/home/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/home/settings.png -------------------------------------------------------------------------------- /src/assets/icons/lastfm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/icons/lastfm.png -------------------------------------------------------------------------------- /src/assets/images/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/images/music.png -------------------------------------------------------------------------------- /src/assets/logo/logo-114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-114.png -------------------------------------------------------------------------------- /src/assets/logo/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-128.png -------------------------------------------------------------------------------- /src/assets/logo/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-16.png -------------------------------------------------------------------------------- /src/assets/logo/logo-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-256.png -------------------------------------------------------------------------------- /src/assets/logo/logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-32.png -------------------------------------------------------------------------------- /src/assets/logo/logo-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-36.png -------------------------------------------------------------------------------- /src/assets/logo/logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-48.png -------------------------------------------------------------------------------- /src/assets/logo/logo-480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-480.png -------------------------------------------------------------------------------- /src/assets/logo/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-512.png -------------------------------------------------------------------------------- /src/assets/logo/logo-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-64.png -------------------------------------------------------------------------------- /src/assets/logo/logo-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/logo/logo-72.png -------------------------------------------------------------------------------- /src/assets/player/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/clear.png -------------------------------------------------------------------------------- /src/assets/player/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/pause.png -------------------------------------------------------------------------------- /src/assets/player/repeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/repeat.png -------------------------------------------------------------------------------- /src/assets/player/volume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/volume.png -------------------------------------------------------------------------------- /src/plugins/ZMusicPlugin.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/plugins/ZMusicPlugin.swf -------------------------------------------------------------------------------- /libs/GraphAPI_Desktop_1_8_1.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/libs/GraphAPI_Desktop_1_8_1.swc -------------------------------------------------------------------------------- /src/assets/icons/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/icons/facebook.png -------------------------------------------------------------------------------- /src/assets/images/nocover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/images/nocover.png -------------------------------------------------------------------------------- /src/assets/player/previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/previous.png -------------------------------------------------------------------------------- /src/assets/player/shuffle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/shuffle.png -------------------------------------------------------------------------------- /src/assets/player/playerfull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/playerfull.png -------------------------------------------------------------------------------- /src/plugins/SoundcloudPlugin.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/plugins/SoundcloudPlugin.swf -------------------------------------------------------------------------------- /src/plugins/VkontakteNLPlugin.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/plugins/VkontakteNLPlugin.swf -------------------------------------------------------------------------------- /src/assets/player/playernormal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mielophone/Mielophone-AIR/HEAD/src/assets/player/playernormal.png -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | #Thu Sep 29 20:10:10 CEST 2011 2 | eclipse.preferences.version=1 3 | encoding/=utf-8 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mielophone (Adobe AIR version). 2 | No longer supported. 3 | 4 | We also have some guides in [Wiki](https://github.com/mielophone/Mielophone/wiki) :P -------------------------------------------------------------------------------- /.flexProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/mielophone/extensions/IMUIExtension.as: -------------------------------------------------------------------------------- 1 | package mielophone.extensions 2 | { 3 | import flash.events.IEventDispatcher; 4 | 5 | public interface IMUIExtension 6 | { 7 | // plugin details 8 | function get PLUGIN_NAME():String; 9 | function get AUTHOR_NAME():String; 10 | } 11 | } -------------------------------------------------------------------------------- /src/logic/Vars.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.models.Album; 4 | import com.codezen.mse.models.Artist; 5 | import com.codezen.mse.models.VideoObject; 6 | 7 | public var mse:MusicSearchEngine; 8 | 9 | // selected artist 10 | public var currentArtist:Artist; 11 | 12 | // selected album 13 | public var currentAlbum:Album; -------------------------------------------------------------------------------- /src/logic/views/general/SupportOverlay.as: -------------------------------------------------------------------------------- 1 | import com.greensock.TweenLite; 2 | 3 | import flash.events.Event; 4 | import flash.net.navigateToURL; 5 | 6 | import spark.components.Group; 7 | 8 | private function onClick(e:Event):void{ 9 | var grp:Group = this; 10 | TweenLite.to(this, 0.5, {alpha:0, onComplete:function():void{ 11 | grp.visible = false; 12 | }}); 13 | } -------------------------------------------------------------------------------- /src/logic/ui/UIExtensions.as: -------------------------------------------------------------------------------- 1 | import flash.events.Event; 2 | import flash.filesystem.File; 3 | 4 | import mielophone.extensions.ExtensionsManager; 5 | 6 | import mx.core.FlexGlobals; 7 | 8 | private var pluginManager:ExtensionsManager; 9 | 10 | private function initUIExtensions():void{ 11 | pluginManager = new ExtensionsManager( [File.applicationDirectory.resolvePath("extensions/").nativePath, File.applicationStorageDirectory.resolvePath("extensions/").nativePath] ); 12 | } -------------------------------------------------------------------------------- /src/mielophone/ui/home/AlbumButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/ArtistButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/VideoButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mielophone 4 | 5 | 6 | 7 | 8 | 9 | com.adobe.flexbuilder.project.flexbuilder 10 | 11 | 12 | 13 | 14 | com.adobe.flexbuilder.project.apollobuilder 15 | 16 | 17 | 18 | 19 | 20 | com.adobe.flexbuilder.project.apollonature 21 | com.adobe.flexbuilder.project.flexnature 22 | com.adobe.flexbuilder.project.actionscriptnature 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/SettingsButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/SongButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/LastFMButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/RadioStreamButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/logic/Autoupdate.as: -------------------------------------------------------------------------------- 1 | import air.update.ApplicationUpdaterUI; 2 | import air.update.events.UpdateEvent; 3 | 4 | import flash.system.Capabilities; 5 | 6 | private const GENERIC_UPDATE:String = "http://mielophone.github.com/update-generic.xml"; 7 | private const NORMAL_UPDATE:String = "http://mielophone.github.com/update.xml"; 8 | 9 | private function initAutoupdate():void{ 10 | var up:ApplicationUpdaterUI = new ApplicationUpdaterUI(); 11 | if( Capabilities.os.indexOf("Windows") != -1 || Capabilities.os.indexOf("Mac") != -1){ 12 | trace('normal up'); 13 | up.updateURL = NORMAL_UPDATE; 14 | }else{ 15 | up.updateURL = GENERIC_UPDATE; 16 | } 17 | up.isCheckForUpdateVisible = false; 18 | up.addEventListener(UpdateEvent.INITIALIZED, function():void{ 19 | up.checkNow(); 20 | }); 21 | up.initialize(); 22 | } -------------------------------------------------------------------------------- /src/mielophone/ui/home/MarketButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 22 | 23 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/logic/views/search/SearchResultView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.greensock.TweenLite; 4 | 5 | import flash.events.ErrorEvent; 6 | import flash.events.Event; 7 | 8 | import mielophone.ui.views.search.ArtistSearchView; 9 | 10 | import mx.collections.ArrayCollection; 11 | import mx.core.FlexGlobals; 12 | 13 | private var mse:MusicSearchEngine; 14 | 15 | public var query:String; 16 | 17 | public function doWork():void{ 18 | mse = FlexGlobals.topLevelApplication.mse; 19 | 20 | mse.addEventListener(Event.COMPLETE, onResults); 21 | mse.query(query); 22 | } 23 | 24 | private function onResults(e:Event):void{ 25 | mse.removeEventListener(Event.COMPLETE, onResults); 26 | 27 | artistList.dataProvider = new ArrayCollection(mse.artists.slice(0,5)); 28 | albumList.dataProvider = new ArrayCollection(mse.albums.slice(0,5)); 29 | songList.dataProvider = new ArrayCollection(mse.songs.slice(0,7)); 30 | tagsList.dataProvider = new ArrayCollection(mse.tags.slice(0,7)); 31 | 32 | this.dispatchEvent(new Event(Event.COMPLETE)); 33 | } 34 | -------------------------------------------------------------------------------- /src/Mielophone.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | 4 | import flash.events.Event; 5 | 6 | import mielophone.ui.player.MusicPlayer; 7 | import mielophone.ui.skins.MielophoneApplication; 8 | 9 | public var musicPlayer:MusicPlayer; 10 | 11 | private function onAppicationComplete():void{ 12 | musicPlayer = (this.skin as MielophoneApplication).musicPlayer; 13 | 14 | // check update 15 | initAutoupdate(); 16 | 17 | // init docking 18 | initDock(); 19 | 20 | // init ui plugins 21 | initUIExtensions(); 22 | 23 | // init hotkeys 24 | initHotkeys(); 25 | 26 | // init facebook 27 | initFacebook(); 28 | 29 | // create search engine 30 | mse = new MusicSearchEngine(); 31 | mse.addEventListener(Event.INIT, onMseInit); 32 | 33 | // init view helpers 34 | initViewHelpers(); 35 | 36 | // init player 37 | musicPlayer.initPlayer(); 38 | 39 | // init radio 40 | radioView.initRadio(); 41 | } 42 | 43 | private function onMseInit(e:Event):void{ 44 | mse.removeEventListener(Event.INIT, onMseInit); 45 | 46 | // init settings 47 | settingsView.initSettings(); 48 | 49 | // get plugins 50 | marketView.fetchPlugins(); 51 | } -------------------------------------------------------------------------------- /src/logic/Hotkeys.as: -------------------------------------------------------------------------------- 1 | 2 | import flash.events.KeyboardEvent; 3 | import flash.ui.Keyboard; 4 | 5 | import mx.core.FlexGlobals; 6 | 7 | private function initHotkeys():void{ 8 | this.nativeApplication.addEventListener(KeyboardEvent.KEY_UP, onKeyPress); 9 | } 10 | 11 | private function onKeyPress(e:KeyboardEvent):void{ 12 | // check if target is input field 13 | // TODO: make it smart way, this is stupid -_- 14 | if(e.target.visible && e.target.toString().indexOf("textDisplay") != -1) return; 15 | 16 | switch(e.keyCode){ 17 | // simple spacebar play-pause 18 | case Keyboard.SPACE: 19 | FlexGlobals.topLevelApplication.musicPlayer.togglePlayPause(); 20 | break; 21 | 22 | // Media keys 23 | case Keyboard.PAUSE: 24 | FlexGlobals.topLevelApplication.musicPlayer.pausePlayback(); 25 | break; 26 | case Keyboard.PLAY: 27 | FlexGlobals.topLevelApplication.musicPlayer.resumePlayback(); 28 | break; 29 | case Keyboard.NEXT: 30 | FlexGlobals.topLevelApplication.musicPlayer.findNextSong(); 31 | break; 32 | case Keyboard.PREVIOUS: 33 | FlexGlobals.topLevelApplication.musicPlayer.findPrevSong(); 34 | break; 35 | case Keyboard.STOP: 36 | FlexGlobals.topLevelApplication.musicPlayer.stopPlayback(); 37 | break; 38 | } 39 | } -------------------------------------------------------------------------------- /src/logic/player/ProgressiveSearch.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.models.Song; 3 | import com.codezen.mse.playr.PlayrTrack; 4 | 5 | import flash.events.Event; 6 | 7 | import mx.utils.ObjectUtil; 8 | 9 | private var prefetchNum:int; 10 | private var prefetchSong:Song; 11 | private var prefetchedNext:Boolean; 12 | 13 | private function prefetchNextSong():void{ 14 | if(nowSearching) return; 15 | if(prefetchedNext) return; 16 | 17 | if(playerShuffle){ 18 | nextRandomPos = prefetchNum = Math.round( playQueue.length * Math.random() ); 19 | }else{ 20 | prefetchNum = playPos+1; 21 | } 22 | prefetchSong = playQueue[prefetchNum] as Song; 23 | 24 | if(prefetchSong != null && prefetchSong.track == null){ 25 | trace('searching for next song link'); 26 | trace(ObjectUtil.toString(prefetchSong)); 27 | nowSearching = true; 28 | mse.addEventListener(Event.COMPLETE, onSongLinkPrefetch); 29 | mse.findMP3(prefetchSong); 30 | } 31 | } 32 | 33 | private function onSongLinkPrefetch(e:Event):void{ 34 | mse.removeEventListener(Event.COMPLETE, onSongLinkPrefetch); 35 | 36 | nowSearching = false; 37 | 38 | if( mse.mp3s.length == 0 ){ 39 | trace('nothing for next song :('); 40 | playQueue[prefetchNum].number = -100; 41 | //findNextSong(); 42 | return; 43 | } 44 | 45 | prefetchSong.track = mse.mp3s[0] as PlayrTrack; 46 | 47 | prefetchedNext = true; 48 | 49 | trace('found next song link'); 50 | trace(ObjectUtil.toString(prefetchSong.track)); 51 | } -------------------------------------------------------------------------------- /src/logic/views/search/MoodSearchView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.models.VideoObject; 4 | 5 | import flash.events.Event; 6 | import flash.events.KeyboardEvent; 7 | import flash.geom.Rectangle; 8 | import flash.media.StageWebView; 9 | import flash.ui.Keyboard; 10 | 11 | import mx.collections.ArrayCollection; 12 | import mx.core.FlexGlobals; 13 | 14 | private var mse:MusicSearchEngine; 15 | 16 | private var topMoods:ArrayCollection; 17 | 18 | public function doWork():void{ 19 | getTopMoods(); 20 | } 21 | 22 | private function getTopMoods():void{ 23 | if(mse != null && moodsList.dataProvider != null){ 24 | this.dispatchEvent(new Event(Event.COMPLETE)); 25 | return; 26 | } 27 | 28 | mse = FlexGlobals.topLevelApplication.mse; 29 | mse.addEventListener(Event.COMPLETE, onTopMoods); 30 | mse.getTopMoods(); 31 | } 32 | 33 | private function onTopMoods(e:Event):void{ 34 | mse.removeEventListener(Event.COMPLETE, onTopMoods); 35 | 36 | topMoods = new ArrayCollection(mse.moods); 37 | moodsList.dataProvider = topMoods; 38 | 39 | this.dispatchEvent(new Event(Event.COMPLETE)); 40 | } 41 | 42 | private function onSearchKeyUp(e:KeyboardEvent):void{ 43 | if(e.keyCode == Keyboard.ENTER && searchInput.text.length > 1){ 44 | moodsList.dataProvider = new ArrayCollection(mse.findMood(searchInput.text)); 45 | 46 | searchInput.text = ''; 47 | }else if(e.keyCode == Keyboard.ESCAPE && searchInput.text.length < 1){ 48 | moodsList.dataProvider = topMoods; 49 | } 50 | } -------------------------------------------------------------------------------- /src/mielophone/ui/views/search/TagSearchView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/search/MoodSearchView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/search/AlbumSearchView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/search/ArtistSearchView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/general/MarketView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/search/SongSearchView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/OtherButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 44 | 45 | 46 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/logic/views/search/AlbumSearchView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.greensock.TweenLite; 4 | 5 | import flash.events.Event; 6 | import flash.events.KeyboardEvent; 7 | import flash.events.MouseEvent; 8 | import flash.ui.Keyboard; 9 | 10 | import mx.collections.ArrayCollection; 11 | import mx.core.FlexGlobals; 12 | 13 | private var mse:MusicSearchEngine; 14 | private var topAlbums:ArrayCollection; 15 | 16 | public function doWork():void{ 17 | getTopAlbums(); 18 | } 19 | 20 | private function getTopAlbums():void{ 21 | if(mse != null && albumList.dataProvider != null){ 22 | this.dispatchEvent(new Event(Event.COMPLETE)); 23 | return; 24 | } 25 | 26 | mse = FlexGlobals.topLevelApplication.mse; 27 | mse.addEventListener(Event.COMPLETE, onChart); 28 | mse.getTopAlbums(); 29 | } 30 | 31 | private function onChart(e:Event):void{ 32 | mse.removeEventListener(Event.COMPLETE, onChart); 33 | 34 | topAlbums = new ArrayCollection(mse.albums); 35 | albumList.dataProvider = topAlbums; 36 | 37 | this.dispatchEvent(new Event(Event.COMPLETE)); 38 | } 39 | 40 | private function onSearchKeyUp(e:KeyboardEvent):void{ 41 | if(e.keyCode == Keyboard.ENTER && searchInput.text.length > 1){ 42 | mse.addEventListener(Event.COMPLETE, onSearch); 43 | mse.findAlbum(searchInput.text); 44 | 45 | FlexGlobals.topLevelApplication.loadingOn(); 46 | 47 | searchInput.text = ''; 48 | }else if(e.keyCode == Keyboard.ESCAPE && searchInput.text.length < 1){ 49 | albumList.dataProvider = topAlbums; 50 | } 51 | } 52 | 53 | private function onSearch(e:Event):void{ 54 | mse.removeEventListener(Event.COMPLETE, onSearch); 55 | 56 | FlexGlobals.topLevelApplication.loadingOff(); 57 | 58 | albumList.dataProvider = new ArrayCollection(mse.albums); 59 | } -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/RadioItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/search/VideoSearchView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/logic/views/search/TagSearchView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.models.VideoObject; 4 | 5 | import flash.events.Event; 6 | import flash.events.KeyboardEvent; 7 | import flash.geom.Rectangle; 8 | import flash.media.StageWebView; 9 | import flash.ui.Keyboard; 10 | 11 | import mx.collections.ArrayCollection; 12 | import mx.core.FlexGlobals; 13 | 14 | private var mse:MusicSearchEngine; 15 | 16 | private var topTags:ArrayCollection; 17 | 18 | public function doWork():void{ 19 | getTopTags(); 20 | } 21 | 22 | private function getTopTags():void{ 23 | if(mse != null && tagsList.dataProvider != null){ 24 | this.dispatchEvent(new Event(Event.COMPLETE)); 25 | return; 26 | } 27 | 28 | mse = FlexGlobals.topLevelApplication.mse; 29 | mse.addEventListener(Event.COMPLETE, onTopTags); 30 | mse.getTopTags(); 31 | } 32 | 33 | private function onTopTags(e:Event):void{ 34 | mse.removeEventListener(Event.COMPLETE, onTopTags); 35 | 36 | topTags = new ArrayCollection(mse.tags); 37 | tagsList.dataProvider = topTags; 38 | 39 | this.dispatchEvent(new Event(Event.COMPLETE)); 40 | } 41 | 42 | private function onSearchKeyUp(e:KeyboardEvent):void{ 43 | if(e.keyCode == Keyboard.ENTER && searchInput.text.length > 1){ 44 | mse.addEventListener(Event.COMPLETE, onSearch); 45 | mse.findTags(searchInput.text); 46 | 47 | FlexGlobals.topLevelApplication.loadingOn(); 48 | 49 | searchInput.text = ''; 50 | }else if(e.keyCode == Keyboard.ESCAPE && searchInput.text.length < 1){ 51 | tagsList.dataProvider = topTags; 52 | } 53 | } 54 | 55 | private function onSearch(e:Event):void{ 56 | mse.removeEventListener(Event.COMPLETE, onSearch); 57 | 58 | FlexGlobals.topLevelApplication.loadingOff(); 59 | 60 | tagsList.dataProvider = new ArrayCollection(mse.tags); 61 | } -------------------------------------------------------------------------------- /src/mielophone/ui/views/general/HomeView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/logic/views/general/HomeView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.greensock.TweenLite; 3 | 4 | import flash.events.Event; 5 | import flash.events.KeyboardEvent; 6 | import flash.ui.Keyboard; 7 | 8 | import mielophone.ui.views.search.ArtistSearchView; 9 | 10 | import mx.core.FlexGlobals; 11 | 12 | public function doWork():void{ 13 | this.dispatchEvent(new Event(Event.COMPLETE)); 14 | } 15 | 16 | private function onArtistClick(e:Event):void{ 17 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.artistView); 18 | } 19 | 20 | private function onAlbumClick(e:Event):void{ 21 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.albumView); 22 | } 23 | 24 | private function onSongClick(e:Event):void{ 25 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.songView); 26 | } 27 | 28 | private function onSettingsClick(e:Event):void{ 29 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.settingsView); 30 | } 31 | 32 | private function onMarketClick(e:Event):void{ 33 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.marketView); 34 | } 35 | 36 | private function onVideoClick(e:Event):void{ 37 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.videoView); 38 | } 39 | 40 | private function onRadioStreamClick(e:Event):void{ 41 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.radioView); 42 | } 43 | 44 | private function onSearchKey(e:KeyboardEvent):void{ 45 | if(e.keyCode == Keyboard.ENTER && searchInput.text.length > 1){ 46 | FlexGlobals.topLevelApplication.searchResView.query = searchInput.text; 47 | 48 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.searchResView); 49 | 50 | searchInput.text = ''; 51 | }else if(e.keyCode == Keyboard.ESCAPE){ 52 | searchInput.text = ''; 53 | } 54 | } -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/MoodItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 46 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/TagItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 47 | 48 | 49 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/info/AlbumInfoView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /.actionScriptProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/PluginItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 50 | 51 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/logic/views/general/SettingsView.as: -------------------------------------------------------------------------------- 1 | 2 | import flash.events.Event; 3 | import flash.net.SharedObject; 4 | 5 | import mx.collections.ArrayCollection; 6 | import mx.core.FlexGlobals; 7 | import mx.utils.ObjectUtil; 8 | 9 | [Bindable] 10 | private var pluginsCollection:ArrayCollection; 11 | 12 | private var generalSettings:SharedObject; 13 | 14 | public function doWork():void{ 15 | this.dispatchEvent(new Event(Event.COMPLETE)); 16 | } 17 | 18 | public function initSettings():void{ 19 | // load general settings 20 | generalSettings = SharedObject.getLocal("mielophone.settings"); 21 | // animation 22 | if( generalSettings.data.animation != null ){ 23 | enableAnimations.selected = generalSettings.data.animation; 24 | FlexGlobals.topLevelApplication.animationEnabled = generalSettings.data.animation; 25 | } 26 | 27 | enableTray.selected = FlexGlobals.topLevelApplication.minimizeToTray; 28 | 29 | pluginsCollection = new ArrayCollection( FlexGlobals.topLevelApplication.mse.getActivePlugins() ); 30 | } 31 | 32 | private function onBufferChange(e:Event):void{ 33 | FlexGlobals.topLevelApplication.musicPlayer.setBuffer(bufferingSlider.value * 1000); 34 | } 35 | 36 | private function onPlaylistBehaveChange(e:Event):void{ 37 | FlexGlobals.topLevelApplication.musicPlayer.setPlaylistBehavior(playlistBehavior.selectedItem.value, playlistBehavior.selectedIndex); 38 | } 39 | 40 | private function pluginName(p:Object):String{ 41 | return p.index + ". " + p.name + " (by " + p.author + ")"; 42 | } 43 | 44 | private function saveSettings():void{ 45 | FlexGlobals.topLevelApplication.musicPlayer.setScrobblingAuth(lastfmLogin.text, lastfmPass.text); 46 | } 47 | 48 | private function onEnableAnimationChange(e:Event):void{ 49 | FlexGlobals.topLevelApplication.animationEnabled = enableAnimations.selected; 50 | generalSettings.data.animation = enableAnimations.selected; 51 | generalSettings.flush(); 52 | } 53 | 54 | private function onMinimizeToTrayChange(e:Event):void{ 55 | FlexGlobals.topLevelApplication.setMinimizeToTray(enableTray.selected); 56 | } 57 | 58 | private function onFacebookClick(e:Event):void{ 59 | FlexGlobals.topLevelApplication.connectFacebook(); 60 | } -------------------------------------------------------------------------------- /src/logic/views/info/AlbumInfoView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.util.CUtils; 4 | import com.greensock.TweenLite; 5 | 6 | import flash.events.ErrorEvent; 7 | import flash.events.Event; 8 | import flash.events.KeyboardEvent; 9 | import flash.events.MouseEvent; 10 | import flash.ui.Keyboard; 11 | 12 | import mx.collections.ArrayCollection; 13 | import mx.controls.Alert; 14 | import mx.core.FlexGlobals; 15 | import mx.utils.ObjectUtil; 16 | 17 | private var mse:MusicSearchEngine; 18 | 19 | public function doWork():void{ 20 | mse = FlexGlobals.topLevelApplication.mse; 21 | 22 | artistName.text = FlexGlobals.topLevelApplication.currentAlbum.artist.name; 23 | albumName.text = FlexGlobals.topLevelApplication.currentAlbum.name; 24 | albumImage.source = FlexGlobals.topLevelApplication.currentAlbum.image; 25 | 26 | mse.addEventListener(Event.COMPLETE, onAlbumTracks); 27 | mse.addEventListener(ErrorEvent.ERROR, onAlbumError); 28 | mse.getAlbumTracks(FlexGlobals.topLevelApplication.currentAlbum); 29 | } 30 | 31 | private function onAlbumError(e:ErrorEvent):void{ 32 | mse.removeEventListener(ErrorEvent.ERROR, onAlbumError); 33 | 34 | Alert.show(e.text, "Error finding album!"); 35 | 36 | this.dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, true, false, "Album error")); 37 | } 38 | 39 | private function onAlbumTracks(e:Event):void{ 40 | mse.removeEventListener(Event.COMPLETE, onAlbumTracks); 41 | 42 | FlexGlobals.topLevelApplication.currentAlbum.songs = mse.album.songs; 43 | songsList.dataProvider = new ArrayCollection(mse.album.songs); 44 | 45 | this.dispatchEvent(new Event(Event.COMPLETE)); 46 | } 47 | 48 | private function playAll():void{ 49 | FlexGlobals.topLevelApplication.musicPlayer.playCurrentAlbum(); 50 | } 51 | 52 | private function openAritst():void{ 53 | if( FlexGlobals.topLevelApplication.currentArtist == null || FlexGlobals.topLevelApplication.currentArtist.name != FlexGlobals.topLevelApplication.currentAlbum.artist.name ){ 54 | FlexGlobals.topLevelApplication.currentArtist = FlexGlobals.topLevelApplication.currentAlbum.artist; 55 | } 56 | 57 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.artistInfoView); 58 | } 59 | -------------------------------------------------------------------------------- /src/logic/player/Vars.as: -------------------------------------------------------------------------------- 1 | import com.codezen.mse.MusicSearchEngine; 2 | import com.codezen.mse.playr.Playr; 3 | import com.codezen.mse.services.LastfmScrobbler; 4 | 5 | import flash.net.SharedObject; 6 | 7 | /******************************************************/ 8 | /** IMAGES **/ 9 | /******************************************************/ 10 | [Bindable] 11 | [Embed(source="/assets/player/play.png")] 12 | private var playImg:Class; 13 | 14 | [Bindable] 15 | [Embed(source="/assets/player/pause.png")] 16 | private var pauseImg:Class; 17 | 18 | [Bindable] 19 | [Embed(source="/assets/images/nocover.png")] 20 | private var nocoverImg:Class; 21 | 22 | [Bindable] 23 | [Embed(source="/assets/player/playernormal.png")] 24 | private var normalImg:Class; 25 | 26 | [Bindable] 27 | [Embed(source="/assets/player/playerfull.png")] 28 | private var fullImg:Class; 29 | 30 | /******************************************************/ 31 | /** CONSTANTS **/ 32 | /******************************************************/ 33 | 34 | public static const PLAYLIST_IGNORE:String = "IgnorePlaylist"; 35 | public static const PLAYLIST_CLEAR:String = "ClearPlaylist"; 36 | public static const PLAYLIST_APPEND:String = "AppendPlaylist"; 37 | 38 | /******************************************************/ 39 | /** VARS **/ 40 | /******************************************************/ 41 | // UI Stuff 42 | private var isFullMode:Boolean; 43 | 44 | // MSE 45 | private var mse:MusicSearchEngine; 46 | // TODO: move this to MSE as a state 47 | private var nowSearching:Boolean = false; 48 | 49 | // Player stuff 50 | private var playerSettings:SharedObject; 51 | private var playerBehavior:String; 52 | private var player:Playr; 53 | private var playQueue:Array; 54 | private var playerVolume:int; 55 | private var playerRepeat:Boolean; 56 | private var playerShuffle:Boolean; 57 | private var nextRandomPos:int; 58 | public var playPos:int; 59 | 60 | // tweaks 61 | private var lastPositionMilliseconds:Number; 62 | 63 | // Scrobbler 64 | private var scrobbler:LastfmScrobbler; 65 | private var scrobblerSettings:SharedObject; 66 | private var scrobbleName:String; 67 | private var scrobblePass:String; 68 | private var trackScrobbled:Boolean; 69 | 70 | // facebook stuff 71 | private var fbSongPosted:Boolean; -------------------------------------------------------------------------------- /src/logic/views/search/ArtistSearchView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.greensock.TweenLite; 4 | 5 | import flash.events.Event; 6 | import flash.events.KeyboardEvent; 7 | import flash.events.MouseEvent; 8 | import flash.ui.Keyboard; 9 | 10 | import mx.collections.ArrayCollection; 11 | import mx.core.FlexGlobals; 12 | 13 | private var mse:MusicSearchEngine; 14 | 15 | private var topArtists:ArrayCollection; 16 | 17 | public function doWork():void{ 18 | getTopArtists(); 19 | } 20 | 21 | private function getTopArtists():void{ 22 | if(mse != null && artistList.dataProvider != null){ 23 | this.dispatchEvent(new Event(Event.COMPLETE)); 24 | return; 25 | } 26 | 27 | mse = FlexGlobals.topLevelApplication.mse; 28 | mse.addEventListener(Event.COMPLETE, onArtists); 29 | mse.getTopArtists(); 30 | } 31 | 32 | private function onArtists(e:Event):void{ 33 | mse.removeEventListener(Event.COMPLETE, onArtists); 34 | 35 | topArtists = new ArrayCollection(mse.artists); 36 | artistList.dataProvider = topArtists; 37 | 38 | this.dispatchEvent(new Event(Event.COMPLETE)); 39 | } 40 | 41 | private function onSearchKeyUp(e:KeyboardEvent):void{ 42 | if(e.keyCode == Keyboard.ENTER && searchInput.text.length > 1){ 43 | mse.addEventListener(Event.COMPLETE, onSearch); 44 | mse.findArtist(searchInput.text); 45 | 46 | FlexGlobals.topLevelApplication.loadingOn(); 47 | 48 | searchInput.text = ''; 49 | }else if(e.keyCode == Keyboard.ESCAPE && searchInput.text.length < 1){ 50 | artistList.dataProvider = topArtists; 51 | } 52 | } 53 | 54 | private function onSearch(e:Event):void{ 55 | mse.removeEventListener(Event.COMPLETE, onSearch); 56 | 57 | FlexGlobals.topLevelApplication.loadingOff(); 58 | 59 | artistList.dataProvider = new ArrayCollection(mse.artists); 60 | } 61 | 62 | // ----- search by tag ------ 63 | public function findArtistsByTag(tag:String):void{ 64 | if(mse == null) 65 | mse = FlexGlobals.topLevelApplication.mse; 66 | 67 | FlexGlobals.topLevelApplication.loadingOn(); 68 | 69 | mse.addEventListener(Event.COMPLETE, onTagArtists); 70 | mse.getArtistByTag(tag); 71 | } 72 | 73 | private function onTagArtists(e:Event):void{ 74 | mse.removeEventListener(Event.COMPLETE, onTagArtists); 75 | 76 | artistList.dataProvider = new ArrayCollection(mse.artists); 77 | 78 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.artistView); 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/logic/player/UIActions.as: -------------------------------------------------------------------------------- 1 | import com.greensock.TweenLite; 2 | 3 | import flash.events.Event; 4 | import flash.events.MouseEvent; 5 | import flash.media.SoundTransform; 6 | 7 | import mx.collections.ArrayCollection; 8 | import mx.core.FlexGlobals; 9 | 10 | import spark.components.Group; 11 | 12 | /******************************************************/ 13 | /** PLAYER BUTTONS HANDLERS **/ 14 | /******************************************************/ 15 | private function playBtn_clickHandler(event:Event):void{ 16 | togglePlayPause(); 17 | } 18 | 19 | private function next_btn_clickHandler(event:MouseEvent):void{ 20 | findNextSong(); 21 | } 22 | 23 | private function prev_btn_clickHandler(event:MouseEvent):void{ 24 | findPrevSong(); 25 | } 26 | 27 | private function onVolumeSlider(e:Event):void{ 28 | playerVolume = volumeSlider.value; 29 | 30 | playerSettings.data.volume = playerVolume; 31 | playerSettings.flush(); 32 | 33 | player.volume = volumeSlider.value/100; 34 | 35 | // radio volume 36 | FlexGlobals.topLevelApplication.radioView.setVolume(volumeSlider.value); 37 | } 38 | 39 | private function toggleMute():void{ 40 | if( player.volume > 0 ){ 41 | player.volume = 0; 42 | volumeSlider.value = 0; 43 | }else{ 44 | player.volume = playerVolume / 100; 45 | volumeSlider.value = playerVolume; 46 | } 47 | } 48 | 49 | /******************************************************/ 50 | /** UI STUFF **/ 51 | /******************************************************/ 52 | private function toggleFullMode():void{ 53 | isFullMode = !isFullMode; 54 | if(isFullMode){ 55 | // enable full mode 56 | //toggleFullBtn.source = normalImg; 57 | 58 | this.x = stage.stageWidth - this.width; 59 | var grp:Group = this; 60 | TweenLite.to(this, 0.5, {x:0, width: stage.stageWidth, onComplete:function():void{ 61 | grp.percentWidth = 100; 62 | }}); 63 | //TweenLite.to(FlexGlobals.topLevelApplication.nativeWindow, 0.5, {width:w}); 64 | }else{ 65 | // revert to normal 66 | //toggleFullBtn.source = fullImg; 67 | 68 | this.x = 0; 69 | this.right = 0; 70 | TweenLite.to(this, 0.5, {width:300}); 71 | } 72 | } 73 | 74 | private function toggleRepeat():void{ 75 | playerRepeat = !playerRepeat; 76 | 77 | if(playerRepeat){ 78 | repeatGlow.alpha = 1; 79 | }else{ 80 | repeatGlow.alpha = 0; 81 | } 82 | } 83 | 84 | private function toggleShuffle():void{ 85 | playerShuffle = !playerShuffle; 86 | 87 | if(playerShuffle){ 88 | shuffleGlow.alpha = 1; 89 | }else{ 90 | shuffleGlow.alpha = 0; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/mielophone/ui/controls/NavigationBar.mxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 61 | 65 | 66 | 67 | 68 | 69 | 70 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/logic/Facebook.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.models.Artist; 3 | import com.codezen.mse.models.Song; 4 | import com.facebook.graph.Facebook; 5 | import com.facebook.graph.FacebookDesktop; 6 | 7 | import mx.controls.Alert; 8 | import mx.core.FlexGlobals; 9 | import mx.utils.ObjectUtil; 10 | 11 | private var fbAppID:String = "255624951156266"; 12 | private var fbPermission:Array = ["offline_access", "publish_actions"]; 13 | private var fbLoggedIn:Boolean = false; 14 | private var fbUser:Object; 15 | 16 | // init facebook api 17 | private function initFacebook():void{ 18 | FacebookDesktop.init(fbAppID, onFbInit); 19 | } 20 | 21 | private function onFbInit(session:Object, failure:Object):void{ 22 | if( session != null ){ // logged in 23 | trace('fb init ok. session exists'); 24 | fbLoggedIn = true; 25 | FlexGlobals.topLevelApplication.settingsView.facebookIcon.alpha = 1; 26 | FlexGlobals.topLevelApplication.settingsView.facebookButton.label = "Disconnect from Facebook"; 27 | }else if( failure != null ){ 28 | trace("Initialized with error. Need login."); 29 | trace(ObjectUtil.toString(failure)); 30 | fbLoggedIn = false; 31 | } 32 | } 33 | 34 | // attach to facebook 35 | public function connectFacebook():void{ 36 | FacebookDesktop.login(onFbLogin, fbPermission); 37 | } 38 | 39 | private function onFbLogin(session:Object, failure:Object):void{ 40 | if( session != null ){ // logged in 41 | fbUser = { 42 | 'id':session.uid, 43 | 'token':session.accessToken, 44 | 'fname':session.user.first_name, 45 | 'gender':session.user.gender, 46 | 'lname':session.user.last_name, 47 | 'location':session.user.location.name 48 | }; 49 | 50 | FlexGlobals.topLevelApplication.settingsView.facebookIcon.alpha = 1; 51 | FlexGlobals.topLevelApplication.settingsView.facebookButton.label = "Disconnect from Facebook"; 52 | 53 | fbLoggedIn = true; 54 | }else if( failure != null ){ 55 | trace(ObjectUtil.toString(failure)); 56 | fbLoggedIn = false; 57 | Alert.show(failure.error.message, "Error logging into Facebook!"); 58 | } 59 | } 60 | 61 | // post song to facebook 62 | public function postFbSong(artist:String, song:String):void{ 63 | if(!fbLoggedIn) return; 64 | 65 | var url:String = "http://mielophone.codezen.ru/get/"+encodeURIComponent(artist)+"/"+encodeURIComponent(song); 66 | 67 | trace('posting to facebook, song url: '+url); 68 | 69 | FacebookDesktop.api("/me/mielophone:listen", onFbPostMessage, {song:url}, "POST"); 70 | } 71 | 72 | private function onFbPostMessage(result:Object, fail:Object):void{ 73 | if( result != null ){ 74 | trace('fb post ok'); 75 | }else if( fail != null ){ 76 | trace( "Alert.show('fb message post fail')" ); 77 | trace( ObjectUtil.toString(fail) ); 78 | } 79 | } 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/Mielophone.mxml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/info/RadioView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 57 | 58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/SongItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/FullSongItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 77 | 79 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/logic/player/Playlists.as: -------------------------------------------------------------------------------- 1 | import com.codezen.mse.models.Artist; 2 | import com.codezen.mse.models.Song; 3 | import com.codezen.util.CUtils; 4 | 5 | import flash.events.Event; 6 | import flash.filesystem.File; 7 | import flash.filesystem.FileMode; 8 | import flash.filesystem.FileStream; 9 | import flash.net.FileFilter; 10 | 11 | import mx.collections.ArrayCollection; 12 | import mx.utils.ObjectUtil; 13 | 14 | private function storePlaylist():void{ 15 | playerSettings.data.playlist = playQueue; 16 | playerSettings.flush(); 17 | } 18 | 19 | private function clearPlaylist():void{ 20 | playQueue = []; 21 | songList.dataProvider = new ArrayCollection(playQueue); 22 | } 23 | 24 | private function savePlaylist():void{ 25 | var plXML:XML = ; 26 | 27 | var s:Object; 28 | for each(s in playQueue){ 29 | plXML.appendChild( 30 | 31 | {s.artist.name} 32 | {s.name} 33 | {s.duration} 34 | 35 | ); 36 | } 37 | 38 | // create xml strign 39 | var outputString:String = '\n'; 40 | // append data 41 | outputString += plXML.toXMLString(); 42 | // remove string breaks 43 | outputString = outputString.replace(/\n/gs, File.lineEnding); 44 | // load prefs file from app dir 45 | var playlistFile:File = File.documentsDirectory.resolvePath("playlist.mpl"); 46 | playlistFile.addEventListener(Event.SELECT, function():void{ 47 | // create new reading stream 48 | var stream:FileStream = new FileStream(); 49 | // create new file if not exist and open 50 | stream.open(playlistFile, FileMode.WRITE); 51 | // write settings 52 | stream.writeUTFBytes(outputString); 53 | // close file 54 | stream.close(); 55 | // reset vars 56 | playlistFile = null; 57 | stream = null; 58 | plXML = null; 59 | }); 60 | playlistFile.browseForSave("Save playlist"); 61 | } 62 | 63 | private function openPlaylist():void{ 64 | // load prefs file from app dir 65 | var playlistFile:File = File.documentsDirectory.resolvePath("playlist.mpl"); 66 | playlistFile.addEventListener(Event.SELECT, function(e:Event):void{ 67 | // create new reading stream 68 | var stream:FileStream = new FileStream(); 69 | // if file exists 70 | if (playlistFile.exists) { 71 | // read file 72 | stream.open(playlistFile, FileMode.READ); 73 | // create xml from file 74 | var prefsXML:XML = XML(stream.readUTFBytes(stream.bytesAvailable)); 75 | // close stream 76 | stream.close(); 77 | 78 | // new queue 79 | playQueue = []; 80 | 81 | // fill vars 82 | var item:XML; 83 | var s:Song; 84 | for each(item in prefsXML.children()){ 85 | s = new Song(); 86 | s.artist = new Artist(); 87 | s.artist.name = item.artist.text(); 88 | s.name = item.title.text(); 89 | s.duration = item.duration.text(); 90 | s.durationText = CUtils.secondsToString(s.duration/1000); 91 | 92 | playQueue.push(s); 93 | } 94 | 95 | songList.dataProvider = new ArrayCollection(playQueue); 96 | } 97 | 98 | item = null; 99 | playlistFile = null; 100 | stream = null; 101 | }); 102 | playlistFile.browseForOpen("Open Playlist", [new FileFilter("Mielophone playlist", "mpl")]); 103 | } -------------------------------------------------------------------------------- /src/logic/views/search/VideoSearchView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.models.VideoObject; 4 | 5 | import flash.events.Event; 6 | import flash.events.KeyboardEvent; 7 | import flash.geom.Rectangle; 8 | import flash.media.StageWebView; 9 | import flash.ui.Keyboard; 10 | 11 | import mx.collections.ArrayCollection; 12 | import mx.core.FlexGlobals; 13 | 14 | private var mse:MusicSearchEngine; 15 | 16 | private var webView:StageWebView; 17 | 18 | private var topVideos:ArrayCollection; 19 | 20 | public function doWork():void{ 21 | getTopVideos(); 22 | } 23 | 24 | private function getTopVideos():void{ 25 | if(mse != null && videoList.dataProvider != null){ 26 | this.dispatchEvent(new Event(Event.COMPLETE)); 27 | return; 28 | } 29 | 30 | if(mse == null) 31 | mse = FlexGlobals.topLevelApplication.mse; 32 | 33 | mse.addEventListener(Event.COMPLETE, onVideos); 34 | mse.getTopVideos(); 35 | } 36 | 37 | private function onVideos(e:Event):void{ 38 | mse.removeEventListener(Event.COMPLETE, onVideos); 39 | 40 | topVideos = new ArrayCollection(mse.videos); 41 | videoList.dataProvider = topVideos; 42 | 43 | this.dispatchEvent(new Event(Event.COMPLETE)); 44 | } 45 | 46 | private function onSearchKeyUp(e:KeyboardEvent):void{ 47 | if(e.keyCode == Keyboard.ENTER && searchInput.text.length > 1){ 48 | mse.addEventListener(Event.COMPLETE, onSearch); 49 | mse.findVideo(searchInput.text); 50 | 51 | FlexGlobals.topLevelApplication.loadingOn(); 52 | 53 | searchInput.text = ''; 54 | }else if(e.keyCode == Keyboard.ESCAPE && searchInput.text.length < 1){ 55 | videoList.dataProvider = topVideos; 56 | } 57 | } 58 | 59 | private function onSearch(e:Event):void{ 60 | mse.removeEventListener(Event.COMPLETE, onSearch); 61 | 62 | FlexGlobals.topLevelApplication.loadingOff(); 63 | 64 | videoList.dataProvider = new ArrayCollection(mse.videos); 65 | } 66 | 67 | public function playVideo(v:VideoObject):void{ 68 | // hide search UI 69 | FlexGlobals.topLevelApplication.musicPlayer.visible = 70 | searchUI.visible = false; 71 | 72 | // show video close button 73 | videoCloseButton.visible = true; 74 | 75 | // show video 76 | webView = new StageWebView(); 77 | webView.stage = this.stage; 78 | webView.viewPort = new Rectangle(0, 20, this.stage.stageWidth, this.stage.stageHeight-20); 79 | webView.loadURL(v.contentURL); 80 | } 81 | 82 | private function closeVideo():void{ 83 | webView.dispose(); 84 | webView = null; 85 | 86 | videoCloseButton.visible = false; 87 | 88 | FlexGlobals.topLevelApplication.musicPlayer.visible = 89 | searchUI.visible = true; 90 | } 91 | 92 | public function findVideo(query:String):void{ 93 | if(mse == null) 94 | mse = FlexGlobals.topLevelApplication.mse; 95 | 96 | mse.addEventListener(Event.COMPLETE, onVideoSearch); 97 | mse.findVideo(query); 98 | 99 | FlexGlobals.topLevelApplication.loadingOn(); 100 | } 101 | 102 | private function onVideoSearch(e:Event):void{ 103 | mse.removeEventListener(Event.COMPLETE, onVideoSearch); 104 | 105 | FlexGlobals.topLevelApplication.loadingOff(); 106 | 107 | videoList.dataProvider = new ArrayCollection(mse.videos); 108 | 109 | FlexGlobals.topLevelApplication.changeView(this); 110 | } -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/VideoItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 10 && imageLoader.content.height > 10 ) 50 | img.source = imageLoader.content; 51 | 52 | imageLoader.removeEventListener(Event.COMPLETE, onImageContent); 53 | imageLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); 54 | imageLoader = null; 55 | } 56 | 57 | private function onError(e:Event):void{ 58 | var imageLoader:LoaderInfo = e.target as LoaderInfo; 59 | imageLoader.removeEventListener(Event.COMPLETE, onImageContent); 60 | imageLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); 61 | imageLoader = null; 62 | } 63 | 64 | private function itemrenderer1_creationCompleteHandler(event:FlexEvent):void 65 | { 66 | roundedMask = new Sprite(); 67 | var cornerRadius:uint = 10; 68 | roundedMask.graphics.clear(); 69 | roundedMask.graphics.beginFill(0xFF0000); 70 | roundedMask.graphics.drawRoundRect(0, 0, 120, 90, cornerRadius, cornerRadius); 71 | roundedMask.graphics.endFill(); 72 | } 73 | 74 | private function itemrenderer1_clickHandler(event:MouseEvent):void 75 | { 76 | FlexGlobals.topLevelApplication.videoView.playVideo(data as VideoObject); 77 | } 78 | 79 | ]]> 80 | 81 | 82 | 83 | 84 | 85 | 86 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/general/SettingsView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/mielophone/ui/home/SupportButton.mxml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 28 | 29 | 34 | 35 | 40 | 41 | 42 | 47 | 48 | 53 | 54 | 59 | 60 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/logic/views/search/SongSearchView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.models.Artist; 4 | import com.codezen.mse.models.Mood; 5 | import com.codezen.mse.models.Song; 6 | import com.codezen.mse.playr.PlayrTrack; 7 | import com.greensock.TweenLite; 8 | 9 | import flash.events.Event; 10 | import flash.events.KeyboardEvent; 11 | import flash.events.MouseEvent; 12 | import flash.ui.Keyboard; 13 | 14 | import mielophone.ui.views.search.SongSearchView; 15 | 16 | import mx.collections.ArrayCollection; 17 | import mx.core.FlexGlobals; 18 | import mx.utils.ObjectUtil; 19 | 20 | private var mse:MusicSearchEngine; 21 | private var topSongs:ArrayCollection; 22 | 23 | public function doWork():void{ 24 | getTopSongs(); 25 | } 26 | 27 | private function getTopSongs():void{ 28 | if(mse != null && songList.dataProvider != null){ 29 | this.dispatchEvent(new Event(Event.COMPLETE)); 30 | return; 31 | } 32 | 33 | if(mse == null) 34 | mse = FlexGlobals.topLevelApplication.mse; 35 | 36 | mse.addEventListener(Event.COMPLETE, onSongs); 37 | mse.getTopTracks(); 38 | } 39 | 40 | private function onSongs(e:Event):void{ 41 | mse.removeEventListener(Event.COMPLETE, onSongs); 42 | 43 | //FlexGlobals.topLevelApplication.musicPlayer.setQueue(mse.songs); 44 | topSongs = new ArrayCollection(mse.songs); 45 | songList.dataProvider = topSongs; 46 | 47 | this.dispatchEvent(new Event(Event.COMPLETE)); 48 | } 49 | 50 | private function onSearchKeyUp(e:KeyboardEvent):void{ 51 | if(e.keyCode == Keyboard.ENTER && searchInput.text.length > 1){ 52 | mse.addEventListener(Event.COMPLETE, onSearch); 53 | mse.findMP3byText(searchInput.text); 54 | 55 | FlexGlobals.topLevelApplication.loadingOn(); 56 | 57 | searchInput.text = ''; 58 | }else if(e.keyCode == Keyboard.ESCAPE && searchInput.text.length < 1){ 59 | songList.dataProvider = topSongs; 60 | } 61 | } 62 | 63 | private function onSearch(e:Event):void{ 64 | mse.removeEventListener(Event.COMPLETE, onSearch); 65 | 66 | FlexGlobals.topLevelApplication.loadingOff(); 67 | 68 | var _songs:Array = []; 69 | var pl:PlayrTrack; 70 | var song:Song; 71 | var num:int = 0; 72 | for each(pl in mse.mp3s){ 73 | song = new Song(); 74 | song.name = pl.title; 75 | song.artist = new Artist(); 76 | song.artist.name = pl.artist; 77 | song.duration = pl.totalSeconds * 1000; 78 | song.durationText = pl.totalTime; 79 | song.number = num++; 80 | song.track = pl; 81 | _songs.push(song); 82 | } 83 | 84 | //FlexGlobals.topLevelApplication.musicPlayer.setQueue(_songs); 85 | songList.dataProvider = new ArrayCollection( _songs ); 86 | } 87 | 88 | public function findSongsByTag(tag:String):void{ 89 | if(mse == null) 90 | mse = FlexGlobals.topLevelApplication.mse; 91 | 92 | FlexGlobals.topLevelApplication.loadingOn(); 93 | 94 | mse.addEventListener(Event.COMPLETE, onTagSongs); 95 | mse.findSongsByTag(tag); 96 | } 97 | 98 | private function onTagSongs(e:Event):void{ 99 | mse.removeEventListener(Event.COMPLETE, onTagSongs); 100 | 101 | songList.dataProvider = new ArrayCollection(mse.songs); 102 | 103 | FlexGlobals.topLevelApplication.loadingOff(); 104 | 105 | FlexGlobals.topLevelApplication.changeView(this); 106 | } 107 | 108 | public function findSongsByMood(mood:Mood):void{ 109 | if(mse == null) 110 | mse = FlexGlobals.topLevelApplication.mse; 111 | 112 | FlexGlobals.topLevelApplication.loadingOn(); 113 | 114 | mse.addEventListener(Event.COMPLETE, onMoodSongs); 115 | mse.findSongsByMood(mood); 116 | } 117 | 118 | private function onMoodSongs(e:Event):void{ 119 | mse.removeEventListener(Event.COMPLETE, onMoodSongs); 120 | 121 | songList.dataProvider = new ArrayCollection(mse.songs); 122 | 123 | FlexGlobals.topLevelApplication.loadingOff(); 124 | 125 | FlexGlobals.topLevelApplication.changeView(this); 126 | } 127 | 128 | private function playAll():void{ 129 | var src:Array = (songList.dataProvider as ArrayCollection).source; 130 | 131 | FlexGlobals.topLevelApplication.musicPlayer.setQueue(src); 132 | } -------------------------------------------------------------------------------- /src/mielophone/ui/skins/MielophoneApplication.mxml: -------------------------------------------------------------------------------- 1 | 11 | 35 | 37 | [HostComponent("spark.components.WindowedApplication")] 38 | 39 | 40 | /* Define the skin elements that should not be colorized. 41 | * For WindowedApplication, border and status bar background are colorized, 42 | * but the content area and status text are not. 43 | * Exclude the titleBar and scroller because they are SparkSkins and we 44 | * don't want to colorize them twice. 45 | * 46 | * @langversion 3.0 47 | * @playerversion Flash 10 48 | * @playerversion AIR 1.5 49 | * @productversion Flex 4 50 | */ 51 | static private const exclusions:Array = ["backgroundRect", "contentGroup", "statusText"]; 52 | 53 | /** 54 | * @private 55 | */ 56 | override public function get colorizeExclusions():Array {return exclusions;} 57 | 58 | /** 59 | * @private 60 | */ 61 | override protected function initializationComplete():void 62 | { 63 | useChromeColor = true; 64 | super.initializationComplete(); 65 | } 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/ArtistItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 10 && imageLoader.content.height > 10 ) 76 | image.source = imageLoader.content; 77 | 78 | imageLoader.removeEventListener(Event.COMPLETE, onImageContent); 79 | imageLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); 80 | imageLoader = null; 81 | } 82 | 83 | private function onError(e:Event):void{ 84 | var imageLoader:LoaderInfo = e.target as LoaderInfo; 85 | imageLoader.removeEventListener(Event.COMPLETE, onImageContent); 86 | imageLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); 87 | imageLoader = null; 88 | } 89 | 90 | private function onImage(e:Event):void{ 91 | var lfm:LastFM = e.target as LastFM; 92 | lfm.removeEventListener(Event.COMPLETE, onImage); 93 | 94 | data.image = lfm.artist.image; 95 | 96 | preloadImage(); 97 | 98 | lfm = null; 99 | } 100 | 101 | private function itemrenderer1_clickHandler(event:MouseEvent):void 102 | { 103 | FlexGlobals.topLevelApplication.currentArtist = data as Artist; 104 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.artistInfoView); 105 | } 106 | 107 | ]]> 108 | 109 | 110 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/mielophone/ui/views/search/SearchResultsView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /src/mielophone/extensions/ExtensionsManager.as: -------------------------------------------------------------------------------- 1 | package mielophone.extensions 2 | { 3 | import com.codezen.helper.Worker; 4 | 5 | import flash.display.Loader; 6 | import flash.events.Event; 7 | import flash.events.FileListEvent; 8 | import flash.filesystem.File; 9 | import flash.net.URLLoader; 10 | import flash.net.URLLoaderDataFormat; 11 | import flash.net.URLRequest; 12 | import flash.system.ApplicationDomain; 13 | import flash.system.LoaderContext; 14 | 15 | import mx.utils.ObjectUtil; 16 | 17 | public final class ExtensionsManager extends Worker 18 | { 19 | // plugins array 20 | private var _plugins:Array; 21 | 22 | // loaders 23 | private var urlReq:URLRequest; 24 | private var urlLoad:URLLoader; 25 | private var loader:Loader; 26 | 27 | // load queue 28 | private var _loadQueue:Array; 29 | 30 | // plugins dir 31 | private var _dirs:Array; 32 | // file class 33 | private var _file:File; 34 | 35 | // context 36 | private var context:LoaderContext; 37 | 38 | // counter 39 | private var dircounter:int; 40 | private var counter:int; 41 | 42 | public function ExtensionsManager(dirs:Array) 43 | { 44 | // save dir 45 | _dirs = dirs.concat(); 46 | 47 | // init plugins array 48 | _plugins = []; 49 | 50 | // load plugins 51 | dircounter = _dirs.length; 52 | loadPlugins(); 53 | } 54 | 55 | private function loadPlugins():void{ 56 | dircounter--; 57 | if(dircounter < 0){ 58 | dispatchEvent(new Event(Event.INIT)); 59 | return; 60 | } 61 | _file = new File(_dirs[dircounter]); 62 | if(!_file.exists){ 63 | loadPlugins(); 64 | return; 65 | } 66 | _file.addEventListener(FileListEvent.DIRECTORY_LISTING, onListing); 67 | //_file.addEventListener(IOErrorEvent.IO_ERROR, onFolderError); 68 | _file.getDirectoryListingAsync(); 69 | } 70 | 71 | // parse listing of files 72 | private function onListing(e:FileListEvent):void{ 73 | var contents:Array = e.files; 74 | 75 | _loadQueue = []; 76 | 77 | var cFile:File; 78 | for (var i:int = 0; i < contents.length; i++) { 79 | cFile = contents[i] as File; 80 | // check extension 81 | if(cFile.extension == "swf") _loadQueue.push(cFile.url); 82 | //loadPluginFromPath(cFile.url); 83 | } 84 | 85 | counter = _loadQueue.length; 86 | loadPluginsFromPath(); 87 | } 88 | 89 | // load plugin from path 90 | private function loadPluginsFromPath():void{ 91 | var path:String = _loadQueue[ _loadQueue.length - counter ]; 92 | 93 | if(path == null){ 94 | checkInit(); 95 | return; 96 | } 97 | 98 | urlReq = new URLRequest(path); 99 | urlLoad = new URLLoader(); 100 | urlLoad.dataFormat = URLLoaderDataFormat.BINARY; 101 | urlLoad.addEventListener(Event.COMPLETE, onPluginData); 102 | urlLoad.load(urlReq); 103 | } 104 | 105 | private function onPluginData(e:Event):void{ 106 | urlLoad.removeEventListener(Event.COMPLETE, onPluginData); 107 | 108 | // create context 109 | context = new LoaderContext(false, ApplicationDomain.currentDomain ); 110 | context.allowCodeImport = true; 111 | context.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain); 112 | // create loader 113 | loader = new Loader(); 114 | loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onPluginLoaded); 115 | loader.loadBytes(urlLoad.data, context); 116 | } 117 | 118 | private function onPluginLoaded(e:Event):void{ 119 | loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onPluginLoaded); 120 | 121 | var className:Class = loader.contentLoaderInfo.applicationDomain.getDefinition("MUIExtension") as Class; 122 | var classInstance:IMUIExtension = new className(); 123 | _plugins.push(classInstance); 124 | 125 | checkInit(); 126 | } 127 | 128 | private function checkInit():void{ 129 | counter--; 130 | if(counter <= 0){ 131 | if(dircounter <= 0){ 132 | trace( 'ui done: '+ObjectUtil.toString(_plugins) ); 133 | 134 | dispatchEvent(new Event(Event.INIT)); 135 | }else{ 136 | loadPlugins(); 137 | } 138 | }else{ 139 | loadPluginsFromPath(); 140 | } 141 | } 142 | 143 | // --------------------------------------------- 144 | public function listPlugins():Array{ 145 | var searcher:IMUIExtension; 146 | var i:int; 147 | var res:Array = []; 148 | for(i = 0; i < _plugins.length; i++){ 149 | searcher = _plugins[i] as IMUIExtension; 150 | res.push({index: i+1, name: searcher.PLUGIN_NAME, author: searcher.AUTHOR_NAME}); 151 | } 152 | 153 | return res; 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /src/logic/ui/SystemTray.as: -------------------------------------------------------------------------------- 1 | 2 | import flash.desktop.DockIcon; 3 | import flash.desktop.NativeApplication; 4 | import flash.desktop.SystemTrayIcon; 5 | import flash.display.Bitmap; 6 | import flash.display.BitmapData; 7 | import flash.display.NativeMenu; 8 | import flash.display.NativeMenuItem; 9 | import flash.display.NativeWindowDisplayState; 10 | import flash.events.Event; 11 | import flash.events.MouseEvent; 12 | import flash.events.NativeWindowDisplayStateEvent; 13 | import flash.net.SharedObject; 14 | 15 | import mx.core.FlexGlobals; 16 | 17 | [Embed(source='/assets/logo/logo-128.png')] 18 | private var trayIcon:Class; 19 | 20 | private var traySettings:SharedObject; 21 | public var minimizeToTray:Boolean; 22 | 23 | // System tray and doc icon classes 24 | private var sysTrayIcon:SystemTrayIcon; 25 | //private var dockIcon:DockIcon; 26 | 27 | public function setTrayTooltip(tip:String = "Mielophone"):void{ 28 | if(sysTrayIcon != null) 29 | sysTrayIcon.tooltip = tip; 30 | } 31 | 32 | public function setMinimizeToTray(s:Boolean):void{ 33 | minimizeToTray = s; 34 | // save 35 | traySettings.data.minimize = s; 36 | traySettings.flush(); 37 | } 38 | 39 | /** 40 | * Initialize system tray or dock icon 41 | * add event listeners 42 | **/ 43 | private function initDock():void{ 44 | // tray 45 | traySettings = SharedObject.getLocal("mielophone.systray"); 46 | if( traySettings.data.minimize != null ){ 47 | minimizeToTray = traySettings.data.minimize; 48 | }else{ 49 | minimizeToTray = true; 50 | } 51 | // check if it's mac 52 | //if(NativeApplication.supportsDockIcon){ 53 | // dockIcon = NativeApplication.nativeApplication.icon as DockIcon; // stopIcon as DockIcon; 54 | //NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, undock); 55 | //dockIcon.menu = createIconMenu(); 56 | //} else 57 | if (NativeApplication.supportsSystemTrayIcon){ // if it's windows 58 | sysTrayIcon = NativeApplication.nativeApplication.icon as SystemTrayIcon; 59 | sysTrayIcon.tooltip = "Mielophone"; 60 | sysTrayIcon.addEventListener(MouseEvent.CLICK, undock); 61 | sysTrayIcon.menu = createIconMenu(); 62 | } 63 | // add dock handling 64 | this.addEventListener(NativeWindowDisplayStateEvent.DISPLAY_STATE_CHANGE, onMinimize); 65 | } 66 | 67 | /** 68 | * On minimize go to tray 69 | **/ 70 | private function onMinimize(e:NativeWindowDisplayStateEvent):void{ 71 | if(e.afterDisplayState == NativeWindowDisplayState.MINIMIZED && minimizeToTray){ 72 | dock(); 73 | } 74 | } 75 | 76 | /** 77 | * Dock to tray 78 | **/ 79 | private function dock(event:Event = null):void{ 80 | this.nativeWindow.visible = false; 81 | var image:Bitmap = new trayIcon; 82 | NativeApplication.nativeApplication.icon.bitmaps = [image.bitmapData]; 83 | 84 | // erase vars 85 | image = null; 86 | } 87 | 88 | /** 89 | * Undock from tray 90 | **/ 91 | private function undock(event:Event = null):void{ 92 | this.nativeWindow.visible = true; 93 | this.nativeWindow.restore(); 94 | this.nativeApplication.icon.bitmaps = []; 95 | } 96 | 97 | 98 | /** 99 | * Exit app 100 | **/ 101 | private function doExit(e:Event):void{ 102 | this.exit(); 103 | } 104 | private function playPause(e:Event):void{ 105 | musicPlayer.togglePlayPause(); 106 | radioView.toggleRadio(); 107 | } 108 | private function playNext(e:Event):void{ 109 | musicPlayer.playNext(); 110 | } 111 | 112 | /** 113 | * Create icon tray or dock 114 | **/ 115 | private function createIconMenu():NativeMenu{ 116 | var iconMenu:NativeMenu = new NativeMenu(); 117 | // playstop 118 | /*var playstopCommand:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("Play/Stop")); 119 | //playstopCommand.addEventListener(Event.SELECT,null);// playMusic); 120 | // next 121 | var playNextCommand:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("Next")); 122 | //playNextCommand.addEventListener(Event.SELECT,null);// skipMusic); 123 | // separator 124 | var separatorA:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("A", true));*/ 125 | // win exit 126 | if(NativeApplication.supportsSystemTrayIcon){ 127 | var playstopCommand:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("Play/Pause")); 128 | playstopCommand.addEventListener(Event.SELECT,playPause); 129 | 130 | var playNextCommand:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("Next")); 131 | playNextCommand.addEventListener(Event.SELECT,playNext); 132 | 133 | // separator 134 | var separatorA:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("A", true)); 135 | 136 | var exitCommand:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("Exit")); 137 | exitCommand.addEventListener(Event.SELECT, doExit); 138 | } 139 | return iconMenu; 140 | } -------------------------------------------------------------------------------- /src/logic/views/info/ArtistInfoView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.models.Album; 4 | import com.codezen.util.CUtils; 5 | import com.greensock.TweenLite; 6 | 7 | import flash.events.ErrorEvent; 8 | import flash.events.Event; 9 | import flash.events.KeyboardEvent; 10 | import flash.events.MouseEvent; 11 | import flash.ui.Keyboard; 12 | 13 | import mx.collections.ArrayCollection; 14 | import mx.core.FlexGlobals; 15 | import mx.utils.ObjectUtil; 16 | 17 | [Embed(source="/assets/images/nocover.png")] 18 | private var nocoverImg:Class; 19 | 20 | private var mse:MusicSearchEngine; 21 | 22 | private var loadedArtist:String; 23 | 24 | private var albumCounter:int; 25 | private var albumQueue:Array; 26 | 27 | public function doWork():void{ 28 | artistImage.source = FlexGlobals.topLevelApplication.currentArtist.image; 29 | artistDesc.text = CUtils.convertHTMLEntities( CUtils.stripTags(FlexGlobals.topLevelApplication.currentArtist.description_short) ); 30 | artistName.text = FlexGlobals.topLevelApplication.currentArtist.name; 31 | 32 | mse = FlexGlobals.topLevelApplication.mse; 33 | 34 | if(loadedArtist != FlexGlobals.topLevelApplication.currentArtist.name){ 35 | loadedArtist = FlexGlobals.topLevelApplication.currentArtist.name; 36 | 37 | // reset old info 38 | artistDesc.text = fullDescription.text = ""; 39 | similarList.dataProvider = tagsList.dataProvider = new ArrayCollection(); 40 | 41 | mse.addEventListener(Event.COMPLETE, onArtistInfo); 42 | mse.addEventListener(ErrorEvent.ERROR, onNoArtistInfo); 43 | mse.getArtistInfo(FlexGlobals.topLevelApplication.currentArtist); 44 | }else{ 45 | this.dispatchEvent(new Event(Event.COMPLETE)); 46 | } 47 | } 48 | 49 | private function onArtistInfo(e:Event):void{ 50 | mse.removeEventListener(Event.COMPLETE, onArtistInfo); 51 | mse.removeEventListener(ErrorEvent.ERROR, onNoArtistInfo); 52 | artistDesc.text = CUtils.convertHTMLEntities( CUtils.stripTags(FlexGlobals.topLevelApplication.currentArtist.description_short) );//description) ); 53 | 54 | similarList.dataProvider = new ArrayCollection(mse.artistInfo.similar); 55 | tagsList.dataProvider = new ArrayCollection(mse.artistInfo.tags); 56 | fullDescription.text = CUtils.convertHTMLEntities( CUtils.stripTags(FlexGlobals.topLevelApplication.currentArtist.description) );//description) ); 57 | 58 | loadAlbums(); 59 | } 60 | 61 | private function onNoArtistInfo(e:Event):void{ 62 | mse.removeEventListener(Event.COMPLETE, onArtistInfo); 63 | mse.removeEventListener(ErrorEvent.ERROR, onNoArtistInfo); 64 | 65 | artistDesc.text = "No info available."; 66 | artistImage.source = nocoverImg; 67 | 68 | loadAlbums(); 69 | } 70 | 71 | private function loadAlbums():void{ 72 | mse.addEventListener(Event.COMPLETE, onArtistAlbums); 73 | mse.addEventListener(ErrorEvent.ERROR, onNoAlbums); 74 | mse.getArtistAlbums(FlexGlobals.topLevelApplication.currentArtist); 75 | } 76 | 77 | private function onArtistAlbums(e:Event):void{ 78 | mse.removeEventListener(Event.COMPLETE, onArtistAlbums); 79 | mse.removeEventListener(ErrorEvent.ERROR, onNoAlbums); 80 | 81 | albumsList.dataProvider = new ArrayCollection(mse.albums); 82 | 83 | this.dispatchEvent(new Event(Event.COMPLETE)); 84 | } 85 | 86 | private function onNoAlbums(e:Event):void{ 87 | mse.removeEventListener(Event.COMPLETE, onArtistAlbums); 88 | mse.removeEventListener(ErrorEvent.ERROR, onNoAlbums); 89 | 90 | albumsList.dataProvider = new ArrayCollection(); 91 | 92 | this.dispatchEvent(new Event(Event.COMPLETE)); 93 | } 94 | 95 | 96 | private function toggleInfo():void{ 97 | albumsListScroller.visible = !albumsListScroller.visible; 98 | artistInfoScroller.visible = !artistInfoScroller.visible; 99 | } 100 | 101 | private function playAll():void{ 102 | albumCounter = albumsList.dataProvider.length; 103 | albumQueue = []; 104 | 105 | FlexGlobals.topLevelApplication.loadingOn(); 106 | 107 | mse.addEventListener(ErrorEvent.ERROR, onError); 108 | 109 | getNextAlbumSongs(); 110 | } 111 | 112 | private function getNextAlbumSongs():void{ 113 | albumCounter--; 114 | if(albumCounter < 0){ 115 | // cleanup 116 | mse.removeEventListener(Event.COMPLETE, onAlbumTracks); 117 | mse.removeEventListener(ErrorEvent.ERROR, onError); 118 | 119 | // hide loader 120 | FlexGlobals.topLevelApplication.loadingOff(); 121 | 122 | // set new queue 123 | FlexGlobals.topLevelApplication.musicPlayer.setQueue(albumQueue); 124 | return; 125 | } 126 | 127 | mse.addEventListener(Event.COMPLETE, onAlbumTracks); 128 | mse.getAlbumTracks(albumsList.dataProvider[albumCounter] as Album); 129 | } 130 | 131 | private function onAlbumTracks(e:Event):void{ 132 | mse.removeEventListener(Event.COMPLETE, onAlbumTracks); 133 | 134 | albumQueue = albumQueue.concat( mse.album.songs ); 135 | 136 | getNextAlbumSongs(); 137 | } 138 | 139 | private function onError(e:Event):void{ 140 | getNextAlbumSongs(); 141 | } -------------------------------------------------------------------------------- /src/logic/player/Events.as: -------------------------------------------------------------------------------- 1 | import com.codezen.mse.playr.PlayrEvent; 2 | import com.codezen.mse.playr.PlayrStates; 3 | import com.codezen.util.CUtils; 4 | 5 | import flash.events.ErrorEvent; 6 | import flash.events.Event; 7 | 8 | import mx.core.FlexGlobals; 9 | 10 | import spark.utils.TextFlowUtil; 11 | 12 | /******************************************************/ 13 | /** PLAYER EVENTS **/ 14 | /******************************************************/ 15 | private function onSeek(e:Event):void{ 16 | var seekTime:Number = timeSlider.slider.value*1000; 17 | player.scrobbleTo(seekTime); 18 | } 19 | 20 | 21 | private function onTrackEnd(e:PlayrEvent):void{ 22 | timeMax.text = ""; 23 | timeCurrent.text = ""; 24 | 25 | FlexGlobals.topLevelApplication.setTrayTooltip(); 26 | FlexGlobals.topLevelApplication.nativeWindow.title = "Mielophone"; 27 | 28 | findNextSong(); 29 | } 30 | 31 | private function onPlayerState(e:PlayrEvent):void{ 32 | switch(e.playrState){ 33 | case PlayrStates.PLAYING: 34 | playBtn.source = pauseImg; 35 | break; 36 | case PlayrStates.STOPPED: 37 | case PlayrStates.WAITING: 38 | timeMax.text = ""; 39 | timeCurrent.text = ""; 40 | 41 | FlexGlobals.topLevelApplication.setTrayTooltip(); 42 | FlexGlobals.topLevelApplication.nativeWindow.title = "Mielophone"; 43 | case PlayrStates.PAUSED: 44 | playBtn.source = playImg; 45 | break; 46 | } 47 | } 48 | 49 | private function onProgress(e:PlayrEvent):void{ 50 | if(timeSlider.maximum != player.totalSeconds){ 51 | timeSlider.maximum = player.totalSeconds; 52 | timeMax.text = player.totalTime; 53 | 54 | var artist:String = CUtils.convertHTMLEntities(player.artist); 55 | var song:String = CUtils.convertHTMLEntities(player.title); 56 | 57 | nowPlayingText.textFlow = TextFlowUtil.importFromString(""+song+"  by "+artist+""); 58 | 59 | FlexGlobals.topLevelApplication.setTrayTooltip( "Mielophone: "+artist+" - "+song ); 60 | FlexGlobals.topLevelApplication.nativeWindow.title = "Mielophone: "+artist+" - "+song; 61 | } 62 | 63 | // check if playback is stuck 64 | if(lastPositionMilliseconds == player.currentMiliseconds && player.currentMiliseconds != 0 && player.playrState != PlayrStates.BUFFERING){ 65 | player.scrobbleTo(0); 66 | player.stop(); 67 | onTrackEnd(null); 68 | return; 69 | } 70 | 71 | lastPositionMilliseconds = player.currentMiliseconds; 72 | timeSlider.position = player.currentSeconds; 73 | 74 | // do stuff on 70% of track 75 | if( player.currentSeconds > (player.totalSeconds * 0.7) ){ 76 | // scrobble track on 70% 77 | if(scrobbler != null && scrobbler.isInitialized && !trackScrobbled){ 78 | scrobbler.doScrobble( 79 | CUtils.convertHTMLEntities(player.artist), 80 | CUtils.convertHTMLEntities(player.title), 81 | new Date().time.toString()); 82 | trackScrobbled = true; 83 | } 84 | 85 | // search for next track url 86 | if(!prefetchedNext) prefetchNextSong(); 87 | 88 | // post to facebook 89 | if(!fbSongPosted){ 90 | fbSongPosted = true; 91 | FlexGlobals.topLevelApplication.postFbSong(player.artist, player.title); 92 | } 93 | } 94 | 95 | // workaround for end event not dispatching 96 | if(player.currentSeconds >= player.totalSeconds){ 97 | player.scrobbleTo(0); 98 | player.stop(); 99 | onTrackEnd(null); 100 | return; 101 | } 102 | 103 | timeCurrent.text = player.currentTime; 104 | } 105 | 106 | private function onStreamProgress(e:PlayrEvent):void{ 107 | timeSlider.progress = e.progress; 108 | } 109 | 110 | private function onSong(e:PlayrEvent):void{ 111 | timeSlider.maximum = player.totalSeconds; 112 | timeMax.text = player.totalTime; 113 | 114 | var artist:String = CUtils.convertHTMLEntities(player.artist); 115 | var song:String = CUtils.convertHTMLEntities(player.title); 116 | 117 | nowPlayingText.textFlow = TextFlowUtil.importFromString(""+song+"  by "+artist+""); 118 | 119 | FlexGlobals.topLevelApplication.setTrayTooltip( "Mielophone: "+artist+" - "+song ); 120 | FlexGlobals.topLevelApplication.nativeWindow.title = "Mielophone: "+artist+" - "+song; 121 | 122 | // get cover 123 | mse.addEventListener(Event.COMPLETE, onTrackCover); 124 | mse.addEventListener(ErrorEvent.ERROR, onCoverError); 125 | mse.getTrackInfo(artist, song); 126 | } 127 | 128 | private function onTrackCover(e:Event):void{ 129 | mse.removeEventListener(Event.COMPLETE, onTrackCover); 130 | 131 | if(mse.songInfo.album.image != null && mse.songInfo.album.image.length > 0){ 132 | //albumCover.source = mse.songInfo.album.image; 133 | }else{ 134 | //albumCover.source = nocoverImg; 135 | } 136 | } 137 | 138 | private function onCoverError(e:Event):void{ 139 | mse.removeEventListener(ErrorEvent.ERROR, onCoverError); 140 | 141 | trace('album search error'); 142 | 143 | //albumCover.source = nocoverImg; 144 | } -------------------------------------------------------------------------------- /src/logic/views/general/MarketView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.greensock.TweenLite; 3 | 4 | import flash.events.Event; 5 | import flash.events.IOErrorEvent; 6 | import flash.events.KeyboardEvent; 7 | import flash.events.ProgressEvent; 8 | import flash.filesystem.File; 9 | import flash.filesystem.FileMode; 10 | import flash.filesystem.FileStream; 11 | import flash.net.FileReference; 12 | import flash.net.URLLoader; 13 | import flash.net.URLRequest; 14 | import flash.net.URLStream; 15 | import flash.ui.Keyboard; 16 | import flash.utils.ByteArray; 17 | 18 | import mielophone.ui.views.search.ArtistSearchView; 19 | 20 | import mx.collections.ArrayCollection; 21 | import mx.controls.Alert; 22 | import mx.core.FlexGlobals; 23 | import mx.utils.ObjectUtil; 24 | import mx.utils.object_proxy; 25 | 26 | private const MARKET_URL:String = "http://mielophone.github.com/market.xml"; 27 | 28 | [Bindable] 29 | private var plugins:Array; 30 | 31 | private var request:URLRequest; 32 | private var stream:URLStream; 33 | private var fileStream:FileStream; 34 | private var file:File; 35 | 36 | private var downloadPath:String; 37 | private var downloadFile:String; 38 | 39 | public function doWork():void{ 40 | this.dispatchEvent(new Event(Event.COMPLETE)); 41 | } 42 | 43 | public function fetchPlugins():void{ 44 | var urlReq:URLRequest = new URLRequest(MARKET_URL); 45 | var urlLoader:URLLoader = new URLLoader(); 46 | urlLoader.addEventListener(Event.COMPLETE, onMarketData); 47 | urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onMarketError); 48 | urlLoader.load(urlReq); 49 | } 50 | 51 | private function onMarketError(e:Event):void{ 52 | e.target.removeEventListener(Event.COMPLETE, onMarketData); 53 | e.target.removeEventListener(IOErrorEvent.IO_ERROR, onMarketError); 54 | 55 | statusText.text = "Sorry! Market is currently unavailable, try later!"; 56 | } 57 | 58 | private function onMarketData(e:Event):void{ 59 | e.target.removeEventListener(Event.COMPLETE, onMarketData); 60 | e.target.removeEventListener(IOErrorEvent.IO_ERROR, onMarketError); 61 | 62 | statusText.text = ""; 63 | 64 | // new plugins 65 | plugins = []; 66 | // get installed plugins 67 | var installedPlugins:Array = FlexGlobals.topLevelApplication.mse.getActivePlugins(); 68 | 69 | var newPluginsCount:int = 0; 70 | 71 | // parse xml 72 | var xml:XML = new XML(e.target.data); 73 | var plugin:XML, isInstalled:Boolean, i:int; 74 | for each(plugin in xml.children()){ 75 | isInstalled = false; 76 | // detect if plugin is installed 77 | for(i = 0; i < installedPlugins.length; i++){ 78 | if( plugin.@name == installedPlugins[i].name ){ 79 | isInstalled = true; 80 | break; 81 | } 82 | } 83 | // append 84 | plugins.push({name: plugin.@name, author: plugin.@author, description: plugin.@description, url: plugin.@url, installed:isInstalled}); 85 | if(!isInstalled) newPluginsCount++; 86 | } 87 | 88 | if(newPluginsCount > 0) FlexGlobals.topLevelApplication.homeView.marketButton.newPlugins.text = newPluginsCount; 89 | 90 | pluginList.dataProvider = new ArrayCollection(plugins); 91 | } 92 | 93 | public function installPlugin(plugin:Object):void{ 94 | if(plugin.installed){ 95 | Alert.show("Plugin is already installed!", "Error!"); 96 | return; 97 | }else{ 98 | var url:String = plugin.url; 99 | var name:String = plugin.name; 100 | 101 | downloadProgress.visible = progressText.visible = true; 102 | 103 | fileStream = new FileStream(); 104 | stream = new URLStream(); 105 | 106 | stream.addEventListener(ProgressEvent.PROGRESS, onProgress); 107 | stream.addEventListener(Event.COMPLETE, onComplete); 108 | stream.addEventListener(IOErrorEvent.IO_ERROR, onError); 109 | 110 | downloadFile = url.substr(url.lastIndexOf("/")); 111 | 112 | trace(downloadFile); 113 | 114 | file = File.desktopDirectory.resolvePath( File.applicationStorageDirectory.resolvePath("plugins/").nativePath+downloadFile ); 115 | trace(file.nativePath); 116 | request = new URLRequest(url); 117 | fileStream.openAsync(file, FileMode.WRITE); 118 | 119 | stream.load(request); 120 | } 121 | } 122 | 123 | private function onProgress(e:ProgressEvent):void{ 124 | var byteArray:ByteArray = new ByteArray(); 125 | var value:Number = e.bytesLoaded; 126 | var total:Number = e.bytesTotal; 127 | var precent:Number = Math.round(value*100/total); 128 | 129 | stream.readBytes(byteArray, 0, stream.bytesAvailable); 130 | fileStream.writeBytes(byteArray, 0, byteArray.length); 131 | 132 | downloadProgress.setProgress( e.bytesLoaded, e.bytesTotal ); 133 | } 134 | 135 | private function onComplete(e:Event):void{ 136 | fileStream.close(); 137 | stream.close(); 138 | 139 | downloadProgress.visible = progressText.visible = false; 140 | Alert.show("Please, restart to enable new plugin", "Plugin download complete!"); 141 | } 142 | 143 | private function onError(e:Event):void{ 144 | downloadProgress.visible = progressText.visible = false; 145 | Alert.show("Something went wrong, try again.", "Error downloading plugin!"); 146 | } -------------------------------------------------------------------------------- /src/mielophone/ui/views/info/ArtistInfoView.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/AlbumItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 10 && imageLoader.content.height > 10 ) 61 | img.source = imageLoader.content; 62 | 63 | imageLoader.removeEventListener(Event.COMPLETE, onImageContent); 64 | imageLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); 65 | imageLoader = null; 66 | } 67 | 68 | private function onError(e:Event):void{ 69 | var imageLoader:LoaderInfo = e.target as LoaderInfo; 70 | imageLoader.removeEventListener(Event.COMPLETE, onImageContent); 71 | imageLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError); 72 | imageLoader = null; 73 | } 74 | 75 | private function itemrenderer1_creationCompleteHandler(event:FlexEvent):void 76 | { 77 | roundedMask = new Sprite(); 78 | var cornerRadius:uint = 10; 79 | roundedMask.graphics.clear(); 80 | roundedMask.graphics.beginFill(0xFF0000); 81 | roundedMask.graphics.drawRoundRect(0, 0, 120, 120, cornerRadius, cornerRadius); 82 | roundedMask.graphics.endFill(); 83 | } 84 | 85 | private function itemrenderer1_clickHandler(event:MouseEvent):void 86 | { 87 | if( event.target.parent.id == "playAlbum") return; 88 | 89 | FlexGlobals.topLevelApplication.currentAlbum = data as Album; 90 | FlexGlobals.topLevelApplication.changeView(FlexGlobals.topLevelApplication.albumInfoView); 91 | } 92 | 93 | private function image1_clickHandler(event:MouseEvent):void 94 | { 95 | FlexGlobals.topLevelApplication.musicPlayer.playAlbum(data as Album); 96 | } 97 | 98 | ]]> 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/mielophone/ui/itemrenderers/PlaylistSongItem.mxml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 132 | 134 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /src/logic/views/info/RadioView.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.playr.PlayrTrack; 4 | import com.codezen.util.CUtils; 5 | import com.greensock.TweenLite; 6 | 7 | import flash.events.Event; 8 | import flash.events.KeyboardEvent; 9 | import flash.events.MouseEvent; 10 | import flash.media.Sound; 11 | import flash.media.SoundChannel; 12 | import flash.media.SoundTransform; 13 | import flash.net.URLLoader; 14 | import flash.net.URLRequest; 15 | import flash.ui.Keyboard; 16 | 17 | import mx.collections.ArrayCollection; 18 | import mx.core.FlexGlobals; 19 | import mx.utils.ObjectUtil; 20 | 21 | /******************************************************/ 22 | /** IMAGES **/ 23 | /******************************************************/ 24 | [Bindable] 25 | [Embed(source="/assets/player/play.png")] 26 | private var playImg:Class; 27 | 28 | [Bindable] 29 | [Embed(source="/assets/player/pause.png")] 30 | private var pauseImg:Class; 31 | 32 | /******************************************************/ 33 | /** VARS **/ 34 | /******************************************************/ 35 | [Bindable] 36 | private var categoriesCollection:ArrayCollection; 37 | 38 | private var radioChannel:SoundChannel; 39 | private var radioSound:Sound; 40 | private var currentRadioURL:String; 41 | private var currentRadioTitle:String; 42 | private var isPlaying:Boolean; 43 | private var radioVolume:Number; 44 | 45 | public function initRadio():void{ 46 | isPlaying = false; 47 | currentRadioURL = null; 48 | radioVolume = 100; 49 | } 50 | 51 | public function doWork():void{ 52 | if(categoriesCollection != null){ 53 | this.dispatchEvent(new Event(Event.COMPLETE)); 54 | return; 55 | } 56 | 57 | var urlRequest:URLRequest = new URLRequest("http://mielophone.github.com/stations.xml"); 58 | 59 | var urlLoader:URLLoader = new URLLoader(); 60 | urlLoader.addEventListener(Event.COMPLETE, onStations); 61 | urlLoader.load(urlRequest); 62 | } 63 | 64 | private function onStations(e:Event):void{ 65 | e.target.removeEventListener(Event.COMPLETE, onStations); 66 | 67 | var xml:XML = new XML(e.target.data); 68 | var groups:XMLList = xml.group; 69 | 70 | categoriesCollection = new ArrayCollection(); 71 | 72 | var stations:Array; 73 | var stats:XMLList; 74 | var grp:XML, station:XML; 75 | for each(grp in groups){ 76 | stats = grp.station; 77 | 78 | stations = []; 79 | for each(station in stats){ 80 | stations.push({ 81 | title: station.title.text(), 82 | tag: station.tag.text(), 83 | cover: station.cover.text(), 84 | type: station.type.text(), 85 | url: station.url.text() 86 | }); 87 | } 88 | 89 | categoriesCollection.addItem({ 90 | id: grp.@id, 91 | title: grp.@title, 92 | stations: stations 93 | }); 94 | } 95 | 96 | categories.selectedIndex = 0; 97 | radioList.dataProvider = new ArrayCollection(categories.selectedItem.stations); 98 | 99 | this.dispatchEvent(new Event(Event.COMPLETE)); 100 | } 101 | 102 | private function onCategoryChange(e:Event):void{ 103 | if(categories.selectedIndex < 0) return; 104 | 105 | radioList.dataProvider = new ArrayCollection(categories.selectedItem.stations); 106 | } 107 | 108 | private function openRadioByURL():void{ 109 | streamURL.visible = true; 110 | openURL.visible = false; 111 | } 112 | 113 | private function onStreamURLKeyUp(e:KeyboardEvent):void{ 114 | if(e.keyCode == Keyboard.ESCAPE){ 115 | streamURL.visible = false; 116 | openURL.visible = true; 117 | streamURL.text = ''; 118 | }else if(e.keyCode == Keyboard.ENTER && streamURL.text.length > 2){ 119 | playRadioURL(streamURL.text, "Radio from URL"); 120 | streamURL.visible = false; 121 | openURL.visible = true; 122 | streamURL.text = ''; 123 | } 124 | } 125 | 126 | public function toggleRadio():void{ 127 | isPlaying = !isPlaying; 128 | 129 | if(isPlaying && currentRadioURL != null){ 130 | radioSound = new Sound(new URLRequest(currentRadioURL)); 131 | radioChannel = radioSound.play(); 132 | radioChannel.soundTransform = new SoundTransform(radioVolume/100); 133 | playRadio.source = pauseImg; 134 | currentRadio.text = currentRadioTitle; 135 | }else if(!isPlaying){ 136 | try{ 137 | radioSound.close(); 138 | radioSound = null; 139 | radioChannel.stop(); 140 | }catch(e:Error){} 141 | 142 | playRadio.source = playImg; 143 | currentRadio.text = "Radio is off"; 144 | } 145 | } 146 | 147 | public function playRadioURL(url:String, title:String):void{ 148 | // stop player if it's playing 149 | FlexGlobals.topLevelApplication.musicPlayer.stopPlayback(); 150 | 151 | // stop radion if it's playing 152 | if(isPlaying){ 153 | try{ 154 | radioSound.close(); 155 | radioSound = null; 156 | radioChannel.stop(); 157 | }catch(e:Error){} 158 | } 159 | 160 | // load new stream 161 | currentRadioURL = url; 162 | currentRadioTitle = currentRadio.text = title; 163 | isPlaying = true; 164 | playRadio.source = pauseImg; 165 | 166 | // play new stream 167 | radioSound = new Sound(new URLRequest(currentRadioURL)); 168 | radioChannel = radioSound.play(); 169 | radioChannel.soundTransform = new SoundTransform(radioVolume/100); 170 | } 171 | 172 | public function killRadio():void{ 173 | if(isPlaying){ 174 | try{ 175 | radioSound.close(); 176 | radioSound = null; 177 | radioChannel.stop(); 178 | }catch(e:Error){} 179 | 180 | playRadio.source = playImg; 181 | currentRadio.text = "Radio is off"; 182 | 183 | isPlaying = false; 184 | } 185 | } 186 | 187 | public function setVolume(v:Number):void{ 188 | radioVolume = v; 189 | 190 | if(radioChannel){ 191 | radioChannel.soundTransform = new SoundTransform(radioVolume/100); 192 | } 193 | } -------------------------------------------------------------------------------- /src/logic/ViewHandling.as: -------------------------------------------------------------------------------- 1 | import com.codezen.skins.scroll.SmallScroll; 2 | import com.codezen.skins.scroll.SmallVScrollThumb; 3 | import com.codezen.skins.scroll.SmallVScrollTrack; 4 | import com.greensock.TweenLite; 5 | 6 | import flash.events.ErrorEvent; 7 | import flash.events.Event; 8 | import flash.events.MouseEvent; 9 | import flash.events.TimerEvent; 10 | import flash.ui.Mouse; 11 | import flash.utils.Timer; 12 | 13 | import mielophone.ui.skins.MielophoneApplication; 14 | 15 | import mx.messaging.messages.ErrorMessage; 16 | import mx.utils.ObjectUtil; 17 | 18 | import spark.components.Group; 19 | 20 | /******************************************************/ 21 | /** VARS **/ 22 | /******************************************************/ 23 | // current view 24 | private var currentView:String; 25 | // views array 26 | private var viewHistory:Array; 27 | // 28 | public var animationEnabled:Boolean = true; 29 | // animation state 30 | private var isAnimating:Boolean; 31 | 32 | /******************************************************/ 33 | /** INITIALIZATION **/ 34 | /******************************************************/ 35 | private function initViewHelpers():void{ 36 | viewHistory = []; 37 | currentView = "homeView"; 38 | isAnimating = false; 39 | } 40 | 41 | /******************************************************/ 42 | /** BACK BUTTON HANDLING **/ 43 | /******************************************************/ 44 | 45 | public function navigateBack(home:Boolean = false):void{ 46 | if( viewHistory.length == 0 ) return; 47 | if( isAnimating ) return; 48 | 49 | // new view var 50 | var newView:String; 51 | 52 | // if home button pressed - go home 53 | if( home ){ 54 | // pop and hide all old views 55 | while(viewHistory.length > 1){ 56 | newView = viewHistory.pop(); 57 | this[newView].visible = false; 58 | } 59 | } 60 | 61 | // get new view string 62 | newView = viewHistory.pop(); 63 | 64 | // get current view 65 | var view:Group = this[currentView]; 66 | // reset size and position to absolute 67 | view.horizontalCenter = view.verticalCenter = 0; 68 | view.height = stage.stageHeight-64; 69 | view.width = nativeWindow.width; 70 | 71 | // reset focus 72 | this.setFocus(); 73 | 74 | // show new view 75 | this[newView].visible = true; 76 | 77 | // animate move-out 78 | if(animationEnabled){ 79 | isAnimating = true; 80 | TweenLite.to(view, 0.3, {width:nativeWindow.width-100, height:stage.height-100, onComplete:function():void{ 81 | TweenLite.to(view, 0.5, {horizontalCenter:nativeWindow.width, onComplete:function():void{ 82 | view.visible = false; 83 | isAnimating = false; 84 | }}); 85 | }}); 86 | }else{ 87 | view.visible = false; 88 | } 89 | 90 | currentView = newView; 91 | } 92 | 93 | /******************************************************/ 94 | /** VIEW CHANGING **/ 95 | /******************************************************/ 96 | 97 | public function changeView(view:*):void{ 98 | if( isAnimating ) return; 99 | 100 | // show loader 101 | loadingOn(); 102 | 103 | // do view work 104 | view.addEventListener(Event.COMPLETE, onViewWork); 105 | view.addEventListener(ErrorEvent.ERROR, onViewError); 106 | view.doWork(); 107 | } 108 | 109 | private function onViewError(e:ErrorEvent):void{ 110 | e.target.removeEventListener(Event.COMPLETE, onViewWork); 111 | e.target.removeEventListener(ErrorEvent.ERROR, onViewError); 112 | 113 | loadingOff(); 114 | } 115 | 116 | private function onViewWork(e:Event):void{ 117 | e.target.removeEventListener(Event.COMPLETE, onViewWork); 118 | 119 | // remove loading indicator 120 | loadingOff(); 121 | // get view 122 | var view:Group = e.target as Group; 123 | 124 | // if view is already active - do nothing; 125 | if(currentView == view.id) 126 | return; 127 | 128 | // reset focus 129 | this.setFocus(); 130 | 131 | // check if it's reverse 132 | var reverse:Boolean = this.getElementIndex(view) < this.getElementIndex(this[currentView]); 133 | 134 | if(reverse){ 135 | // remove old view 136 | viewHistory.pop(); 137 | 138 | // get current view 139 | var viewOld:Group = this[currentView]; 140 | // reset size and position to absolute 141 | viewOld.horizontalCenter = viewOld.verticalCenter = 0; 142 | viewOld.height = stage.stageHeight-64; 143 | viewOld.width = nativeWindow.width; 144 | 145 | // show new view 146 | view.x = view.y = 0; 147 | view.horizontalCenter = view.verticalCenter = 0; 148 | view.percentHeight = view.percentWidth = 100; 149 | view.visible = true; 150 | 151 | trace(view.horizontalCenter, view.id, view.visible); 152 | 153 | // animate move-out 154 | if(animationEnabled){ 155 | isAnimating = true; 156 | TweenLite.to(viewOld, 0.3, {width:nativeWindow.width-100, height:stage.height-100, onComplete:function():void{ 157 | TweenLite.to(viewOld, 0.5, {horizontalCenter:nativeWindow.width, onComplete:function():void{ 158 | viewOld.visible = false; 159 | viewOld.horizontalCenter = 0; 160 | isAnimating = false; 161 | }}); 162 | }}); 163 | }else{ 164 | viewOld.visible = false; 165 | viewOld.horizontalCenter = 0; 166 | } 167 | }else{ 168 | // save last view 169 | viewHistory.push(currentView); 170 | 171 | // position window outside 172 | //view.percentWidth = view.percentHeight = 95; 173 | view.width = this.stage.stageWidth - 100; 174 | view.height = this.stage.stageHeight - 100; 175 | view.horizontalCenter = this.stage.stageWidth; 176 | view.verticalCenter = 0; 177 | view.visible = true; 178 | 179 | // get old view 180 | var oldView:Group = this[viewHistory[viewHistory.length-1]] as Group; 181 | 182 | // animate move-in 183 | if(animationEnabled){ 184 | isAnimating = true; 185 | TweenLite.to(view, 0.5, {horizontalCenter:0, onComplete:function():void{ 186 | TweenLite.to(view, 0.3, {width:stage.stageWidth, height:stage.stageHeight-64, onComplete:function():void{ 187 | // set new params 188 | view.x = view.y = 0; 189 | view.percentHeight = view.percentWidth = 100; 190 | // hide old view 191 | oldView.visible = false; 192 | oldView.horizontalCenter = 0; 193 | // animation flag 194 | isAnimating = false; 195 | }}); 196 | }}); 197 | }else{ 198 | view.horizontalCenter = 0; 199 | // set new params 200 | view.x = view.y = 0; 201 | view.percentHeight = view.percentWidth = 100; 202 | // hide old view 203 | oldView.visible = false; 204 | oldView.horizontalCenter = 0; 205 | } 206 | } 207 | 208 | // set new current view 209 | currentView = view.id; 210 | } 211 | 212 | /******************************************************/ 213 | /** LOADING INDICATION **/ 214 | /******************************************************/ 215 | public function loadingOn():void{ 216 | (this.skin as MielophoneApplication).loadingIndicator.visible = (this.skin as MielophoneApplication).loadingIndicator.isLoading = true; 217 | } 218 | 219 | public function loadingOff():void{ 220 | (this.skin as MielophoneApplication).loadingIndicator.visible = (this.skin as MielophoneApplication).loadingIndicator.isLoading = false; 221 | } -------------------------------------------------------------------------------- /src/mielophone/ui/player/MusicPlayer.mxml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 54 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 73 | 74 | 76 | 77 | 78 | 81 | 84 | 87 | 88 | 89 | 90 | 91 | 94 | 95 | 96 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 121 | 122 | 125 | 126 | 129 | 130 | 131 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 149 | 150 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 163 | 165 | 166 | 167 | 169 | 170 | 171 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /src/Mielophone-app.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 18 | Mielophone 19 | 20 | 21 | Mielophone 22 | 23 | 25 | Mielophone 26 | 27 | 30 | 0.3.2 31 | 32 | 33 | v0.3.2 34 | 35 | 37 | Minimalistic open source music discovery application 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | [This value will be overwritten by Flash Builder in the output app.xml] 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | false 109 | false 110 | false 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 128 | 129 | assets/logo/logo-16.png 130 | assets/logo/logo-32.png 131 | assets/logo/logo-36.png 132 | assets/logo/logo-48.png 133 | assets/logo/logo-72.png 134 | assets/logo/logo-114.png 135 | assets/logo/logo-128.png 136 | 137 | 138 | 141 | 142 | 143 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 192 | 193 | 194 | 195 | 196 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /src/logic/player/Player.as: -------------------------------------------------------------------------------- 1 | 2 | import com.codezen.mse.MusicSearchEngine; 3 | import com.codezen.mse.models.Album; 4 | import com.codezen.mse.models.Artist; 5 | import com.codezen.mse.models.Song; 6 | import com.codezen.mse.playr.PlaylistManager; 7 | import com.codezen.mse.playr.Playr; 8 | import com.codezen.mse.playr.PlayrEvent; 9 | import com.codezen.mse.playr.PlayrStates; 10 | import com.codezen.mse.playr.PlayrTrack; 11 | import com.codezen.mse.services.LastFM; 12 | import com.codezen.mse.services.LastfmScrobbler; 13 | import com.codezen.mse.services.MusicBrainz; 14 | import com.codezen.util.CUtils; 15 | import com.greensock.TweenLite; 16 | 17 | import flash.errors.EOFError; 18 | import flash.errors.IOError; 19 | import flash.events.ErrorEvent; 20 | import flash.events.Event; 21 | import flash.events.IOErrorEvent; 22 | import flash.events.MouseEvent; 23 | import flash.events.TimerEvent; 24 | import flash.net.SharedObject; 25 | import flash.net.URLRequest; 26 | import flash.utils.Timer; 27 | 28 | import mx.collections.ArrayCollection; 29 | import mx.controls.Alert; 30 | import mx.core.FlexGlobals; 31 | import mx.events.FlexEvent; 32 | import mx.utils.ObjectUtil; 33 | 34 | import spark.components.Group; 35 | import spark.utils.TextFlowUtil; 36 | 37 | /******************************************************/ 38 | /** INIT STUFF **/ 39 | /******************************************************/ 40 | public function initPlayer():void{ 41 | playerRepeat = playerShuffle = false; 42 | isFullMode = false; 43 | prefetchedNext = false; 44 | fbSongPosted = false; 45 | 46 | playPos = nextRandomPos = -1; 47 | 48 | mse = FlexGlobals.topLevelApplication.mse; 49 | 50 | player = new Playr(); 51 | // add events 52 | player.addEventListener(PlayrEvent.PLAYRSTATE_CHANGED, onPlayerState); 53 | player.addEventListener(PlayrEvent.TRACK_PROGRESS, onProgress); 54 | player.addEventListener(PlayrEvent.STREAM_PROGRESS, onStreamProgress); 55 | player.addEventListener(PlayrEvent.SONGINFO, onSong); 56 | player.addEventListener(PlayrEvent.TRACK_COMPLETE, onTrackEnd); 57 | // load player settings 58 | playerSettings = SharedObject.getLocal("mielophone.player"); 59 | // buffer 60 | if( playerSettings.data.buffer != null ){ 61 | player.buffer = playerSettings.data.buffer; 62 | FlexGlobals.topLevelApplication.settingsView.bufferingSlider.value = playerSettings.data.buffer / 1000; 63 | } 64 | // volume 65 | if( playerSettings.data.volume != null ){ 66 | playerVolume = playerSettings.data.volume; 67 | player.volume = playerVolume/100; 68 | volumeSlider.value = playerVolume; 69 | }else{ 70 | playerVolume = 100; 71 | } 72 | // behavior 73 | if( playerSettings.data.behavior != null ){ 74 | playerBehavior = playerSettings.data.behavior; 75 | FlexGlobals.topLevelApplication.settingsView.playlistBehavior.selectedIndex = playerSettings.data.behaviorIndex; 76 | }else{ 77 | playerBehavior = PLAYLIST_IGNORE; 78 | } 79 | // playlist 80 | if( playerSettings.data.playlist != null ){ 81 | playQueue = []; 82 | 83 | var s:Song; 84 | var o:Object; 85 | for each(o in playerSettings.data.playlist){ 86 | s = new Song(); 87 | s.duration = o.duration; 88 | s.durationText = o.durationText; 89 | s.name = o.name; 90 | s.number = o.number; 91 | s.artist = new Artist(); 92 | s.artist.name = o.artist.name; 93 | s.artist.mbID = o.artist.mbID; 94 | 95 | playQueue.push(s); 96 | } 97 | songList.dataProvider = new ArrayCollection(playQueue); 98 | }else{ 99 | playQueue = []; 100 | } 101 | 102 | timeSlider.slider.addEventListener(FlexEvent.CHANGE_END, onSeek); 103 | timeSlider.slider.dataTipFormatFunction = timeDataTip; 104 | 105 | // load scrobbling settings 106 | scrobblerSettings = SharedObject.getLocal("mielophone.scrobbling"); 107 | if( scrobblerSettings.data.username != null ){ 108 | scrobbleName = scrobblerSettings.data.username; 109 | scrobblePass = scrobblerSettings.data.pass; 110 | FlexGlobals.topLevelApplication.settingsView.lastfmLogin.text = scrobbleName; 111 | FlexGlobals.topLevelApplication.settingsView.lastfmPass.text = scrobblePass; 112 | 113 | // scrobbling 114 | initScrobbler(); 115 | } 116 | } 117 | 118 | public function initScrobbler():void{ 119 | if(scrobbleName.length > 1 && scrobblePass.length > 1){ 120 | trace("scrobble init"); 121 | scrobbler = new LastfmScrobbler("0b18095c48d2bb8bf4acbab629bcc30e", "536549b8e66a766fe3de7d61a0fa7390"); 122 | scrobbler.addEventListener(ErrorEvent.ERROR, function():void{ 123 | Alert.show("Some error in scrobbling class! Wrong login/pass?", "Scrobbling error!"); 124 | }); 125 | scrobbler.addEventListener(Event.INIT, function(e:Event):void{ 126 | trace('scrobble inited'); 127 | FlexGlobals.topLevelApplication.settingsView.lastfmIcon.alpha = 1; 128 | }); 129 | scrobbler.auth(scrobbleName, scrobblePass); 130 | } 131 | } 132 | 133 | /** 134 | * Data tip for time slider 135 | * @param val 136 | * @return 137 | * 138 | */ 139 | private function timeDataTip(val:String):String{ 140 | var duration:Number = Number(val); 141 | 142 | return CUtils.secondsToString(duration); 143 | } 144 | 145 | /******************************************************/ 146 | /** PLAYER FUNCS **/ 147 | /******************************************************/ 148 | 149 | public function setBuffer(b:int):void{ 150 | player.buffer = b; 151 | playerSettings.data.buffer = b; 152 | playerSettings.flush(); 153 | } 154 | 155 | public function setPlaylistBehavior(behavior:String, index:int):void{ 156 | playerBehavior = behavior; 157 | playerSettings.data.behavior = behavior; 158 | playerSettings.data.behaviorIndex = index; 159 | playerSettings.flush(); 160 | } 161 | 162 | public function setScrobblingAuth(login:String, pass:String):void{ 163 | scrobblerSettings.data.username = scrobbleName = login; 164 | scrobblerSettings.data.pass = scrobblePass = pass; 165 | scrobblerSettings.flush(); 166 | 167 | if(login.length > 0 && pass.length > 0) initScrobbler(); 168 | } 169 | 170 | public function togglePlayPause():void{ 171 | player.togglePlayPause(); 172 | 173 | if(playQueue.length > 0 && player.playrState == PlayrStates.WAITING && playPos == -1){ 174 | playSongByNum(0); 175 | } 176 | } 177 | 178 | public function pausePlayback():void{ 179 | player.pause(); 180 | } 181 | 182 | public function resumePlayback():void{ 183 | player.play(); 184 | } 185 | 186 | public function stopPlayback():void{ 187 | player.stop(); 188 | } 189 | 190 | public function playNext():void{ 191 | findNextSong(); 192 | } 193 | 194 | public function deleteSongFromPlaylist(index:int):void{ 195 | playQueue.splice(index,1); 196 | if(playPos == index) playPos = -1; 197 | 198 | songList.dataProvider = new ArrayCollection(playQueue); 199 | 200 | storePlaylist(); 201 | } 202 | 203 | public function addSongToPlaylist(s:Song):void{ 204 | playQueue.push(s); 205 | 206 | songList.dataProvider = new ArrayCollection(playQueue); 207 | 208 | storePlaylist(); 209 | } 210 | 211 | public function getCurrentTrack():PlayrTrack{ 212 | return player.playlist.getCurrentTrack(); 213 | } 214 | 215 | /******************************************************/ 216 | /** SEARCH AND PLAY **/ 217 | /******************************************************/ 218 | public function playSongByNum(num:int):void{ 219 | playPos = num; 220 | // notify about index change 221 | this.dispatchEvent(new Event(Event.CHANGE)); 222 | 223 | //albumCover.source = nocoverImg; 224 | nowPlayingText.text = "Searching for stream.."; 225 | if(playQueue[playPos].track == null){ 226 | nowSearching = true; 227 | mse.addEventListener(Event.COMPLETE, onSongLinks); 228 | mse.findMP3(playQueue[playPos] as Song); 229 | }else{ 230 | playSong(playQueue[playPos].track); 231 | } 232 | //findSongAndPlay(playQueue[playPos] as Song); 233 | } 234 | 235 | public function findNextSong():void{ 236 | trace('next song'); 237 | if(nowSearching) return; 238 | if(playQueue == null || playQueue.length < 1) return; 239 | 240 | nowSearching = true; 241 | if( playerShuffle ){ 242 | playPos = nextRandomPos == -1 ? Math.round( playQueue.length * Math.random() ) : nextRandomPos; 243 | nextRandomPos = -1; 244 | }else{ 245 | playPos++; 246 | } 247 | 248 | // notify about index change 249 | this.dispatchEvent(new Event(Event.CHANGE)); 250 | 251 | if(playPos < 0 || playPos >= playQueue.length){ 252 | if(playerRepeat){ 253 | playPos = 0; 254 | findSongAndPlay(playQueue[playPos] as Song); 255 | }else{ 256 | nowSearching = false; 257 | } 258 | }else{ 259 | findSongAndPlay(playQueue[playPos] as Song); 260 | } 261 | } 262 | 263 | public function findPrevSong():void{ 264 | trace('prev song'); 265 | if(nowSearching) return; 266 | if(playQueue == null || playQueue.length < 1) return; 267 | 268 | nowSearching = true; 269 | 270 | if( playerShuffle ){ 271 | playPos = Math.round( playQueue.length * Math.random() ); 272 | }else{ 273 | playPos--; 274 | } 275 | 276 | // notify about index change 277 | this.dispatchEvent(new Event(Event.CHANGE)); 278 | 279 | if(playPos < 0){ 280 | playPos = -1; 281 | nowSearching = false; 282 | }else{ 283 | findSongAndPlay(playQueue[playPos] as Song); 284 | } 285 | } 286 | 287 | public function findSongAndPlay(song:Song):void{ 288 | // check is song is already in queue 289 | var i:int, inqueue:Boolean; 290 | for(i = 0; i < playQueue.length; i++){ 291 | if(playQueue[i] == song){ 292 | trace('match'); 293 | inqueue = true; 294 | break; 295 | } 296 | } 297 | // if song there, just play it 298 | if(inqueue){ 299 | playPos = i; 300 | 301 | // notify about index change 302 | this.dispatchEvent(new Event(Event.CHANGE)); 303 | 304 | //albumCover.source = nocoverImg; 305 | nowPlayingText.text = "Searching for stream.."; 306 | 307 | if(song.track == null){ 308 | nowSearching = true; 309 | mse.addEventListener(Event.COMPLETE, onSongLinks); 310 | mse.findMP3(song); 311 | }else{ 312 | playSong(song.track); 313 | } 314 | return; 315 | } 316 | 317 | switch(playerBehavior) 318 | { 319 | case PLAYLIST_APPEND: 320 | var startPlay:Boolean; 321 | if(playQueue == null || playQueue.length < 1){ 322 | playPos = -1; 323 | startPlay = true; 324 | } 325 | playQueue.push(song); 326 | songList.dataProvider = new ArrayCollection(playQueue); 327 | storePlaylist(); 328 | if(startPlay) findNextSong(); 329 | break; 330 | 331 | case PLAYLIST_CLEAR: 332 | playQueue = [song]; 333 | playPos = -1; 334 | songList.dataProvider = new ArrayCollection(playQueue); 335 | storePlaylist(); 336 | case PLAYLIST_IGNORE: 337 | default: 338 | //albumCover.source = nocoverImg; 339 | nowPlayingText.text = "Searching for stream.."; 340 | if(song.track == null){ 341 | nowSearching = true; 342 | mse.addEventListener(Event.COMPLETE, onSongLinks); 343 | mse.findMP3(song); 344 | }else{ 345 | playSong(song.track); 346 | } 347 | break; 348 | } 349 | } 350 | 351 | private function onSongLinks(e:Event):void{ 352 | mse.removeEventListener(Event.COMPLETE, onSongLinks); 353 | 354 | nowSearching = false; 355 | 356 | if( mse.mp3s.length == 0 ){ 357 | trace('nothing :('); 358 | playQueue[playPos].number = -100; 359 | findNextSong(); 360 | return; 361 | } 362 | 363 | playSong(mse.mp3s[0] as PlayrTrack); 364 | } 365 | 366 | private function playSong(song:PlayrTrack):void{ 367 | nowSearching = false; 368 | 369 | // kill radio if it's playing 370 | FlexGlobals.topLevelApplication.radioView.killRadio(); 371 | 372 | // create playlist for song 373 | var pl:PlaylistManager = new PlaylistManager(); 374 | pl.addTrack(song); 375 | 376 | fbSongPosted = prefetchedNext = trackScrobbled = false; 377 | 378 | player.stop(); 379 | player.playlist = pl; 380 | player.play(); 381 | player.volume = playerVolume/100; 382 | } 383 | 384 | /******************************************************/ 385 | /** ALBUM PLAYBACK **/ 386 | /******************************************************/ 387 | public function playCurrentAlbum():void{ 388 | var startPlay:Boolean; 389 | 390 | switch(playerBehavior) 391 | { 392 | case PLAYLIST_APPEND: 393 | if(playQueue == null || playQueue.length < 1){ 394 | playPos = -1; 395 | startPlay = true; 396 | } 397 | playQueue = playQueue.concat(FlexGlobals.topLevelApplication.currentAlbum.songs); 398 | songList.dataProvider = new ArrayCollection(playQueue); 399 | storePlaylist(); 400 | if(startPlay) findNextSong(); 401 | break; 402 | 403 | case PLAYLIST_CLEAR: 404 | case PLAYLIST_IGNORE: 405 | playQueue = FlexGlobals.topLevelApplication.currentAlbum.songs.concat(); 406 | playPos = -1; 407 | 408 | songList.dataProvider = new ArrayCollection(playQueue); 409 | 410 | storePlaylist(); 411 | findNextSong(); 412 | break; 413 | } 414 | } 415 | 416 | public function playAlbum(album:Album):void{ 417 | FlexGlobals.topLevelApplication.currentAlbum = album; 418 | 419 | mse.addEventListener(Event.COMPLETE, onAlbumTracks); 420 | mse.getAlbumTracks(album); 421 | } 422 | 423 | private function onAlbumTracks(e:Event):void{ 424 | mse.removeEventListener(Event.COMPLETE, onAlbumTracks); 425 | 426 | FlexGlobals.topLevelApplication.currentAlbum.songs = mse.album.songs; 427 | 428 | playCurrentAlbum(); 429 | } 430 | 431 | public function setQueue(ac:Array):void{ 432 | var startPlay:Boolean = false; 433 | 434 | switch(playerBehavior) 435 | { 436 | case PLAYLIST_APPEND: 437 | if(playQueue == null || playQueue.length < 1){ 438 | playPos = -1; 439 | startPlay = true; 440 | } 441 | playQueue = playQueue.concat(ac); 442 | break; 443 | 444 | case PLAYLIST_CLEAR: 445 | case PLAYLIST_IGNORE: 446 | playQueue = ac.concat(); 447 | playPos = -1; 448 | startPlay = true; 449 | break; 450 | } 451 | 452 | songList.dataProvider = new ArrayCollection(playQueue); 453 | 454 | storePlaylist(); 455 | 456 | if(startPlay) findNextSong(); 457 | } --------------------------------------------------------------------------------