/// <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> /// 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> /// Retrieves the URL to the thumbnail for a Jamendo track. /// </summary> /// <param name="track">The Jamendo track</param> public static string GetThumbnail(TrackData track) { return track.ArtURL; }
/// <summary> /// Initializes a new instance of the <see cref="SourcesModifiedEventArgs"/> class /// </summary> /// <param name="track">The track that was either added or removed</param> /// <param name="modType">The modification type of the source</param> /// <param name="callback">The callbacks and their respective parameters which to call when the event is handled</param> public SourceModifiedEventArgs(TrackData track, SourceModificationType modType, List<KeyValuePair<ScannerCallback, object>> callbacks) { Track = track; ModificationType = modType; Callbacks = callbacks; }
/// <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; }
/// <summary> /// The dispatcher of the <see cref="FilesystemManager.SourcesModified"/> event /// </summary> /// <param name="track">The track that was either added, removed or updated</param> /// <param name="modType">The modification type of the source</param> /// <param name="callback">The callbacks and their respective parameters that will be sent along with the SourceModified event</param> private static void DispatchSourceModified(TrackData track, SourceModificationType modType, List<KeyValuePair<ScannerCallback, object>> callbacks) { if (SourceModified != null) SourceModified(null, new SourceModifiedEventArgs(track, modType, callbacks)); }
/// <summary> /// Checks if the meta data of a track has been updated since last read. /// </summary> /// <param name="track">The track to check</param> /// <returns>True if the track has been written to since last read</returns> public static bool Updated(TrackData track) { try { FileInfo fInfo = new FileInfo(track.Path); return fInfo.LastWriteTimeUtc.Ticks > track.LastWrite; } catch { return false; } }
/// <summary> /// Starts playback of the media player. /// </summary> /// <param name="track">The track to start playing</param> private static void Start(TrackData track) { //U.L(LogLevel.Information, "MEDIA", "Playing " + track.Path); // remove the song if it's in the queue if (SettingsManager.QueueTracks.Contains(track)) { U.L(LogLevel.Debug, "MEDIA", "Remove track from queue"); SettingsManager.QueueTracks.Remove(track); foreach (TrackData trackInQueue in SettingsManager.QueueTracks) trackInQueue.Number = SettingsManager.QueueTracks.IndexOf(trackInQueue) + 1; } ThreadStart PlayThread = delegate() { trackWasSkipped = false; // add song to played/recent list songsAlreadyPlayed.Add(track); TrackType type = GetType(track); if (stream != 0) Bass.BASS_StreamFree(stream); stream = 0; switch (type) { case TrackType.YouTube: string vid = YouTubeManager.GetYouTubeID(track.Path); InvokeScript("setVolume", new object[] { SettingsManager.Volume }); 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.Jamendo: stream = Bass.BASS_StreamCreateURL(JamendoManager.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); break; } if (stream != 0) { // set some fx SetFX(); Bass.BASS_ChannelRemoveSync(stream, syncer); syncer = Bass.BASS_ChannelSetSync(stream, BASSSync.BASS_SYNC_END, 0, sync, IntPtr.Zero); Bass.BASS_Start(); Bass.BASS_ChannelPlay(stream, true); if (type == TrackType.File) { double p = SettingsManager.Seek; Bass.BASS_ChannelSetPosition(stream, (long)((p / 10.0) * Bass.BASS_ChannelGetLength(stream))); } } SettingsManager.CurrentTrack = track; if ((YouTubeManager.IsYouTube(track.Path) && YouTubeManager.HasFlash) || stream != 0) SettingsManager.MediaState = MediaState.Playing; loadedTrack = null; PlaylistManager.TrackWasPlayed(SettingsManager.CurrentTrack); DispatchStarted(); }; Thread play_thread = new Thread(PlayThread); play_thread.Name = "Playback"; play_thread.IsBackground = true; play_thread.Priority = ThreadPriority.BelowNormal; play_thread.Start(); }
/// <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> /// 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> /// 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> /// 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 { DispatchStarted(); return; } StartTicker(); loadedTrack = null; DispatchStarted(); } }
/// <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> /// Writes the current track data to the file /// </summary> /// <param name="track">The track to save</param> public static void SaveTrack(TrackData track) { TagLib.File file = TagLib.File.Create(track.Path); file.Tag.Performers = track.Artist.Split(','); file.Tag.Album = track.Album; file.Tag.Title = track.Title; file.Tag.Year = Convert.ToUInt32(track.Year); file.Tag.Genres = track.Genre.Split(','); file.Tag.Track = Convert.ToUInt32(track.Track); file.Save(); UpdateTrack(track); }
/// <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> /// The dispatcher of the <see cref="FilesystemManager.SourcesModified"/> event /// </summary> /// <param name="track">The track that was either added, removed or updated</param> /// <param name="modType">The modification type of the source</param> private static void DispatchSourceModified(TrackData track, SourceModificationType modType) { DispatchSourceModified(track, modType, new List<KeyValuePair<ScannerCallback, 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; } foreach (TrackData t in SettingsManager.FileTracks) if (t.Path == track.Path) return t; return track; }
/// <summary> /// The dispatcher of the <see cref="TrackModified"/> event /// </summary> /// <param name="track">The track that was modified</param> /// <param name="e">The event data</param> private static void DispatchTrackModified(TrackData track, PropertyChangedEventArgs e) { if (TrackModified != null) TrackModified(track, e); }
/// <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> /// Reads the metadata of a track and updates it /// </summary> /// <param name="track">The track to update</param> /// <param name="copy">Whether the data should be copied to queue/history/playlists</param> public static void UpdateTrack(TrackData track, bool copy = true) { if (!Updated(track)) return; try { TagLib.File file = TagLib.File.Create(track.Path, TagLib.ReadStyle.Average); track.Artist = U.CleanXMLString(file.Tag.JoinedPerformers); track.Album = U.CleanXMLString(file.Tag.Album); track.Title = U.CleanXMLString(file.Tag.Title); track.Genre = U.CleanXMLString(file.Tag.JoinedGenres); track.Track = file.Tag.Track; track.Year = file.Tag.Year; track.Length = file.Properties.Duration.TotalMilliseconds / 1000.0; track.Bitrate = file.Properties.AudioBitrate; track.Channels = file.Properties.AudioChannels; track.SampleRate = file.Properties.AudioSampleRate; track.Codecs = ""; foreach (TagLib.ICodec c in file.Properties.Codecs) if (c != null) track.Codecs += c.Description + ", "; track.Codecs = track.Codecs.Substring(0, track.Codecs.Length - 2); } catch (Exception e) { if (track.Artist == U.T("MetaDataLoading")) track.Artist = ""; if (track.Album == U.T("MetaDataLoading")) track.Album = ""; if (track.Title == U.T("MetaDataLoading")) track.Title = ""; if (track.Genre == U.T("MetaDataLoading")) track.Genre = ""; U.L(LogLevel.Warning, "FILESYSTEM", "Could not read ID3 data of file: " + track.Path); U.L(LogLevel.Warning, "FILESYSTEM", e.Message); } try { FileInfo fInfo = new FileInfo(track.Path); track.LastWrite = fInfo.LastWriteTimeUtc.Ticks; if (String.IsNullOrWhiteSpace(track.Title)) { track.Title = Path.GetFileNameWithoutExtension(track.Path); track.Title = track.Title.Trim(); string s = Regex.Replace(track.Title, @"^(\d+\s)+", ""); if (!String.IsNullOrWhiteSpace(s)) track.Title = s; track.Title = U.PrettifyTag(track.Title); } } catch (Exception e) { U.L(LogLevel.Warning, "FILESYSTEM", "Could not access file: " + track.Path); U.L(LogLevel.Warning, "FILESYSTEM", e.Message); } track.Icon = "pack://application:,,,/Platform/Windows 7/GUI/Images/Icons/FileAudio.ico"; track.Processed = true; if (!copy) return; for (int i = 0; i < SettingsManager.FileTracks.Count; i++) { if (SettingsManager.FileTracks[i].Path == track.Path) { CopyTrackInfo(track, SettingsManager.FileTracks[i]); break; } } for (int i = 0; i < SettingsManager.QueueTracks.Count; i++) { if (SettingsManager.QueueTracks[i].Path == track.Path) { CopyTrackInfo(track, SettingsManager.QueueTracks[i]); break; } } for (int i = 0; i < SettingsManager.HistoryTracks.Count; i++) { if (SettingsManager.HistoryTracks[i].Path == track.Path) { CopyTrackInfo(track, SettingsManager.HistoryTracks[i]); break; } } foreach (PlaylistData p in SettingsManager.Playlists) for (int i = 0; i < p.Tracks.Count; i++) { if (p.Tracks[i].Path == track.Path) { CopyTrackInfo(track, p.Tracks[i]); break; } } }
/// <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.Jamendo: return U.T("FileTypeJamendo", 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> /// Create a track given a filename /// </summary> /// <param name="filename">The filename of the track</param> /// <param name="dispatchModified">Whether or not to dispatch that the track has been added</param> /// <returns>The newly created track</returns> public static TrackData CreateTrack(String filename, bool dispatchModified = true) { if (!MediaManager.IsSupported(filename)) { U.L(LogLevel.Warning, "FILESYSTEM", "Cannot create track " + filename + ": unsupported format"); return null; } if (!File.Exists(filename)) { U.L(LogLevel.Warning, "FILESYSTEM", "Cannot create track " + filename + ": file does not exist"); return null; } FileInfo fInfo = new FileInfo(filename); TrackData track = new TrackData { Processed = false, Artist = U.T("MetaDataLoading"), Album = U.T("MetaDataLoading"), Title = U.T("MetaDataLoading"), Genre = U.T("MetaDataLoading"), Year = 0, Length = 0.0, PlayCount = 0, Track = 0, Path = filename, Bitrate = 0, Channels = 0, SampleRate = 0, Codecs = "", Number = 0, Bookmarks = new List<double>(), Source = "Files", Icon = @"..\..\Platform\Windows 7\GUI\Images\Icons\FileAudio.ico", LastWrite = 0 }; track.PropertyChanged += new PropertyChangedEventHandler(Track_PropertyChanged); if (dispatchModified) DispatchSourceModified(track, SourceModificationType.Added); return track; }
/// <summary> /// Initialize the manager /// </summary> public static void Initialize() { U.L(LogLevel.Debug, "MEDIA", "Init BASS"); FFTData = new float[1024]; BassNet.Registration(U.BassMail, U.BassKey); 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_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; U.L(LogLevel.Debug, "MEDIA", "Initialized"); }
/// <summary> /// Add a file to the collection /// </summary> /// <param name="filename">The path of the file</param> /// <param name="scanMetaData">Whether to scan the tracks meta data as well</param> /// <param name="callbacks">The callbacks and their respective parameters that will be sent along with the SourceModified event</param> private static void AddFile(String filename, bool scanMetaData, List<KeyValuePair<ScannerCallback, object>> callbacks) { FileInfo fInfo = new FileInfo(filename); TrackData track = new TrackData { Processed = false, Artist = U.T("MetaDataLoading"), Album = U.T("MetaDataLoading"), Title = U.T("MetaDataLoading"), Genre = U.T("MetaDataLoading"), Year = 0, Length = 0.0, PlayCount = 0, Bitrate = 0, Channels = 0, SampleRate = 0, Codecs = "", Track = 0, Path = fInfo.FullName, Number = 0, Icon = @"..\..\Platform\Windows 7\GUI\Images\Icons\FileAudio.ico", LastWrite = 0 }; DispatchSourceModified(track, SourceModificationType.Added, callbacks); track.PropertyChanged += new PropertyChangedEventHandler(Track_PropertyChanged); if (scanMetaData) DispatchPathModified(track.Path); }
/// <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; }
/// <summary> /// Retrieves the stream URL for a Jamendo track /// </summary> /// <param name="track">The Jamendo track</param> public static string GetStreamURL(TrackData track) { return String.Format("http://storage-new.newjamendo.com/?trackid={0}&format=mp31&u=0", GetID(track.Path)); }
/// <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> /// Parses a JSON object into a track. /// </summary> /// <param name="json">The JSON data</param> /// <returns>A track</returns> private static TrackData ParseTrack(JObject json) { if (json == null) return null; try { TrackData track = new TrackData(); track.Icon = "pack://application:,,,/Platform/Windows 7/GUI/Images/Icons/Jamendo.ico"; track.Path = String.Format("{0}{1}", pathPrefix, json["id"]); track.Title = (string)json["name"]; track.Genre = (string)json["genre"]; track.URL = (string)json["url"]; track.Artist = (string)json["artist_name"]; track.Album = (string)json["album_name"]; int d = (int)json["duration"]; track.Length = (double)d; if (json["image"].Type == JTokenType.String) track.ArtURL = (string)json["image"]; else if (json["album_image"].Type == JTokenType.String) track.ArtURL = (string)json["album_image"]; else if (json["artist_image"].Type == JTokenType.String) track.ArtURL = (string)json["artist_image"]; track.Image = track.ArtURL; if (track.Image == null || track.Image == "") track.Image = track.Icon; return track; } catch (Exception e) { U.L(LogLevel.Warning, "JAMENDO", "Could not parse track JSON data: " + e.Message); return null; } }
/// <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; }