/Chavah/Controllers/SongsController.cs
C# | 882 lines | 773 code | 87 blank | 22 comment | 85 complexity | eff1dedf3db34bd9f712e5059cdd7e18 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.Serialization;
- using System.ServiceModel;
- using System.ServiceModel.Web;
- using System.Text;
- using System.IO;
- using System.Timers;
- using System.Collections.Concurrent;
- using System.Reactive.Linq;
- using System.Threading.Tasks;
- using System.ServiceModel.Syndication;
- using Chavah.Common;
- using System.Web.Mvc;
- using Chavah.Data;
- using Chavah.Services;
- using System.Web;
- using System.Globalization;
- using Raven.Client;
- using Raven.Client.Linq;
- using Chavah.Models;
- using SongLike = Chavah.Models.Like;
- namespace Chavah.Controllers
- {
- public class SongsController : Controller
- {
- private static readonly Random random = new Random();
- private static readonly Lazy<List<SongInfo>> cachedSongs = new Lazy<List<SongInfo>>(() => GetCachedSongsWithLogging(), isThreadSafe: true);
- private static readonly ConcurrentDictionary<string, DateTime> visits = new ConcurrentDictionary<string, DateTime>();
- private readonly IDocumentStore raven = RavenStore.Db;
- public SongsController()
- {
- }
- static SongsController()
- {
- Task.Factory
- .StartNew(UpdateSongPurchaseInfo)
- .ContinueWith(_ => ClearOutOldLogs());
- }
- public JsonResult GetPeopleOnline(int minutes)
- {
- return Json(new
- {
- TotalSinceStart = visits.Count,
- PeopleOnline = "In the last " + minutes.ToString() + " minutes, " + visits.Values.Count(v => v >= DateTime.Now.Subtract(TimeSpan.FromMinutes(minutes))) + " people have used Chavah."
- }, JsonRequestBehavior.AllowGet);
- }
- public ActionResult ActivityFeed()
- {
- using (var session = raven.OpenSession())
- {
- var recentActivities = session
- .Query<Activity>()
- .OrderByDescending(a => a.DateTime)
- .Take(30);
- var feedItems = from activity in recentActivities.ToArray()
- select new SyndicationItem(
- title: activity.Title,
- content: activity.Description,
- itemAlternateLink: activity.MoreInfoUri);
- var feed = new SyndicationFeed("Chavah Messianic Radio", "The latest activity over at Chavah Messianic Radio", new Uri("http://messianicradio.com"), feedItems) { Language = "en-US" };
- return new RssActionResult { Feed = feed };
- }
- }
- private static string GetArtistTwitterHandle(string artist)
- {
- return Match.Value(artist)
- .With("Asharyahuw", "@Asharyahuw")
- .With("Aviad Cohen", "@aviadcohen")
- .With("Barry & Batya Segal", "@VisionForIsrael")
- .With("Carlos Perdomo", "@7ElCantante7")
- .With("Deborah Kline-Iantorno", "@deborahkline")
- .With("Devora Clark", "@devoraclark")
- .With("Downpour", "@downpourband1")
- .With("Frederique Vervoitte", "@fredouvervoitte")
- .With("Ephraim Ben Yoseph", "@EphraimbYoseph")
- .With("Giselle", "@GiselleTka")
- .With("Greg Silverman", "@GSilverPraise")
- .With("Hillel Ben Yochanan", "@Hillel_Yochanan")
- .With("Jonathan Lane", "@doyouknowyeshua")
- .With("Jonathan Settel", "@JSettel")
- .With("Joshua Rosen", "@joshuajrosen")
- .With("Lev Shelo", "@corrybell")
- .With("Liberated Wailing Wall", "@jfjlww")
- .With("Lynne McDowell", "@Lynne_McDowell")
- .With("Maurice Sklar", "@mauricesklar")
- .With("Maurice Sklar & Hugh Sung", "@mauricesklar")
- .With("Magen David", "@MagenDavidMG")
- .With("Micha'el Eliyahu BenDavid", "@MalachHaBrit")
- .With("Philip Stanley Klein", "@minorkey1")
- .With("Ross", "@ROSS_AandS")
- .With("Sharon Wilbur", "@sharonwilbur")
- .With("The Hebraism Music Project", "@Hebraism")
- .With("The Lumbrosos", "@TheLumbrosos")
- .With("Will Spires", "@wpspires")
- .With("Yerubilee", "@yerubilee")
- .With("Zemer Levav", "@zemerlevav")
- .DefaultTo(null);
- }
- public ActionResult GetAllSongs()
- {
- var result = cachedSongs
- .Value
- .OrderBy(s => s.Artist)
- .ThenBy(s => s.Album)
- .ThenBy(s => s.Number)
- .Select(s => s.ToDto(SongLikeStatus.None));
- return Json(result, JsonRequestBehavior.AllowGet);
- }
-
- public ActionResult GetAlbumArt(long songId)
- {
- var cachedResult = GetCachedContentResultOrNull(TimeSpan.FromDays(30));
- if (cachedResult != null)
- {
- return cachedResult;
- }
- else
- {
- var song = cachedSongs.Value.FirstOrDefault(s => s.Id == songId);
- if (song == null)
- {
- throw new HttpException(404, "Couldn't find the song with ID " + songId.ToString());
- }
- var albumArtFilePath = song.GetAlbumArtFilePath();
- var contentType = Match.Value(albumArtFilePath).With(".png", "image/png").DefaultTo("image/jpeg");
- return File(albumArtFilePath, contentType);
- }
- }
- private ContentResult GetCachedContentResultOrNull(TimeSpan cacheTime)
- {
- var modifiedSinceHeader = Request.Headers["If-Modified-Since"];
- if (modifiedSinceHeader.Exists())
- {
- var lastMod = DateTime.ParseExact(modifiedSinceHeader, "r", CultureInfo.InvariantCulture);
- var expireTime = lastMod.Add(cacheTime);
- if (expireTime > DateTime.Now)
- {
- Response.StatusCode = 304;
- Response.StatusDescription = "Not Modified";
- return Content(String.Empty);
- }
- }
- Response.Cache.SetCacheability(HttpCacheability.Public);
- Response.Cache.SetLastModified(DateTime.Now);
- return null;
- }
- public ActionResult GetTotalPlays()
- {
- using (var session = raven.OpenSession())
- {
- //var result = session.Query<Models.User>().Sum(u => u.TotalPlays);
- return Json(0, JsonRequestBehavior.AllowGet);
- }
- }
- public ActionResult GetTrendingSongs(int count)
- {
- using (var session = raven.OpenSession())
- {
- var trendingSongs = session
- .Query<SongLike>()
- .Where(l => l.LikeStatus == true)
- .OrderByDescending(l => l.Date)
- .Take(count * 2) // Count * 2, so that we can .Distinct on the in-memory stuff and still get back the requested number of elements.
- .Select(l => l.SongId)
- .AsEnumerable()
- .Distinct()
- .Take(count)
- .Select(id => cachedSongs.Value.First(s => s.Id == id).ToDto(SongLikeStatus.None));
- return Json(trendingSongs.ToArray(), JsonRequestBehavior.AllowGet);
- }
- }
- [NoCaching]
- public ActionResult GetTopSongs(int count)
- {
- var topSongCount = 25;
- var maxPlace = topSongCount - Math.Min(count, topSongCount);
- var randomPlace = random.Next(0, maxPlace);
- var result = cachedSongs.Value
- .OrderByDescending(s => s.CommunityRank)
- .Skip(randomPlace)
- .Take(count)
- .Select(s => s.ToDto(SongLikeStatus.None));
- return Json(result, JsonRequestBehavior.AllowGet);
- }
- public ActionResult GetSongMatches(string searchText)
- {
- var stringMatches = new Func<string, string, bool>((s1, s2) => string.Equals(s1, s2, StringComparison.InvariantCultureIgnoreCase));
- var isHeavenlySeventy = new Func<string, bool>(s => stringMatches(s, "heavenly seventy") || stringMatches(s, "heavenly 70"));
- var isBowelyBottom = new Func<string, bool>(s => stringMatches(s, "bowely bottom"));
- var isLucasLovelyList = new Func<string, bool>(s => stringMatches(s, "lucas lovely list") || stringMatches(s, "lucas' lovely list"));
- var songMatches = Match.Value(searchText)
- .With(isHeavenlySeventy, _ => GetHeavenlySeventy())
- .With(isBowelyBottom, _ => GetBowelyBottom())
- .With(isLucasLovelyList, _ => GetLucasLovelyList())
- .With(_ => true, s => GetSongMatchingText(s));
- return Json(songMatches.Evaluate(), JsonRequestBehavior.AllowGet);
- }
- private IEnumerable<Song> GetLucasLovelyList()
- {
- var lucasLovelyListIds = new long[] { 1179, 359, 573, 1176, 2283, 518, 577, 350, 572, 2513, 667, 2068, 536, 1181, 382, 648, 2279, 324, 2209, 1826, 1913, 2053, 649, 568, 505, 357, 2169, 495, 1824, 335, 483, 97, 723, 1185, 2300, 1282, 1079, 149, 273, 1863, 401, 363, 1969, 241, 162, 513, 1159, 410, 330, 570, 168, 524, 1515, 1945, 633, 2350, 774, 82, 1972, 721, 134, 1283, 1350, 1970, 618, 343, 2341, 444, 2170, 728, 76, 527, 1178, 535, 74, 445, 2302, 553, 1, 277, 1952, 364, 1765, 1976, 346, 1946, 2204, 249, 236, 621, 2100, 1977, 422, 154, 411, 458, 459, 2510 };
- return lucasLovelyListIds
- .Join(cachedSongs.Value, l => l, s => s.Id, (i, s) => s)
- .WhereNotNull()
- .Select(s => s.ToDto(SongLikeStatus.None));
- }
- public IEnumerable<Song> GetSongMatchingText(string searchText)
- {
- if (searchText.Length <= 2)
- {
- return Enumerable.Empty<Song>();
- }
- var allSongs = cachedSongs.Value;
- var matchingSongNames = allSongs.Where(s => s.Name.Contains(searchText, StringComparison.OrdinalIgnoreCase));
- var matchingArtists = allSongs.Where(s => s.Artist.Contains(searchText, StringComparison.OrdinalIgnoreCase));
- var matchingAlbums = allSongs.Where(s => s.Album.Contains(searchText, StringComparison.OrdinalIgnoreCase));
- return matchingSongNames
- .Concat(matchingArtists)
- .Concat(matchingAlbums)
- .Take(25)
- .Select(s => s.ToDto(SongLikeStatus.None));
- }
- private IEnumerable<Song> GetBowelyBottom()
- {
- return cachedSongs.Value
- .OrderBy(s => s.CommunityRank)
- .Take(70)
- .AsEnumerable()
- .Select(s => s.ToDto(SongLikeStatus.None));
- }
- public IEnumerable<Song> GetHeavenlySeventy()
- {
- return cachedSongs.Value
- .OrderByDescending(s => s.CommunityRank)
- .Take(70)
- .AsEnumerable()
- .Select(s => s.ToDto(SongLikeStatus.None));
- }
- public ActionResult GetRandomLikedSongs(int count)
- {
- var likedSongs = from like in Dependency.Get<LikesCache>()
- .ForClient(GetUserIdFromRequest())
- .Where(l => l.LikeStatus == true)
- .ToList()
- .RandomOrder()
- let song = cachedSongs.Value.FirstOrDefault(s => s.Id == like.SongId)
- where song != null
- select song.ToDto(SongLikeStatus.Like);
- return Json(likedSongs.Take(count), JsonRequestBehavior.AllowGet);
- }
- [NoCaching]
- public ActionResult GetSongForClient()
- {
- try
- {
- var userId = GetUserIdFromRequest();
- OnSongPlayedForClient(userId);
- var song = GetSongForClientWithLikeWeights(userId);
- return Json(song, JsonRequestBehavior.AllowGet);
- }
- catch (Exception error)
- {
- ChavahEntities.LogInNewContext(error.ToString());
- return Json(Song.GetErrorSong(error.ToString()), JsonRequestBehavior.AllowGet);
- }
- }
-
- public ActionResult GetSongById(long songId)
- {
- var userId = GetUserIdFromRequest();
- OnSongPlayedForClient(userId);
- using (var entities = new ChavahEntities())
- {
- var song = entities.SongInfoes.FirstOrDefault(s => s.Id == songId);
- if (song != null)
- {
- var like = Dependency.Get<LikesCache>()
- .ForClient(userId)
- .FirstOrDefault(l => l.SongId == songId);
- var result = song.ToDto(like.ToSongLikeEnum());
- return Json(result, JsonRequestBehavior.AllowGet);
- }
- // This should never happen: a client requets a song ID that doesn't exist.
- var errorMessage = "Unable to find song with ID = " + songId.ToString();
- entities.Log(errorMessage);
- throw new Exception(errorMessage);
- }
- }
- public ActionResult GetRequestedSongId()
- {
- using (var session = raven.OpenSession())
- {
- var userId = GetUserIdFromRequest();
- var user = session.Load<User>(userId);
- if (user != null)
- {
- var userDislikes = Dependency.Get<LikesCache>().ForClient(userId).Where(l => l.LikeStatus == false);
- var halfHourAgo = DateTime.Now.Subtract(TimeSpan.FromMinutes(30));
- var songRequest = session
- .Query<SongRequest>()
- .OrderByDescending(s => s.DateTime)
- .Where(s => s.DateTime >= halfHourAgo && s.UserWhoMadeRequestId != user.Id)
- .Take(30)
- .ToArray()
- .Where(s => !s.PlayedForClientIds.Contains(user.Id, StringComparer.OrdinalIgnoreCase))
- .Where(s => userDislikes.All(d => d.SongId != s.SongId))
- .FirstOrDefault();
- if (songRequest != null)
- {
- songRequest.PlayedForClientIds.Add(user.Id);
- session.SaveChanges();
- return Json(songRequest.SongId, JsonRequestBehavior.AllowGet);
- }
- }
- return Json(null, JsonRequestBehavior.AllowGet);
- }
- }
- public ActionResult GetIndividualSongRanks(int songId)
- {
- using (var session = raven.OpenSession())
- {
- var upVoteCount = session.Query<SongLike>().Count(l => l.SongId == songId && l.LikeStatus == true);
- var downVoteCount = session.Query<SongLike>().Count(l => l.SongId == songId && l.LikeStatus == false);
- var result = new
- {
- UpVotes = upVoteCount,
- DownVotes = downVoteCount,
- SongId = songId
- };
- return Json(result, JsonRequestBehavior.AllowGet);
- }
- }
-
- public ActionResult RequestSong(long songId)
- {
- using (var session = raven.OpenSession())
- {
- var user = session.Load<User>(this.GetUserIdFromRequest());
- var song = cachedSongs.Value.FirstOrDefault(s => s.Id == songId);
- if (song != null && user != null)
- {
- var requestExpiration = DateTime.UtcNow.AddDays(14);
- if (!HasRecentPendingSongRequest(songId, session) && !HasManyPendingSongRequestForArtist(song.Artist, session))
- {
- var songRequest = new SongRequest
- {
- DateTime = DateTime.Now,
- PlayedForClientIds = new List<string> { user.Id },
- SongId = songId,
- Artist = song.Artist,
- UserWhoMadeRequestId = user.Id
- };
- session.Store(songRequest);
- session.AddRavenExpiration(songRequest, requestExpiration);
- }
- var songArtist = Match.Value(GetArtistTwitterHandle(song.Artist))
- .IfNotNull(h => h)
- .DefaultTo(song.Artist)
- .Evaluate();
- var activity = new Activity
- {
- DateTime = DateTime.Now,
- Title = string.Format("{0} - {1} was requested by one of our listeners", song.Artist, song.Name),
- Description = string.Format("\"{0}\" by {1} was requested by one of our listeners on Chavah Messianic Radio.", song.Name, songArtist),
- MoreInfoUri = song.GetAbsoluteSongUri()
- };
- session.Store(activity);
- session.AddRavenExpiration(activity, requestExpiration);
- session.SaveChanges();
- }
- }
- return GetSongById(songId);
- }
- private bool HasRecentPendingSongRequest(long songId, IDocumentSession session)
- {
- var recent = DateTime.Now.Subtract(TimeSpan.FromMinutes(30));
- return session
- .Query<SongRequest>()
- .Any(s => s.SongId == songId && s.DateTime >= recent);
- }
- private bool HasManyPendingSongRequestForArtist(string artist, IDocumentSession session)
- {
- var recent = DateTime.Now.Subtract(TimeSpan.FromMinutes(60));
- var many = 2;
- return session
- .Query<SongRequest>()
- .Count(s => s.Artist == artist && s.DateTime >= recent) >= many;
- }
- private void OnSongPlayedForClient(string userId)
- {
- Task.Factory.StartNew(() => RecordSongPlayedForUser(userId));
- }
- private Song GetSongForClientWithLikeWeights(string userId)
- {
- // Song weights algorithm described here:
- // http://stackoverflow.com/questions/3345788/algorithm-for-picking-thumbed-up-items/3345838#3345838
- var allSongs = cachedSongs.Value;
- var likeDislikeSongs = Dependency.Get<LikesCache>().ForClient(userId);
- var songsWithWeight =
- (
- from song in allSongs
- let likeStatus = GetLikeStatusForSong(song, likeDislikeSongs)
- select new
- {
- Weight = GetSongWeight(song, likeStatus),
- Like = likeStatus,
- Info = song
- }
- ).ToArray();
- var totalWeights = songsWithWeight.Sum(s => s.Weight);
- var randomWeight = RandomDoubleWithMaxValue(totalWeights);
- var runningWeight = 0.0;
- foreach (var song in songsWithWeight)
- {
- var newWeight = runningWeight + song.Weight;
- if (randomWeight >= runningWeight && randomWeight <= newWeight)
- {
- return song.Info.ToDto(song.Like);
- }
- runningWeight = newWeight;
- }
- var errorMessage = "Unable to find random song. This should never happen. Random weight chosen was " + randomWeight.ToString() + ", max weight was " + totalWeights.ToString();
- ChavahEntities.LogInNewContext(errorMessage);
- throw new Exception(errorMessage);
- }
- private string GetUserIdFromRequest()
- {
- var userId = Match.Value(Request.Cookies["userIdValue"])
- .IfNotNull(c => c.Value)
- .Evaluate();
- if (string.IsNullOrEmpty(userId))
- {
- throw new InvalidOperationException("Doesn't have a user Id");
- }
- return userId;
- }
- private static double GetSongWeight(SongInfo song, SongLikeStatus likeStatus)
- {
- var likeWeightMultiplier = GetSongLikeWeightMultiplier(likeStatus);
- var shabbatWeight = GetShabbatWeight(song, likeStatus);
- var popularityWeight = GetPopularityWeight(song);
- var proposedFinalWeight = (shabbatWeight + popularityWeight) * likeWeightMultiplier;
- return proposedFinalWeight.MinMax(.001, 10);
- }
- private static double GetSongLikeWeightMultiplier(SongLikeStatus likeStatus)
- {
- const double normalMultiplier = 1;
- const double likeMultiplier = 1.5;
- const double dislikeMultiplier = 0.01;
- return Match.Value(likeStatus)
- .With(SongLikeStatus.Like, likeMultiplier)
- .With(SongLikeStatus.Dislike, dislikeMultiplier)
- .DefaultTo(normalMultiplier);
- }
- private static double GetPopularityWeight(SongInfo song)
- {
- const double veryUnpopularWeight = 0.01;
- const double unpopularWeight = 0.07;
- const double normalWeight = 1;
- const double likedWeight = 1.45;
- const double popularWeight = 1.65;
- const double veryPopularWeight = 2.05;
- const double extremelyPopularWeight = 2.2;
- return Match.Value(song.CommunityRank)
- .With(v => v < -5, veryUnpopularWeight)
- .With((-4).Through(-1), unpopularWeight)
- .With(0.Through(9), normalWeight)
- .With(10.Through(29), likedWeight)
- .With(30.Through(49), popularWeight)
- .With(50.Through(100), veryPopularWeight)
- .With(v => v >= 101, extremelyPopularWeight)
- .DefaultTo(normalWeight);
- }
- private static double GetShabbatWeight(SongInfo song, SongLikeStatus likeStatus)
- {
- const int shabbatBoost = 7;
- return Match.Value(song)
- .With(_ => likeStatus == SongLikeStatus.Dislike || song.CommunityRank < -3, 0)
- .With(s => DateTime.Now.IsShabbat() && s.IsShabbatSong, shabbatBoost);
- }
- private static double RandomDoubleWithMaxValue(double maxValueInclusive)
- {
- var randomValue = random.NextDouble();
- var desiredValue = randomValue * maxValueInclusive;
- var desiredValueTrimmed = Math.Min(maxValueInclusive, desiredValue);
- return desiredValueTrimmed;
- }
- private SongLikeStatus GetLikeStatusForSong(SongInfo song, SongLike[] userSongPreferences)
- {
- var likeDislikeForThisSong = userSongPreferences.FirstOrDefault(l => l.SongId == song.Id);
- return likeDislikeForThisSong.ToSongLikeEnum();
- }
- private void RecordSongPlayedForUser(string userId)
- {
- visits.AddOrUpdate(userId, DateTime.Now, (_, __) => DateTime.Now);
- using (var session = raven.OpenSession())
- {
- var user = session.Load<User>(userId);
- if (user != null)
- {
- user.TotalPlays += 1;
- user.LastVisit = DateTime.Now.Date;
- }
- session.SaveChanges();
- }
- }
- [HttpPost]
- public ActionResult LikeById(long songId)
- {
- UpdateLikeStatus(this.GetUserIdFromRequest(), songId, SongLikeStatus.Like);
- StoreLikeActivity(songId);
- return Json(true);
- }
- [HttpPost]
- public ActionResult DislikeById(long songId)
- {
- UpdateLikeStatus(this.GetUserIdFromRequest(), songId, SongLikeStatus.Dislike);
- return Json(true);
- }
- public ActionResult GetSongByAlbum(string album, string artist)
- {
- var albumSongs = cachedSongs.Value.Where(s => string.Equals(s.Album, album, StringComparison.OrdinalIgnoreCase));
- var song = albumSongs.RandomOrder().FirstOrDefault();
- if (song != null)
- {
- return GetSongById(song.Id);
- }
- else
- {
- ChavahEntities.LogInNewContext("Unable to find an album matching name " + album);
- return GetSongForClient();
- }
- }
- public ActionResult GetSongByArtist(string artist)
- {
- var artistSongs = cachedSongs.Value.Where(s => string.Equals(s.Artist, artist, StringComparison.OrdinalIgnoreCase));
- var song = artistSongs.RandomOrder().FirstOrDefault();
- if (song != null)
- {
- return GetSongById(song.Id);
- }
- else
- {
- ChavahEntities.LogInNewContext("Unable to find a song name starting with " + artist);
- return GetSongForClient();
- }
- }
- private static List<SongInfo> GetCachedSongsWithLogging()
- {
- try
- {
- return GetCachedSongs();
- }
- catch (Exception error)
- {
- using (var entities = new ChavahEntities())
- {
- entities.Logs.AddObject(new Chavah.Data.Log() { Message = error.ToString(), TimeStamp = DateTime.Now });
- entities.SaveChanges();
- }
- throw;
- }
- }
- static void UpdateSongPurchaseInfo()
- {
- using (var entities = new ChavahEntities())
- {
- var knownPurchaseLinks = new Dictionary<string, string>()
- {
- { "Aviad Cohen", "http://aviadcohen.com/shop.cfm" },
- { "Alicia Smith", "http://www.cduniverse.com/productinfo.asp?pid=7345816" },
- { "Alyssa Kennedy", "http://www.youtube.com/watch?v=lSSXeuXvYWU" },
- { "Avner & Rachel Boskey", "http://www.davidstent.org" },
- { "Baruch HaShem Worship Team", "http://baruchhashemsynagogue.org/" },
- { "Barry & Batya Segal", "http://www.visionforisrael.com" },
- { "Bruce & Lynne Patterson", "http://www.ldpatterson.com" },
- { "Bruce Cohen", "http://www.bethelnyc.org/meet_the_rabbi/r'bruce_products.htm" },
- { "Carlos Perdomo", "http://www.carlosperdomoministries.com/" },
- { "Carolyn Hyde", "http://www.heartofg-d.org" },
- { "Christene Jackman", "http://christenejackman.com" },
- { "Christopher Mann", "http://www.kadoshmann.com" },
- { "Deborah Kline-Iantorno", "http://deborahkline-iantorno.com" },
- { "Devora Clark", "http://devoraclark.bandcamp.com/" },
- { "Deanne Glenn", "http://www.deannemusic.com/" },
- { "Deanne Shallenberger", "http://www.deannemusic.com/" },
- { "Downpour", "http://www.thedownpourband.com/" },
- { "Elisheva Shomron", "http://www.last.fm/music/Elisheva+Shomron" },
- { "Ephraim Ben Yoseph", "http://www.reverbnation.com/ephraimbenyoseph" },
- { "Giselle", "http://www.cdbaby.com/cd/giselle33" },
- { "Greg Silverman", "http://www.gregsilverman.com/" },
- { "Hananyah Naftali", "https://itunes.apple.com/il/album/your-presence/id680795419?i=680795748" },
- { "Helen Shapiro", "http://www.mannamusic.co.uk/materialspage/materialspg.htm" },
- { "Hillel Ben Yochanan", "http://www.facebook.com/hillel70#!/pages/Hillel/214029558644294?sk=app_178091127385" },
- { "Israel's Hope", "http://www.fvgifts.com/music.html" },
- { "Joel Chernoff", "http://www.lambmessianicmusic.com/lamb_05_joelchernoff_mn.html" },
- { "Jonathan Kegans", "http://www.jonathankegans.com/index.html" },
- { "Jonathan Settel", "http://www.settel.org" },
- { "Joshua Aaron", "http://worshipinisrael.com/" },
- { "Joshua Rosen", "http://joshuarosen.bandcamp.com" },
- { "Justin Black", "http://www.facebook.com/profile.php?id=100000597792846#!/profile.php?id=100002453000204" },
- { "Karen Davis", "http://www.messianicweb.com/Music/GOTN/Davis/" },
- { "Kathy Shooster", "http://kathyshoostermusic.com/?page_id=10" },
- { "Kehilat Ha Ma'ayan Congregation", "http://kehilat-hamaayan.org.il/" },
- { "Lamb", "http://www.lambmessianicmusic.com/lamb_04_lamb_mn.html" },
- { "Lee Rothman", "http://www.purevolume.com/hisway" },
- { "Lenny & Varda Harris", "http://www.lennyandvarda.com/" },
- { "Leslie Ann", "http://www.songsofleslieann.com/" },
- { "Lev Shelo", "http://levshelo.com/store.cfm" },
- { "Lynne McDowell", "http://lynnemcdowell.com/" },
- { "Magen David", "http://www.cdbaby.com/cd/magendavid" },
- { "Martin Sarvis", "http://www.cdbaby.com/cd/martinsarvis" },
- { "Marty Goetz", "http://www.martygoetz.com/products/products.php" },
- { "Micha'el Eliyahu BenDavid", "http://emetzionmusic.com/index.php?option=com_maianmedia&view=music&Itemid=84" },
- { "Michael Nissim", "http://www.fvgifts.com/music.html" },
- { "Misha Goetz", "http://mishagoetz.com/" },
- { "Mishkanim", "http://mishkanim.com/" },
- { "Meha Shamayim", "http://www.galileeofthenations.com/" },
- { "Mijael Hayom", "http://www.librerialosolivos.com/index.php?cPath=615_70_267" },
- { "Natalie Isaacs", "http://natalieisaacs.com" },
- { "Natasha Kraus-Reynolds", "http://www.facebook.com/pages/Natasha-Kraus-Reynolds/199220550117812" },
- { "New Wine", "http://messianic.beithassedel.org/music.htm" },
- { "Paul Wilbur", "https://wilburministries.com/" },
- { "Philip Stanley Klein", "http://www.yeshuasongs.com" },
- { "Roeh Israel Worship Team", "http://www.storesonline.com/site/634305/page/916905" },
- { "Roman and Alaina", "http://romanandalaina.com/" },
- { "Ross", "http://www.andrewandsarahross.com" },
- { "Sally Klein O'Connor", "http://www.sallykleinoconnor.com/" },
- { "Sha'rei HaShamayim", "http://www.hebraic.info/Hebraic/Music.html" },
- { "Sharon Wilbur", "http://sharonwilbur.com" },
- { "Sons of Korah", "http://www.sonsofkorah.com" },
- { "Will Spires", "http://www.myspace.com/WiLLiamSpires/" },
- { "Steve McConnell", "http://www.amazon.com/s/ref=pd_lpo_k2_dp_sr_sq_top?ie=UTF8&keywords=steve%20mcconnell%20messianic%20music&index=blended&pf_rd_p=486539851&pf_rd_s=lpo-top-stripe-1&pf_rd_t=201&pf_rd_i=B000CAG4US&pf_rd_m=ATVPDKIKX0DER&pf_rd_r=1F61C18YZ8YW8XH4D8RH" },
- { "Ted Pearce", "http://www.tedpearce.com/music/" },
- { "Tents of Mercy", "http://www.fvgifts.com/music.html" },
- { "The Hebraism Music Project", "http://www.hebraism.org/Hebraism/Home.html" },
- { "The Lumbrosos", "http://thelumbrosos.com/" },
- { "Troy Mitchell", "http://ffoz.com/troy-mitchell-yoke-of-the-king-music-audio-cd.html" }
- };
- entities.SongInfoes
- .AsEnumerable()
- .Where(s => knownPurchaseLinks.ContainsKey(s.Artist))
- .Where(s => s.PurchaseUrl != knownPurchaseLinks[s.Artist])
- .ForEach(s => s.PurchaseUrl = knownPurchaseLinks[s.Artist]);
- entities.SaveChanges();
- }
- }
- private static void ClearOutOldLogs()
- {
- using (var entities = new ChavahEntities())
- {
- var monthAgo = DateTime.Now.Subtract(TimeSpan.FromDays(30));
- entities
- .Logs
- .Where(l => l.TimeStamp < monthAgo)
- .ForEach(entities.Logs.DeleteObject);
- }
- }
- private static List<SongInfo> GetCachedSongs()
- {
- var songsOnDisk = Directory.EnumerateFiles(Constants.MessianicMusicPath, "*.mp3").Select(Path.GetFileName).Memoize();
- var cachedSongsList = new List<SongInfo>(1800);
- using (var entities = new ChavahEntities())
- {
- // Ensure they're all in the database.
- var songsInDatabase = entities.SongInfoes.ToArray();
- songsOnDisk
- .Where(fileName => !songsInDatabase.Any(s => string.Equals(s.FileName, fileName, StringComparison.InvariantCultureIgnoreCase)))
- .Select(fileName => CreateSongFromDisk(fileName))
- .Do(s => entities.Log("Adding song to DB: " + s.FileName))
- .Do(entities.SongInfoes.AddObject)
- .Concat(songsInDatabase)
- .ForEach(cachedSongsList.Add);
- // Remove from the database any that are missing on disk.
- if (songsOnDisk.Any())
- {
- var songsRemovedFromDisk =
- (
- from dbSong in songsInDatabase
- where !songsOnDisk.Any(s => dbSong.FileName == s)
- select dbSong
- ).ToArray();
- // If we're missing a bunch of songs, something is wrong, don't delete.
- if (songsRemovedFromDisk.Any() && songsRemovedFromDisk.Length < 100)
- {
- songsRemovedFromDisk
- .Do(entities.SongInfoes.DeleteObject)
- .Do(s => cachedSongsList.Remove(s))
- .ForEach(s => entities.Log("Removing song from DB: " + s.FileName));
- }
- }
- entities.SaveChanges();
- cachedSongsList.ForEach(entities.SongInfoes.Detach);
- }
- return cachedSongsList;
- }
- private static SongInfo CreateSongFromDisk(string fileName)
- {
- var song = new SongInfo { FileName = fileName };
- song.FillSongDetailsFromFileName(fileName);
- return song;
- }
- private void StoreLikeActivity(long songId)
- {
- using (var session = raven.OpenSession())
- {
- var song = cachedSongs.Value.FirstOrDefault(s => s.Id == songId);
- if (song != null)
- {
- var songRankString = Match
- .Value(song.CommunityRank)
- .With(i => i > 0, "+")
- .DefaultTo("")
- .Evaluate() + song.CommunityRank.ToString();
- var songArtist = Match.Value(GetArtistTwitterHandle(song.Artist))
- .IfNotNull(h => h)
- .DefaultTo(song.Artist)
- .Evaluate();
- var activity = new Activity
- {
- DateTime = DateTime.Now,
- Title = string.Format("{0} - {1} was thumbed up", song.Artist, song.Name),
- Description = string.Format("\"{0}\" by {1} was thumbed up ({2}) on Chavah Messianic Radio.", song.Name, songArtist, songRankString),
- MoreInfoUri = song.GetAbsoluteSongUri()
- };
- session.Store(activity);
- session.AddRavenExpiration(activity, DateTime.UtcNow.AddDays(30));
- session.SaveChanges();
- }
- }
- }
- private static void UpdateLikeStatus(string userId, long songId, SongLikeStatus likeStatus)
- {
- var hasReversedLikeStatus = false;
- using (var session = RavenStore.Db.OpenSession())
- {
- var existingLike = session.Query<SongLike>().FirstOrDefault(l => l.SongId == songId && l.UserId == userId);
- if (existingLike != null)
- {
- if (existingLike.LikeStatus == likeStatus.ToBool())
- {
- // You already like/dislike this song. There's nothing to update.
- return;
- }
- hasReversedLikeStatus = true;
- existingLike.LikeStatus = likeStatus.ToBool();
- }
- else
- {
- var newLikeStatus = new SongLike
- {
- LikeStatus = likeStatus.ToBool(),
- SongId = (int)songId,
- UserId = userId,
- Date = DateTime.Now
- };
- session.Store(newLikeStatus);
- }
- session.SaveChanges();
- }
- // Update the community rank.
- using (var entities = new ChavahEntities())
- {
- var song = entities.SongInfoes.FirstOrDefault(s => s.Id == songId);
- if (song != null)
- {
- var adjustmentAmount = hasReversedLikeStatus ? 2 : 1;
- song.CommunityRank += likeStatus == SongLikeStatus.Like ? adjustmentAmount : (-1 * adjustmentAmount);
- }
- entities.SaveChanges();
- }
- Dependency.Get<LikesCache>().OnLikesChanged(userId);
- // Update the in-memory item.
- var adjustementAmount = Match.Value(likeStatus)
- .With(s => s == SongLikeStatus.Like && !hasReversedLikeStatus, 1)
- .With(s => s == SongLikeStatus.Like && hasReversedLikeStatus, 2)
- .With(s => s == SongLikeStatus.Dislike && !hasReversedLikeStatus, -1)
- .With(s => s == SongLikeStatus.Dislike && hasReversedLikeStatus, -2)
- .Evaluate();
- cachedSongs.Value
- .Where(s => s.Id == songId)
- .Take(1)
- .ForEach(s => s.CommunityRank += adjustementAmount);
- }
-
- //private static void UpdateLikeStatus(Guid clientId, Uri songUri, SongLike likeStatus)
- //{
- // var songName = songUri.Segments.Last().Replace("%20", " ");
- // using (var entities = new ChavahEntities())
- // {
- // var song = entities.SongInfoes.FirstOrDefault(s => s.FileName == songName);
- // if (song != null)
- // {
- // UpdateLikeStatus(clientId, song.Id, likeStatus);
- // }
- // }
- //}
- }
- }