/// <summary> /// Retrieve the Genres from a list of TMDb genre-ids /// </summary> /// <param name="genreids">list of TMDb genre-ids</param> /// <param name="adult">Set if it is a movie for adults</param> /// <returns>Genres as array of string</returns> public async Task <string[]> GetGenresAsync(List <int> genreids, bool adult = false) { if (GenresLUTLanguage != _Language || GenresLUT == null) { // Retrieve LUT Genres Id --> Description LimeMsg.Debug("LimeMetaSearch: GetGenresAsync: {0}", _Language); GenresLUTLanguage = _Language; Connect(); GenresLUT = await TmdbClient.GetMovieGenresAsync(_Language); } List <string> ret = new List <string>(genreids.Count); if (adult) { ret.Add(LimeLanguage.Translate(IniLanguageSection, "GenreAdult", "GenreAdult")); } foreach (var id in genreids) { foreach (var genre in GenresLUT) { if (genre.Id == id) { ret.Add(genre.Name); break; } } } return(ret.ToArray()); }
// -------------------------------------------------------------------------------------------------- #region Methods public void Copy(LimeMetadata source) { if (source == null) { return; } LimeMsg.Debug("LimeMetadata Copy: {0}", source.Title); bool buildcover = false; foreach (var src in source) { if (src.Visible && !src.ReadOnly || src.Ident == "Pictures") { if (TryGet(src.Ident, out var dest)) { LimeMsg.Debug("LimeMetadata Copy: key: {0} = {1}", src.Ident, src.Value); dest.Content = src.Content; if (src.Ident == "Pictures") { buildcover = true; } } } } if (buildcover) { BuildCover(coverOnly: false); } }
// -------------------------------------------------------------------------------------------------- #region Load/Save class functions /// <summary> /// Generic function to Load the Settings of LimeLauncher from an XML file. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="path">Path of the file/link setting representation.</param> /// <returns>The resulting LimeConfig (typically a singleton instance).</returns> public static T Load <T>(string path) where T : LimeConfig { LimeMsg.Debug("LimeConfig Load: {0}", path); try { XmlSerializer reader = new XmlSerializer(typeof(T)); using (StreamReader file = new StreamReader(path)) { XmlReader xread = XmlReader.Create(file); // required to preserve whitespaces T ret = (T)reader.Deserialize(xread); ret.Modified = false; return(ret); } } catch (FileNotFoundException) { LimeMsg.Debug("Setting file not found: {0}", path); } catch (Exception ex) { LimeMsg.Error("ErrLoadFile", path); LimeMsg.Debug("LimeConfig Load error: {0}", ex.ToString()); } return((T)Activator.CreateInstance(typeof(T))); }
public void OnPropertyChanged(bool modified = true, [CallerMemberName] string name = null) { LimeMsg.Debug("LimeConfig OnPropertyChanged: {0}.{1} (modified={2})", this, name, modified); if (modified) { Modified = true; } OnPropertyChanged(new PropertyChangedEventArgs(name)); }
/// <summary> /// Schedule the loading asynchronously of the Persons icons. /// The Order in which it was requested is LIFO, using a Thread-Safe Stack. /// This Stack can be cancelled using <see cref="Cancel"/>. /// </summary> ///<param name="download">Enable to download from Internet if the peroson in missing in the local DB</param> public void LoadAsync(bool download = false) { if (download) { IsDownloading = true; } // Start Queue if (Stack == null) { LimeMsg.Debug("LimePerson LoadAsync: Start Stack Consumer: {0}", download); Stack = new BlockingCollection <LimePerson>(new ConcurrentStack <LimePerson>()) { this }; Task.Run(() => { foreach (var person in Stack.GetConsumingEnumerable()) { // Cancellation if (person == null) { LimeMsg.Debug("LimePerson LoadAsync: Stop Stack Consumer: {0}", Stack.Count); while (Stack.TryTake(out var pers)) { if (pers != null) { pers.IsDownloading = false; } } continue; } LimeMsg.Debug("LimePerson LoadAsync: {0}", person?.Name); lock (Stack) { try { person.Load(); } finally { person.IsDownloading = false; } } } }); } else { // Add Item to Stack Stack.Add(this); } }
/// <summary> /// Free PIDL, if the path actually represents a PIDL /// </summary> /// <param name="path">PIDL path (e.g. ":12345")</param> /// <returns>true if PIDL, false otherwise</returns> public static bool FreePIDL(string path) { if (path != null && path.Length > 2 && path[0] == ':' && path[1] != ':') { LimeMsg.Debug("LimeLib FreePIDL: PIDL: {0}", path); int pidli = int.Parse(path.Substring(1)); Win32.ILFree((IntPtr)pidli); return(true); } return(false); }
// -------------------------------------------------------------------------------------------------- #region Methods /// <summary> /// Request for the current LoadAsync() Stack cancellation /// </summary> public static void Cancel() { if (Stack != null) { // Close this Queue LimeMsg.Debug("LimePerson Cancel: {0}", Stack.Count); // This will eventually kill the Consumer in LoadAsync Stack.Add(null); } }
/// <summary> /// Reserve a PIDL by cloning an existent PIDL and convert it to a path ':1234' format. /// </summary> /// <param name="pidl">Source PIDL (IntPtr) to be cloned</param> /// <returns>A PIDL path in ':1234' format</returns> public static string ReservePIDL(IntPtr pidl) { string ret = null; if (pidl != IntPtr.Zero) { var cpidl = PIDL.ILClone(pidl); ret = String.Format(":{0}", cpidl); LimeMsg.Debug("LimeLib ReservePIDL: PIDL: {0} --> {1}", pidl, ret); } return(ret); }
/// <summary> /// Gets the Lime Property associated with the specified key /// </summary> /// <param name="key"></param> /// <returns></returns> public LimeProperty this[string key] { get { if (!TryGet(key, out var ret)) { LimeMsg.Debug("LimePropertyCollection [key]: key not found: {0}", key); throw new InvalidProgramException(string.Format("LimePropertyCollection [key]: key not found: {0}", key)); } return(ret); } }
// -------------------------------------------------------------------------------------------------- #region Class functions /// <summary> /// Resolve a file-path. Follows the links if the last element is a link. /// </summary> /// <param name="path">path to resolve</param> /// <returns>path after link-resolution. This can be a non-existing file/directory.</returns> public static string ResolvePath(string path) { while (true) { try { if (Path.GetExtension(path).ToLower() == ".lnk") { } else if (!File.Exists(path)) { if (File.Exists(path + ".lnk")) { path += ".lnk"; } else if (File.Exists(Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)) + ".lnk")) { path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)) + ".lnk"; } else { break; } } else { break; } ShellShortcut link = new ShellShortcut(path); LimeMsg.Debug("LimeLib ResolvePath: link: {0}", path); path = link.Path; if (path == "" || IsNetworkDrive(path) || IsLibrary(path)) { // Prefer PIDL to unhandled path-formats IntPtr pidl = link.PIDL; path = string.Format(":{0}", pidl); break; } } catch { return(path); } } return(path); }
// -------------------------------------------------------------------------------------------------- #region Methods /// <summary> /// Set the Content /// </summary> /// <param name="value">new content</param> /// <param name="content">notify change on "Content" if true, on "Item" if false</param> public void Set(object value, bool content = true) { bool changed = !Object.Equals(Content, value); if (!changed && Type != null) { return; } LimeMsg.Debug("LimeProperty Content set: {0} = {1}", Ident, value); if (PInfo != null) { PInfo.SetValue(Source, value); } else { Source = value; if (Type == null) { Type = value.GetType(); } else if (value != null && value.GetType() != Type) { throw new InvalidCastException(string.Format("LimeProperty Set Content: {0} (expected {1})", value.GetType(), Type)); } } // Bind the new object if (changed && value is INotifyPropertyChanged src) { //LimeMsg.Debug("LimeProperty Set: Subscribe {0} / {1} ({2})", Ident, src, src.GetType()); src.PropertyChanged += SourcePropertyChanged; } if (content) { OnPropertyChanged("Content"); OnPropertyChanged("Value"); OnPropertyChanged("Serialize"); } else { OnPropertyChanged("Item"); } }
/// <summary> /// Try to save the Person data to the local database. /// </summary> public void Save() { if (IsBusy) { return; } if (string.IsNullOrEmpty(Name)) { return; } // Make filename from person name string path = null; string localDb = LimeLib.ResolvePath(LocalDbPath); if (localDb != null) { var name = Name; // Remove characters forbidden in filenames string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars()); foreach (char c in invalid) { name = name.Replace(c.ToString(), ""); } path = Path.Combine(localDb, name + Extension); } try { IsSaving = true; LimeMsg.Debug("Person SaveAsync: {0}", Name); using (Stream file = File.Open(path, FileMode.Create)) { ZeroFormatterSerializer.Serialize(file, this); } } catch { LimeMsg.Error("UnableFileDir", path); } finally { IsSaving = false; IsLoaded = true; } }
/// <summary> /// Propagate the PropertyChanged event from contained object (Source) to LimeProperty Content/Value/Serialize /// </summary> /// <param name="sender">Source</param> /// <param name="e"></param> private void SourcePropertyChanged(object sender = null, PropertyChangedEventArgs e = null) { if (sender != Source) { return; } //LimeMsg.Debug("LimeProperty SourcePropertyChanged: {0} <- {1} . {2}", Ident != null ? Ident: Type.Name, sender, e.PropertyName); if (PInfo == null) { // Unreferenced Source OnPropertyChanged("Item"); if (Ident != null && Visible) { try { // TODO: retrieve instance from sender in Global LimeMsg.Debug("LimeProperty SourcePropertyChanged: Item: {0}={1} (Source={2}, Prop: {3})", Ident, Value, sender, e?.PropertyName); } catch { LimeMsg.Debug("LimeProperty SourcePropertyChanged: Item: {0}", e.PropertyName); } } } else if (string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == PInfo.Name) { // Referenced Source if (Ident != null && Visible) { try { LimeMsg.Debug("LimeProperty SourcePropertyChanged: {0}={1} (Source={2}, Prop: {3})", Ident, Value, sender, e?.PropertyName); } catch { LimeMsg.Debug("LimeProperty SourcePropertyChanged: {0}", e.PropertyName); } } OnPropertyChanged("Content"); OnPropertyChanged("Value"); OnPropertyChanged("Serialize"); } }
/// <summary> /// Build a cover from the Jumbo icon of a file/directory /// </summary> /// <param name="path">path to the file</param> /// <returns>Pictures image</returns> public void BuildCover(string path) { LimeMsg.Debug("LimeMetadata BuildCover: path: {0}", path); ImageSource ret = null; try { LimeItem item = new LimeItem(path); using (var bmp = item.Bitmap(256)) { ret = LimeLib.ImageSourceFrom(bmp); } } catch { LimeMsg.Debug("LimeMetadata BuildCover: Failed: path: {0}", path); } Pictures = ret; }
/// <summary> /// Connect to IMDB Database /// </summary> public void Connect() { if (TmdbClient == null) { LimeMsg.Debug("LimeMetaSearch: Connecting..."); // Initialize WEB client to download data WebClient = new WebClient(); // Initialize TMDB TmdbClient = new TMDbClient(TmdbApiKey); // Retrieve TMDB config TmdbClient.GetConfigAsync(); LimeMsg.Debug("LimeMetaSearch: Connected."); } // Make sure the language settings are up-to-date TmdbClient.DefaultLanguage = _Language; TmdbClient.DefaultImageLanguage = _Language; }
/// <summary> /// Save the Settings of LimeLauncher to an XML file. /// </summary> /// <param name="path">File to save the settings to.</param> /// <param name="data">Instance of the data to be saved.</param> /// <returns>true if successful, false otherwise.</returns> public static bool Save <T>(string path, T data) where T : LimeConfig { if (data == null) { LimeMsg.Error("CfgInit"); return(false); } LimeMsg.Debug("LimeConfig Save: {0}", path); try { XmlSerializer writer = new XmlSerializer(typeof(T)); using (StreamWriter file = new StreamWriter(path)) writer.Serialize(file, data); data.Modified = false; return(true); } catch { LimeMsg.Error("ErrSavFile", path); return(false); } }
/// <summary> /// Save the Metadata, if these have changed /// </summary> public void Save() { LimeMsg.Debug("LimeMetadata Save: modified: {0}", Modified); if (!Modified) { return; } // Save Media if (TagLibFile != null && TagLibFile.Tag != null) { LimeMsg.Debug("LimeMetadata Save: TagLibFile: {0}", TagLibFile.Name); bool isVideo = Type == MediaType.Video; string[] genres = null; if (Get("Genres")?.Content is StringComposite <string> compgenres) { genres = compgenres.ToArray(); } TagLibFile.Tag.Genres = genres; string conductor = null; if (Get(isVideo ? "Director" : "Conductor")?.Content is string person) { conductor = person; } TagLibFile.Tag.Conductor = conductor; string[] performers = null; string[] performersRole = null; if (Get(isVideo ? "Actors" : "Artists")?.Content is StringComposite <LimePerson> comppers && comppers.Collection != null) { var persons = comppers.Collection; performers = new string[persons.Count]; performersRole = new string[persons.Count]; for (int i = 0; i < persons.Count; i++) { performers[i] = persons[i].Name; performersRole[i] = persons[i].Roles != null? string.Join("; ", persons[i].Roles) : null; } } TagLibFile.Tag.Performers = performers; TagLibFile.Tag.PerformersRole = performersRole; TagLibFile.Tag.SetInfoTag(); TagLibFile.Save(); } // Save Link var lnk = Get("LinkTarget"); if (lnk != null) { var path = Get("Path"); LimeMsg.Debug("LimeMetadata Save: Link: {0}", path.Value); using (var link = new ShellShortcut(path.Value)) { if (!lnk.ReadOnly) { link.Path = lnk.Value; } link.WorkingDirectory = Get("LinkWorkdir").Value; link.Description = Get("LinkComment").Value; //link.Hotkey = (System.Windows.Forms.Keys)Get("LinkKey").Content; link.WindowStyle = (System.Diagnostics.ProcessWindowStyle)Get("LinkWindowStyle").Content; link.Arguments = Get("LinkArguments")?.Value; link.Save(); } } LimeMsg.Debug("LimeMetadata Save: done"); }
/// <summary> /// Construct and retrieve Metadata from a directory/file path /// </summary> /// <param name="item">The <see cref="LimeItem"/> element to construct Metadata for.</param> /// <param name="coverOnly">Try to load only a cover, not the system icon</param> public LimeMetadata(LimeItem item, bool coverOnly) : this() { LimeLib.LifeTrace(this); // Disable modification detection _Modified = null; LimeMsg.Debug("LimeMetadata: {0}", item.Name); string path = item.Path; if (!coverOnly) { // Add Name Add("Name", item.Name, true, false); if (item.Link != null) { // Link path = item.Link; Add("LinkLabel"); } // Handle tasks if (item.Task) { Add("Task", item.Name, true); } // Display path Add("Path", item, "Path", readOnly: item.Task); } // Retrieve Tags if (!item.Task && !item.Directory && !LimeLib.IsPIDL(path) && !LimeLib.IsSSPD(path)) { LimeMsg.Debug("LimeMetadata: TagLib: {0}", item.Name); try { TagLibFile = TagLib.File.Create(path, TagLib.ReadStyle.Average | TagLib.ReadStyle.PictureLazy); } catch { LimeMsg.Debug("LimeMetadata: {0}: Failed TagLib.File.Create({1})", item.Name, path); } } // Extract Tags if (TagLibFile != null) { LimeMsg.Debug("LimeMetadata: TagLib done: {0}", item.Name); // Retrieve Type if (TagLibFile.Properties != null && TagLibFile.Properties.Codecs != null) { TagLib.MediaTypes[] prioTypes = new TagLib.MediaTypes[] { TagLib.MediaTypes.Video, TagLib.MediaTypes.Audio, TagLib.MediaTypes.Photo, TagLib.MediaTypes.Text }; var codecs = TagLibFile.Properties.Codecs; foreach (var codec in codecs) { if (codec != null) { TagLib.MediaTypes mask = codec.MediaTypes | (TagLib.MediaTypes)Type; foreach (var typ in prioTypes) { if ((mask & typ) != 0) { Type = (MediaType)typ; break; } } } } } else if (TagLibFile is TagLib.Image.NoMetadata.File) { Type = MediaType.Image; } } // Handle Links if (!coverOnly && item.Link != null) { using (var link = new ShellShortcut(item.Path)) { Add("LinkWorkdir", link.WorkingDirectory); //Add("LinkKey", link.Hotkey); Add("LinkWindowStyle", (ShellLinkWindowStyle)link.WindowStyle); if (Type == MediaType.None) { Add("LinkArguments", link.Arguments); } Add("LinkComment", link.Description); } Add("TagLabel"); // Target Path if (LimeLib.IsPIDL(item.Link)) { // Retrieve name of the PIDL try { var dInfo = new DirectoryInfoEx(new ShellDll.PIDL(LimeLib.GetPIDL(item.Link), true)); Add("LinkTarget", dInfo.Label, true); } catch { } } else { Add("LinkTarget", item.Link, LimeLib.IsSSPD(item.Link)); } } if (TagLibFile != null) { LimeMsg.Debug("LimeMetadata: TagLib done 2: {0}", item.Name); // Build the Properties BuildProperties(); // Build the Pictures image BuildCover(coverOnly); } else if (!coverOnly) { // Build the Pictures image from the file icon using (var bmp = item.Bitmap(256)) { Pictures = LimeLib.ImageSourceFrom(bmp); } } if (!coverOnly) { // Finalize BuildToolTip(); } // re-enable modification detection _Modified = false; LimeMsg.Debug("LimeMetadata End: {0}", item.Name); }
/// <summary> /// Try to retrieve the Person data from the local database, or Internet /// </summary> /// <returns>true if successful (false if not found)</returns> public bool Load() { if (IsLoading || IsSaving || IsLoaded) { return(true); } if (string.IsNullOrEmpty(Name)) { return(false); } // Make filename from person name string path = null; string localDb = LimeLib.ResolvePath(LocalDbPath); if (localDb != null) { var name = Name; // Remove characters forbidden in filenames string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars()); foreach (char c in invalid) { name = name.Replace(c.ToString(), ""); } path = Path.Combine(localDb, name + Extension); } try { IsLoading = true; if (File.Exists(path)) { LimePerson lp; // Local Database using (Stream file = File.Open(path, FileMode.Open)) { lp = ZeroFormatterSerializer.Deserialize <LimePerson>(file); } // Properties to be preserved var roles = Roles; var rolesReadOnly = RolesReadOnly; // Copy properties LimeLib.CopyPropertyValues(lp, this); // Restore Properties to be preserved Roles = roles; RolesReadOnly = rolesReadOnly; // Done IsLoaded = true; return(true); } } catch { LimeMsg.Error("UnableFileDir", path); } finally { IsLoading = false; } if (IsDownloading && MetaSearch != null) { bool ret = false; try { IsLoading = true; LimeMsg.Debug("Person Load: Download {0}", Name); ret = MetaSearch.GetPersonAsync(this).Result; } catch { LimeMsg.Error("ErrPersonDownload", Name); } finally { IsLoading = false; IsDownloading = false; } IsLoaded = true; if (ret && AutoSave) { Save(); } return(ret); } return(false); }
/// <summary> /// Translate the key of a section to the defined language. /// </summary> /// <param name="section">Section in which the key should be retrieved.</param> /// <param name="key">key identifier or description to be translated</param> /// <param name="fallback">fallback translation to create a new entry (Debug only)</param> /// <returns>the translation of the key to the defined language.</returns> public static string Translate(string section, string key, string fallback = null) { if (key == null) { return(null); } int multi = 1; // Retrieve language entry string keyval = ini.IniReadValue(section, key); string mkey = null; string mval = null; if (String.IsNullOrEmpty(keyval)) { mkey = key + "." + multi.ToString(); mval = ini.IniReadValue(section, mkey); if (!String.IsNullOrEmpty(mval)) { multi++; keyval = mval; } } #if DEBUG // Try to avoid crashes in Xaml designer if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) { return(key); } // Create New Entries if (string.IsNullOrEmpty(keyval) && fallback != null) { keyval = fallback; if (!isIniDefault && fallback != "") { fallback = "<" + fallback + ">"; } ini.IniWriteValue(section, key, fallback); LimeMsg.Debug("Compile: Commands: New INI Language Command Key: {0}/{1}={2}", section, key, fallback); if (!_DbgCompileWarning) { LimeMsg.Warning("CmpCreateIni"); _DbgCompileWarning = true; } } #endif string ret; if (!string.IsNullOrEmpty(keyval)) { ret = keyval; } else if (fallback == "") { ret = ""; } else if (!isIniDefault) { // Fallback to default language if key doesn't exist keyval = iniDefault.IniReadValue(section, key); if (!String.IsNullOrEmpty(keyval)) { ret = keyval; } else { ret = key; } } else { ret = key; } if (multi > 1) { mkey = key + "." + multi.ToString(); while (!String.IsNullOrEmpty(mval = ini.IniReadValue(section, mkey))) { ret += Environment.NewLine + mval; multi++; mkey = key + "." + multi.ToString(); } } return(ret); }