/// <summary> /// クエリ文字列をSQL文に展開してPrepare /// </summary> /// <param name="queryText">クエリ文字列</param> /// <returns>プリペアドステートメントまたはNULL</returns> private SQLite3DB.STMT ExpandAndPrepareQueryText(string queryText) { SQLite3DB.STMT tmt = null; for (int i = 0; i < RETRY_COUNT; i++) { foreach (var dlg in QueryTextExpanders) { try { tmt = PrepareForCreatePlaylistView(dlg(queryText)); break; } catch (NotSupportedException) { /* nothin to do */ } catch (ArgumentException) { /* nothin to do */ } catch (SQLite3DB.SQLite3Exception e) { Logger.Log(e); } } ; if (tmt != null) { break; } Thread.Sleep(RETRY_DELAY); } return(tmt); }
private Column[] LoadColumnDefinitionFromDB() { using (var db = this.Connect()) { var colCount = 0; using (SQLite3DB.STMT tmt2 = db.Prepare("SELECT COUNT(*) FROM library_definition ;")) { tmt2.Evaluate((o) => colCount = int.Parse(o[0].ToString())); } if (colCount > 0) { var coldefs = new object[colCount][]; db.FetchRowRange("library_definition", 0, colCount, coldefs); var columns = new List <Column>(); foreach (var coldef in coldefs) { columns.Add(new Column( Name: coldef[0].ToString(), LocalText: coldef[1].ToString(), type: (LibraryColumnType)int.Parse(coldef[2].ToString()), IsPrimaryKey: coldef[3].ToString() == "1", MappedTagField: coldef[4].ToString(), IsTextSearchTarget: coldef[5].ToString() == "1", OmitOnImport: coldef[6].ToString() == "1")); } return(columns.ToArray()); } } return(null); }
/// <summary> /// トラックの情報をSTMTにBINDする /// </summary> /// <param name="stmt">BINDするSTMT</param> /// <param name="track">トラック情報</param> /// <param name="cols">データベースのカラムのリスト</param> private void BindTrackInfo(SQLite3DB.STMT stmt, LuteaAudioTrack track, Column[] cols) { stmt.Reset(); string extension = (((track.file_ext == "CUE") && (track is CD.Track)) ? ((CD.Track)track).file_ext_CUESheet : track.file_ext).ToUpper(); var values = cols .Select(col => { switch (col.Name) { case LibraryDBColumnTextMinimum.file_name: return(track.file_name); case LibraryDBColumnTextMinimum.file_title: return(track.file_title); case LibraryDBColumnTextMinimum.file_ext: return(extension); case LibraryDBColumnTextMinimum.file_size: return(track.file_size.ToString()); case LibraryDBColumnTextMinimum.statDuration: return(((int)track.duration).ToString()); case LibraryDBColumnTextMinimum.statChannels: return(track.channels.ToString()); case LibraryDBColumnTextMinimum.statSamplingrate: return(track.freq.ToString()); case LibraryDBColumnTextMinimum.statBitrate: return(track.bitrate.ToString()); case LibraryDBColumnTextMinimum.statVBR: return("0"); case LibraryDBColumnTextMinimum.infoCodec: return(track.codec.ToString()); case LibraryDBColumnTextMinimum.infoCodec_sub: return(extension); case LibraryDBColumnTextMinimum.infoTagtype: return("0"); case LibraryDBColumnTextMinimum.gain: return("0"); case LibraryDBColumnTextMinimum.modify: return(MusicLibrary.currentTimestamp.ToString()); default: if (!track.tag.Exists((e) => e.Key == col.MappedTagField)) { return(""); } if (track.tag.First((e) => e.Key == col.MappedTagField).Value == null) { return(""); } var tagValue = track.tag.First((e) => e.Key == col.MappedTagField).Value.ToString(); // DATEの表現形式を正規化して格納する return(col.MappedTagField == "DATE" ? normalizeDateString(tagValue) ?? tagValue : tagValue); } }); for (int i = 0; i < cols.Length; i++) { stmt.Bind(i + 1, values.ElementAt(i)); } }
/// <summary> /// プレイリストの行を取得する(キャッシュ付き) /// </summary> /// <param name="index">行番号</param> /// <returns>行の内容,またはNULL</returns> public object[] GetPlaylistRow(int index) { if (index < 0) { return(null); } // このメソッドの呼び出し中にcacheの参照が変わる可能性があるので、最初に参照をコピーする // 一時的に古いcacheの内容を吐いても問題ないので、mutexで固めるほどではない var _cache = PlaylistCache; if (_cache == null) { return(null); } if (_cache.Length <= index) { return(null); } object[] value = null; for (int i = 0; i < RETRY_COUNT; i++) { if (_cache[index] == null) { try { lock (FetchRowStmtLock) { if (FetchRowStmt == null) { FetchRowStmt = LibraryDB.Prepare("SELECT * FROM list WHERE file_name = (SELECT file_name FROM " + PlaylistTableName + " WHERE ROWID=?);"); } FetchRowStmt.Bind(1, (index + 1).ToString()); _cache[index] = FetchRowStmt.EvaluateFirstROW(); } } catch (SQLite3DB.SQLite3Exception) { } } if ((_cache[index] == null) || (_cache[index].Length == 0) || (_cache[index][0] == null)) { _cache[index] = null; Thread.Sleep(1); } else { break; } } value = _cache[index]; if (value == null || value.Length == 0) { return(null); } return(value); }
/// <summary> /// プレイリスト内の行を取得するプリペアドステートメントを破棄 /// </summary> private void DisposeFetchStmt() { lock (FetchRowStmtLock) { if (FetchRowStmt != null) { FetchRowStmt.Dispose(); FetchRowStmt = null; } } }
/// <summary> /// ライブラリのfile_nameカラムがfile_nameまたはfile_name+" "に一致するもののmodifyの値をDateTime型で返す。 /// file_nameがライブラリにない場合はDateTime(0)を返す。 /// </summary> /// <param name="lastModifySTMT"></param> /// <param name="file_name"></param> /// <returns></returns> private DateTime LastModifyDatetime(SQLite3DB.STMT lastModifySTMT, string file_name) { // SQLiteがDBがロックされてたとかで失敗する場合があるのでリトライする for (int i = 0; i < 10; i++) { try { lastModifySTMT.Reset(); lastModifySTMT.Bind(1, file_name); lastModifySTMT.Bind(2, file_name + " "); var result = lastModifySTMT.EvaluateAll(); if (result.Length == 0) { return(new DateTime(0)); } return(Util.Util.timestamp2DateTime(long.Parse(result[0][0].ToString()))); } catch (SQLite3DB.SQLite3Exception) { Thread.Sleep(1); } } return(new DateTime(0)); }
/// <summary> /// CUEとCUE以外を順に解析を行う /// </summary> /// <param name="filenameOfCUEs"></param> /// <param name="filenameOfOthers"></param> /// <param name="threadLocalResults"></param> /// <param name="selectModifySTMT"></param> private void DoAnalyze(IEnumerable <string> filenameOfCUEs, IEnumerable <string> filenameOfOthers, List <LuteaAudioTrack> threadLocalResults, SQLite3DB.STMT selectModifySTMT) { // CUEファイルを処理 if (TypesToImport.HasFlag(ImportableTypes.CUE)) { foreach (var cuefile in filenameOfCUEs) { try { AnalyzeCUE(cuefile, threadLocalResults, selectModifySTMT); } catch (System.IO.IOException ex) { Logger.Error(ex.ToString()); } } } // 全てのファイルを処理 foreach (var file in filenameOfOthers) { try { AnalyzeStreamFile(file, threadLocalResults, selectModifySTMT); } catch (System.IO.IOException ex) { Logger.Error(ex.ToString()); } } }
/// <summary> /// 単体の音楽ファイルを解析する /// </summary> /// <param name="file_name">ファイル名</param> /// <param name="threadLocalResultQueue">スレッド固有の解析結果</param> /// <param name="lastModifySTMT">modifyを取得するプリペアドステートメント</param> private void AnalyzeStreamFile(string file_name, List <LuteaAudioTrack> threadLocalResultQueue, SQLite3DB.STMT lastModifySTMT) { // 既に処理済みの場合はreturn if (AlreadyAnalyzedFiles.Contains(file_name)) { return; } // 処理済みファイルに追加 lock (AlreadyAnalyzedFiles) { AlreadyAnalyzedFiles.Add(file_name); } if (LastModifyDatetime(lastModifySTMT, file_name) > new System.IO.FileInfo(file_name).LastWriteTime&& IsFastMode) { return; } var tag = MetaTag.readTagByFilename(file_name, false); if (tag == null) { return; } var cue = tag.Find(match => match.Key.ToUpper() == "CUESHEET"); if (cue.Key != null) { var cd = InternalCUEReader.Read(file_name, true); if (cd == null) { Logger.Error("CUESHEET is embedded. But, it has error. " + file_name); return; } threadLocalResultQueue.AddRange(cd.tracks.Cast <LuteaAudioTrack>()); } else { // PERFORMERがないとき、ARTISTをPERFORMERとして扱う if (tag.Find((e) => { return(e.Key == "PERFORMER"); }).Value == null) { var artist = tag.Find((e) => { return(e.Key == "ARTIST"); }); if (artist.Value != null) { tag.Add(new KeyValuePair <string, object>("PERFORMER", artist.Value)); } } var tr = new LuteaAudioTrack() { file_name = file_name, file_size = new System.IO.FileInfo(file_name).Length }; if (tag.Exists(_ => _.Key == "__X-LUTEA-CHANS__") && tag.Exists(_ => _.Key == "__X-LUTEA-FREQ__") && tag.Exists(_ => _.Key == "__X-LUTEA-DURATION__")) { tr.duration = int.Parse(tag.Find(_ => _.Key == "__X-LUTEA-DURATION__").Value.ToString()); tr.channels = int.Parse(tag.Find(_ => _.Key == "__X-LUTEA-CHANS__").Value.ToString()); tr.freq = int.Parse(tag.Find(_ => _.Key == "__X-LUTEA-FREQ__").Value.ToString()); } else { try { using (var strm = DecodeStreamFactory.CreateFileStreamPrimitive(file_name)) { tr.duration = (int)strm.LengthSec; tr.channels = (int)strm.Chans; tr.freq = (int)strm.Freq; } } catch (ArgumentException ex) { Logger.Error("cannot open file (by BASS)" + file_name); Logger.Debug(ex); } } tr.tag = tag; if (tr.file_ext == "M4A") { if (tag.Exists(_ => (_.Key == "PURCHASE DATE") || (_.Key == "PURCHASED"))) { if ((TypesToImport & ImportableTypes.M4A_iTunes) == 0) { return; } } else if (tr.codec == H2k6Codec.ALAC) { if ((TypesToImport & ImportableTypes.M4A_ALAC) == 0) { return; } } else { if ((TypesToImport & ImportableTypes.M4A) == 0) { return; } } } threadLocalResultQueue.Add(tr); } }
/// <summary> /// CUEシートを解析する /// </summary> /// <param name="file_name">ファイル名</param> /// <param name="lastModifySTMT">modifyを取得するプリペアドステートメント</param> private void AnalyzeCUE(string file_name, List <LuteaAudioTrack> threadLocalResults, SQLite3DB.STMT lastModifySTMT) { // 既に処理済みの場合はreturn if (AlreadyAnalyzedFiles.Contains(file_name)) { return; } // 処理済みファイルに追加 lock (AlreadyAnalyzedFiles) { AlreadyAnalyzedFiles.Add(file_name); } // ファイルを解析 var cd = CUEReader.ReadFromFile(file_name, true); if (cd == null) { return; } string lastCheckedFilename = null; bool lastCheckedFileShouldSkip = false; bool modifyed = LastModifyDatetime(lastModifySTMT, file_name) <= new System.IO.FileInfo(file_name).LastWriteTime; foreach (CD.Track tr in cd.tracks) { if (tr.file_name_CUESheet == "") { continue; } // 実体ストリームが存在しない、またはCUESHEETが埋め込まれているなら、.cueファイルとしてのインポートはスキップする。 if (lastCheckedFilename != tr.file_name_CUESheet) { lastCheckedFilename = tr.file_name_CUESheet; lastCheckedFileShouldSkip = false; if (!System.IO.File.Exists(tr.file_name_CUESheet)) { lastCheckedFileShouldSkip = true; } // CUEシートが埋め込まれているならスキップ var tagInRealStream = MetaTag.readTagByFilename(tr.file_name_CUESheet, false); if (tagInRealStream != null && (tagInRealStream.Exists(e => e.Key == "CUESHEET"))) { lastCheckedFileShouldSkip = true; } } // ストリームのタグにCUESHEETがある時はなにもしない if (lastCheckedFileShouldSkip) { continue; } lock (AlreadyAnalyzedFiles) { AlreadyAnalyzedFiles.AddRange(cd.tracks.Select(_ => _.file_name_CUESheet)); } if (modifyed || !IsFastMode) { threadLocalResults.Add(tr); } } }