/// <summary> /// /// </summary> /// <param name="track"></param> /// <param name="parent"></param> public TrayNotification(TrackData track, StoffiWindow parent) { ParentWindow = parent; InitializeComponent(); TrackArtist.Text = track.Artist; TrackTitle.Text = track.Title; AlbumArt.Source = Utilities.GetImageTag(track); }
/// <summary> /// Copies a TrackData into a ConfigTrack /// </summary> /// <param name="track">The track to be copied</param> /// <returns>The track as a configuration structure</returns> private static ConfigTrack ExportTrack(TrackData track) { if (track == null) return null; return new ConfigTrack() { Path = track.Path, PlayCount = track.PlayCount, LastPlayed = track.LastPlayed }; }
/// <summary> /// Finds all playlists that contains a given track. /// </summary> /// <param name="track">The track to look for</param> /// <returns>All playlists containing <paramref name="track"/></returns> public static List<PlaylistData> Has(TrackData track) { List<PlaylistData> has = new List<PlaylistData>(); foreach (PlaylistData p in SettingsManager.Playlists) if (Contains(p, track)) has.Add(p); return has; }
/// <summary> /// Checks whether a given track is a youtube track /// </summary> /// <param name="t">The track to check</param> /// <returns>True if the track is a youtube track</returns> public static bool IsYouTube(TrackData t) { return (t != null && IsYouTube(t.Path)); }
/// <summary> /// Retrieves the URL to the thumbnail for a YouTube track /// </summary> /// <param name="track">The YouTube track</param> public static string GetThumbnail(TrackData track) { if (IsYouTube(track)) return "https://img.youtube.com/vi/" + GetYouTubeID(track.Path) + "/1.jpg"; else return ""; }
/// <summary> /// Encodes a track to a JSON object. /// </summary> /// <param name="track">The track to encode</param> /// <returns>A JSON object</returns> private static JObject TrackToJSON(TrackData track) { JObject json = new JObject(); if (track != null) { json.Add("title", U.EscapeJSON(track.Title)); json.Add("artist", U.EscapeJSON(track.Artist)); json.Add("art_url", U.EscapeJSON(track.ArtURL)); json.Add("foreign_url", U.EscapeJSON(track.URL)); json.Add("genre", U.EscapeJSON(track.Genre)); json.Add("length", U.EscapeJSON(track.Length)); switch (MediaManager.GetType(track)) { case TrackType.File: json.Add("path", U.EscapeJSON(Path.GetFileName(track.Path))); break; default: json.Add("path", U.EscapeJSON(track.Path)); break; } } return json; }
/// <summary> /// Invoked when the media manager starts playing a track. /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event data</param> private static void MediaManager_Started(object sender, EventArgs e) { if (SettingsManager.MediaState == MediaState.Playing && SettingsManager.CurrentTrack != null && (currentListen == null || SettingsManager.CurrentTrack.Path != currentListen.Path)) { uint oldListen = idOfCurrentListen; idOfCurrentListen = 0; if (oldListen > 0 && stampOfCurrentListen != null) { DateTime stamp = stampOfCurrentListen.Value; TimeSpan diff = DateTime.Now - stamp; if (diff.TotalSeconds >= minimumListenTime) EndListen(oldListen); else DeleteListen(oldListen); } stampOfCurrentListen = DateTime.Now; currentListen = SettingsManager.CurrentTrack; StartListen(); } }
/// <summary> /// Shares a track. /// </summary> /// <param name="track">The track to share</param> public static void ShareSong(TrackData track) { ThreadStart shareThread = delegate() { string path = track.Path; if (MediaManager.GetType(track) == TrackType.File) path = Path.GetFileName(track.Path); string query = String.Format("?{0}&object=song", String.Join("&", EncodeTrack(track, "track"))); var response = SendRequest("/shares.json", "POST", query); if (response == null || response.StatusCode != HttpStatusCode.Created) { U.L(LogLevel.Error, "SERVICE", "There was a problem sharing song " + track.Artist + " - " + track.Title); U.L(LogLevel.Error, "SERVICE", response); } else { U.L(LogLevel.Information, "SERVICE", "Shared song "+track.Artist + " - " + track.Title); } if (response != null) response.Close(); }; Thread sharethread = new Thread(shareThread); sharethread.Name = "Share thread"; sharethread.Priority = ThreadPriority.Lowest; sharethread.Start(); }
/// <summary> /// Finds the track inside the library from which a given track originates /// </summary> /// <param name="track">The track find the library source for</param> /// <returns>The track that was the original source, inside the library, for <b>track</b>, or <b>track</b> if no such track was found</returns> public static TrackData GetLibrarySourceTrack(TrackData track) { foreach (TrackData t in SettingsManager.FileTracks) if (t.Path == track.Path) return t; return track; }
/// <summary> /// Creates an instance of the TrackSwitchedEventArgs class /// </summary> /// <param name="oldTrack">The track before the switch</param> /// <param name="newTrack">The track after the switch</param> public TrackSwitchedEventArgs(TrackData oldTrack, TrackData newTrack) { OldTrack = oldTrack; NewTrack = newTrack; }
/// <summary> /// Starts playback of the media player. /// </summary> /// <param name="track">The track to start playing</param> private static void Start(TrackData track) { SettingsManager.MediaState = MediaState.Playing; SettingsManager.CurrentTrack = track; //U.L(LogLevel.Information, "MEDIA", "Playing " + track.Path); ThreadStart PlayThread = delegate() { trackWasSkipped = false; // add song to played/recent list songsAlreadyPlayed.Add(track); TrackType type = GetType(track); bool skip = false; lock (locker) { if (stream != 0) { Bass.BASS_Stop(); Bass.BASS_StreamFree(stream); stream = 0; } if (YouTubeManager.HasFlash) InvokeScript("pause"); switch (type) { case TrackType.YouTube: string vid = YouTubeManager.GetYouTubeID(track.Path); InvokeScript("setVolume", new object[] { SettingsManager.Volume }); InvokeScript("setQuality", new object[] { SettingsManager.YouTubeQuality }); SettingsManager.MediaState = MediaState.Stopped; InvokeScript("loadNewVideo", new object[] { vid, 0 }); break; case TrackType.File: stream = Bass.BASS_StreamCreateFile(track.Path, 0, 0, BASSFlag.BASS_SAMPLE_FLOAT); break; case TrackType.SoundCloud: stream = Bass.BASS_StreamCreateURL(SoundCloudManager.GetStreamURL(track), 0, BASSFlag.BASS_SAMPLE_FLOAT, null, IntPtr.Zero); break; case TrackType.WebRadio: stream = Bass.BASS_StreamCreateURL(track.Path, 0, BASSFlag.BASS_SAMPLE_FLOAT, null, IntPtr.Zero); break; default: U.L(LogLevel.Error, "MEDIA", "Unsupported track type: " + type); SettingsManager.CurrentTrack = track; skip = true; break; } if (stream != 0) { Bass.BASS_ChannelRemoveSync(stream, syncer); syncer = Bass.BASS_ChannelSetSync(stream, BASSSync.BASS_SYNC_END, 0, sync, IntPtr.Zero); if (type == TrackType.File) { double p = SettingsManager.Seek; Bass.BASS_ChannelSetPosition(stream, (long)((p / 10.0) * Bass.BASS_ChannelGetLength(stream))); } Bass.BASS_Start(); Bass.BASS_ChannelPlay(stream, false); // set some fx SetFX(); } if (stream == 0 && type != TrackType.YouTube) { U.L(LogLevel.Error, "MEDIA", "Could not start track: " + track.Path); skip = true; } else { loadedTrack = null; PlaylistManager.TrackWasPlayed(SettingsManager.CurrentTrack); DispatchStarted(); } } if (skip) Next(false, true); }; Thread play_thread = new Thread(PlayThread); play_thread.Name = "Playback"; play_thread.IsBackground = true; play_thread.Priority = ThreadPriority.BelowNormal; play_thread.Start(); }
/// <summary> /// The dispatcher of the <see cref="TrackSwitched"/> event. /// </summary> /// <param name="oldTrack">The track before the switch</param> /// <param name="newTrack">The track after the switch</param> private static void DispatchTrackSwitched(TrackData oldTrack, TrackData newTrack) { if (TrackSwitched != null) TrackSwitched(new TrackSwitchedEventArgs(oldTrack, newTrack)); }
/// <summary> /// The dispatcher of the <see cref="LoadedTrack"/> event. /// </summary> /// <param name="track">The track that was loaded</param> private static void DispatchLoadedTrack(TrackData track) { if (LoadedTrack != null) LoadedTrack(track); }
/// <summary> /// Stops playback /// </summary> public static void Stop() { SettingsManager.CurrentTrack = null; loadedTrack = null; if (YouTubeManager.HasFlash) InvokeScript("pause"); SettingsManager.MediaState = MediaState.Stopped; SettingsManager.Seek = 0; Bass.BASS_Stop(); Bass.BASS_StreamFree(stream); stream = 0; }
/// <summary> /// Plays the currently loaded track if there is one /// or continue playing current track if there is one /// </summary> public static void Play() { if (SettingsManager.MediaState == MediaState.Playing) return; if (loadedTrack != null) Start(loadedTrack); else if (SettingsManager.CurrentTrack != null) { if (YouTubeManager.IsYouTube(SettingsManager.CurrentTrack)) { if (YouTubeManager.HasFlash) InvokeScript("play"); } else if (stream != 0) { Bass.BASS_Start(); SettingsManager.MediaState = MediaState.Playing; } else { Start(SettingsManager.CurrentTrack); return; } StartTicker(); loadedTrack = null; DispatchStarted(); } }
public void SetTrack(TrackData track) { TrackArtist.Text = track.Artist; TrackTitle.Text = track.Title; AlbumArt.Source = Utilities.GetImageTag(track); }
/// <summary> /// Gets the value of a specific metadata tag given its name /// </summary> /// <param name="track">The track to retrieve the tag from</param> /// <param name="tag">The name of the tag</param> /// <returns>The value of the tag</returns> private object GetTag(TrackData track, string tag) { switch (tag) { case "Artist": return track.Artist as object; case "Title": return track.Title as object; case "Album": return track.Album as object; case "Genre": return track.Genre as object; case "Track": return track.Track as object; case "Year": if (track.Year == null) return null; object y = track.Year as object; return track.Year as object; default: return "" as object; } }
/// <summary> /// Finds the original source track, inside the library or a playlist, from which a given track originates /// </summary> /// <param name="track">The track to find the source for</param> /// <returns>The track that was the original source for <b>track</b>, or <b>track</b> if no such track was found</returns> public static TrackData GetSourceTrack(TrackData track) { if (track == null) return null; if (track.Source != null && track.Source.StartsWith("Playlist:")) { PlaylistData p = PlaylistManager.FindPlaylist(track.Source.Split(new[]{':'},2)[1]); if (p != null) foreach (TrackData t in p.Tracks) if (t.Path == track.Path) return t; } ObservableCollection<TrackData> tracks = null; switch (MediaManager.GetType(track)) { case TrackType.YouTube: tracks = YouTubeManager.TrackSource; break; case TrackType.SoundCloud: tracks = SoundCloudManager.TrackSource; break; case TrackType.WebRadio: tracks = SettingsManager.RadioTracks; break; case TrackType.File: tracks = SettingsManager.FileTracks; break; } if (tracks != null) for (int i = 0; i < tracks.Count; i++) { if (tracks[i].Path == track.Path) return tracks[i]; } return track; }
/// <summary> /// Encodes a track object into HTTP parameters. /// </summary> /// <param name="track">The track to encode</param> /// <param name="prefix">An optional prefix to put on each property</param> /// <returns>The track encoded as title=X, artist=Y, genre=Z...</returns> private static List<string> EncodeTrack(TrackData track, string prefix = "") { List<string> paras = new List<string>(); if (track != null) { paras.Add(U.CreateParam("title", track.Title, prefix)); paras.Add(U.CreateParam("artist", track.Artist, prefix)); paras.Add(U.CreateParam("art_url", track.ArtURL, prefix)); paras.Add(U.CreateParam("foreign_url", track.URL, prefix)); paras.Add(U.CreateParam("genre", track.Genre, prefix)); paras.Add(U.CreateParam("length", track.Length, prefix)); switch (MediaManager.GetType(track)) { case TrackType.File: paras.Add(U.CreateParam("path", Path.GetFileName(track.Path), prefix)); break; default: paras.Add(U.CreateParam("path", track.Path, prefix)); break; } } return paras; }
/// <summary> /// Gets the type of a track. /// </summary> /// <param name="track">The track to get the type of</param> /// <returns>The type of the given track</returns> public static TrackType GetType(TrackData track) { if (track != null) return GetType(track.Path); else return TrackType.Unknown; }
/// <summary> /// Invoked when a property of the settings manager is changed /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event data</param> private static void SettingsManager_PropertyChanged(object sender, PropertyChangedWithValuesEventArgs e) { if (!Linked || Identity == null) return; // create query string switch (e.PropertyName) { case "Volume": if (SynchronizeConfiguration) PushConfigUpdate("volume", Convert.ToString(SettingsManager.Volume)); break; case "Repeat": if (SynchronizeConfiguration) PushConfigUpdate("repeat", SettingsManager.Repeat.ToString()); break; case "Shuffle": if (SynchronizeConfiguration) PushConfigUpdate("shuffle", SettingsManager.Shuffle ? "Random" : "Off"); break; case "MediaState": if (SynchronizeConfiguration) PushConfigUpdate("media_state", SettingsManager.MediaState == MediaState.Playing ? "Playing" : "Paused"); if (SettingsManager.MediaState == MediaState.Stopped) currentListen = null; if (SettingsManager.CurrentTrack != null) { string path = SettingsManager.CurrentTrack.Path; if (listenQueue.ContainsKey(path)) { // listen has been submitted but no reply yet listenQueue[path] = new Tuple<bool, DateTime>(false, listenQueue[path].Item2); } else { if (SettingsManager.MediaState == MediaState.Playing) { if (idOfCurrentListen <= 0) { // no listen submitted yet stampOfCurrentListen = DateTime.Now; StartListen(); } else { // update listen to reflect new exepcted end time double pos = MediaManager.Position; double len = MediaManager.Length; DateTime expectedEnd = DateTime.UtcNow.AddSeconds(len - pos); UpdateListen(expectedEnd); } } else if (idOfCurrentListen > 0) { // paused DateTime stamp = stampOfCurrentListen.Value; TimeSpan diff = DateTime.Now - stamp; if (diff.TotalSeconds >= minimumListenTime) UpdateListen(DateTime.Now); else DeleteListen(idOfCurrentListen); } } } else if (SettingsManager.CurrentTrack != null) { string path = SettingsManager.CurrentTrack.Path; if (listenQueue.ContainsKey(path)) { // listen has been submitted but no reply yet DateTime stamp = listenQueue[path].Item2; TimeSpan diff = DateTime.Now - stamp; if (diff.TotalSeconds < minimumListenTime) listenQueue[path] = new Tuple<bool, DateTime>(true, stamp); } else { if (idOfCurrentListen <= 0) return; // update listen to reflect exepcted end time UpdateListen(DateTime.UtcNow); } } break; case "CurrentTrack": if (SynchronizeConfiguration) PushConfigUpdate("current_track", TrackToJSON(SettingsManager.CurrentTrack)); break; case "SyncPlaylists": SyncPlaylists(); break; case "SyncConfig": SyncConfig(); break; default: return; } }
/// <summary> /// Returns a localized string describing the type of the track /// </summary> /// <param name="track">The track</param> /// <param name="plural">Whether or not plural is used</param> /// <returns>A localized string describing the type of the track</returns> public static string HumanTrackType(TrackData track, bool plural = false) { string t = plural ? "Plural" : "Text"; switch (GetType(track)) { case TrackType.YouTube: return U.T("FileTypeYouTube", t); case TrackType.WebRadio: return U.T("FileTypeRadio", t); case TrackType.SoundCloud: return U.T("FileTypeSoundCloud", t); case TrackType.File: string ext = Path.GetExtension(track.Path).ToUpper().Substring(1); return String.Format(U.T("FileTypeExtension", t), ext); default: return U.T("FileTypeUnknown", t); } }
/// <summary> /// Creates a track using a YouTube video entry /// </summary> /// <param name="v">The video entry</param> /// <returns>A TrackData structure representing the YouTube track</returns> public static TrackData CreateTrack(Video v) { TrackData track = new TrackData(); track.Path = pathPrefix + v.VideoId; track.Icon = "pack://application:,,,/Platform/Windows 7/GUI/Images/Icons/YouTube.ico"; track.Bookmarks = new List<double>(); track.Processed = true; track.Length = Convert.ToDouble(v.Media.Duration.Seconds); string[] str = U.ParseTitle(v.Title); track.Artist = str[0]; track.Title = str[1]; track.IsActive = SettingsManager.CurrentTrack != null && SettingsManager.CurrentTrack.Path == track.Path; if (String.IsNullOrWhiteSpace(track.Artist)) track.Artist = v.Uploader; track.Views = v.ViewCount; track.URL = "https://www.youtube.com/watch?v=" + v.VideoId; return track; }
/// <summary> /// Initialize the manager /// </summary> public static void Initialize() { U.L(LogLevel.Debug, "MEDIA", "Init BASS"); FFTData = new float[1024]; BassNet.Registration("*****@*****.**", "2X2313734152222"); StartTicker(); sync = new SYNCPROC(EndPosition); if (SettingsManager.HistoryIndex > SettingsManager.HistoryTracks.Count) SettingsManager.HistoryIndex = SettingsManager.HistoryTracks.Count - 1; Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_DEV_DEFAULT, true); Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_FLOATDSP, true); Bass.BASS_Init(-1, 44100, 0, (IntPtr)0); U.L(LogLevel.Debug, "MEDIA", "Load codecs"); // load codecs string codecDir = Path.Combine(U.BasePath, "Codecs"); Dictionary<int, string> loadedPlugins = Bass.BASS_PluginLoadDirectory(codecDir); supportedFormatsFilter = Utils.BASSAddOnGetSupportedFileFilter(loadedPlugins, "All supported audio files", true); supportedFormatsExtensions = Utils.BASSAddOnGetSupportedFileExtensions(loadedPlugins, true); // remove .mov and .mp4 cause they don't work anyway :( supportedFormatsExtensions = SupportedFormatsExtensions.Replace("*.mov;", ""); supportedFormatsExtensions = SupportedFormatsExtensions.Replace("*.mp4;", ""); IsInitialized = true; SettingsManager.PropertyChanged += SettingsManager_PropertyChanged; SettingsManager.MediaState = MediaState.Paused; loadedTrack = SettingsManager.CurrentTrack; // soundcloud doesn't support starting to stream from anything other than beginning switch (GetType(SettingsManager.CurrentTrack)) { case TrackType.SoundCloud: case TrackType.WebRadio: SettingsManager.Seek = 0; break; } // seems we need to call this in order for the FX to be applied BassFx.BASS_FX_GetVersion(); U.L(LogLevel.Debug, "MEDIA", "Initialized"); }
/// <summary> /// Retrieves the URL for a YouTube track /// </summary> /// <param name="track">The YouTube track</param> public static string GetURL(TrackData track) { if (IsYouTube(track)) return "https://www.youtube.com/watch?v=" + GetYouTubeID(track.Path); else return ""; }
/// <summary> /// Loads a track to be played /// </summary> /// <param name="track">Track to be played</param> /// <param name="moveForward">If true then increase HistoryIndex (and potentially add track to History), otherwise decrease it</param> /// <seealso cref="Play"/> public static void Load(TrackData track, bool moveForward = true) { if (moveForward) DispatchLoadedTrack(track); else SettingsManager.HistoryIndex--; loadedTrack = track; if (SettingsManager.MediaState == MediaState.Playing) Start(track); else if (GetType(track) == TrackType.YouTube) { InvokeScript("cueNewVideo", new object[] { YouTubeManager.GetYouTubeID(track.Path), 0 }); } }
/// <summary> /// Checks whether a given playlist contains a given track. /// </summary> /// <param name="playlist">The playlist to search in</param> /// <param name="track">The track to search for</param> /// <returns>True of <paramref name="playlist"/> contains <paramref name="track"/>, otherwise false</returns> public static bool Contains(PlaylistData playlist, TrackData track) { foreach (TrackData t in playlist.Tracks) if (t.Path == track.Path) return true; return false; }
/// <summary> /// Parses a URL and extract meta data. /// </summary> /// <param name="URL">The URL to parse</param> /// <returns>The track representing the audio at the URL</returns> public static TrackData ParseURL(string URL) { TrackData track = new TrackData() { PlayCount = 0, Source = "Radio", Icon = @"..\..\Platform\Windows 7\GUI\Images\Icons\Radio.ico", }; track.Path = URL; int stream = Un4seen.Bass.Bass.BASS_StreamCreateURL(URL, 0, Un4seen.Bass.BASSFlag.BASS_SAMPLE_FLOAT, null, IntPtr.Zero); if (stream != 0) { //Un4seen.Bass.Bass.BASS_ChannelPlay(stream, true); string[] tags = Bass.BASS_ChannelGetTagsICY(stream); SortedList<string, string> meta = new SortedList<string, string>(); if (tags != null && tags.Length > 0) { foreach (string tag in tags) { string[] s = tag.Split(new char[] { ':' }, 2); if (s.Length == 2) meta.Add(s[0], s[1]); } if (meta.Keys.Contains("icy-name")) track.Title = meta["icy-name"]; if (meta.Keys.Contains("icy-genre")) track.Genre = meta["icy-genre"]; if (meta.Keys.Contains("icy-url")) track.URL = meta["icy-url"]; } } return track; }
/// <summary> /// Update the "Last Played" and "Play Count" information of a given track /// </summary> /// <param name="RefTrack">The track that was just played</param> public static void TrackWasPlayed(TrackData RefTrack) { if (RefTrack == null) return; uint pc = RefTrack.PlayCount + 1; foreach (TrackData track in SettingsManager.FileTracks) { if (track.Path == RefTrack.Path) { track.PlayCount = pc; track.LastPlayed = DateTime.Now; } } foreach (TrackData track in SettingsManager.QueueTracks) { if (track.Path == RefTrack.Path) { track.PlayCount = pc; track.LastPlayed = DateTime.Now; } } foreach (TrackData track in SettingsManager.HistoryTracks) if (track.Path == RefTrack.Path) track.PlayCount = pc; foreach (PlaylistData playlist in SettingsManager.Playlists) { if (playlist.Name == CurrentPlaylist) { foreach (TrackData track in playlist.Tracks) { if (track.Path == RefTrack.Path) { track.PlayCount = pc; track.LastPlayed = DateTime.Now; } } } } }
/// <summary> /// Copy all metadata from one track to another /// </summary> /// <param name="source">The source track from which the metadata is copied</param> /// <param name="destination">The destination track to which the metadata is copied</param> private static void CopyTrackInfo(TrackData source, TrackData destination) { destination.Artist = source.Artist; destination.Album = source.Album; destination.Genre = source.Genre; destination.Title = source.Title; destination.Track = source.Track; destination.Year = source.Year; destination.Length = source.Length; destination.LastWrite = source.LastWrite; destination.Bitrate = source.Bitrate; destination.Views = source.Views; destination.Icon = source.Icon; destination.Codecs = source.Codecs; destination.Channels = source.Channels; destination.SampleRate = source.SampleRate; }