Esempio n. 1
0
        public void AddTrack(FmShellArguments args)
        {
            LOGGER.Info("Adding track by file path");
            var audioSource = new Mp3FileAudioSource(args.Args[0].ToString());

            RadioCastServer.AddTrack(audioSource);
        }
Esempio n. 2
0
        private static Task StartRadioFeeder(RadioCastServer radioCastServer, MongoWrapper mongoWrapper)
        {
            var userCollection = mongoWrapper.Database.GetCollection <Models.Musician>(nameof(Models.User));

            var songSortBuilder = new SortDefinitionBuilder <ProjectedMusicianSong>();
            var songSort        = songSortBuilder
                                  .Descending(nameof(ProjectedMusicianSong.Score));

            var userFilterBuilder = new FilterDefinitionBuilder <Models.Musician>();
            var userFilter        = userFilterBuilder.And
                                    (
                GeneralUtils.NotDeactivated(userFilterBuilder),
                userFilterBuilder.AnyEq("_t", nameof(Models.Musician))
                                    );

            var songFilterBuilder = new FilterDefinitionBuilder <ProjectedMusicianSong>();
            var songFilter        = songFilterBuilder.And
                                    (
                GeneralUtils.NotDeactivated(songFilterBuilder, s => s.Song),
                songFilterBuilder.Eq(s => s.Song.RadioAuthorized, true),
                songFilterBuilder.Eq(s => s.Song.Original, true)
                                    );

            var projectionBuilder = new ProjectionDefinitionBuilder <Models.Musician>();
            var projection        = projectionBuilder.Include(m => m.FullName).Include(m => m.Songs);

            var fsBucket = new GridFSBucket <ObjectId>(mongoWrapper.Database);

            var trackHistory = new List <(IAudioSource, ProjectedMusicianSong)>();

            var onTrackChangedTE = new ManualResetEvent(true);

            radioCastServer.OnTrackChanged += async(s, e) =>
            {
                List <(IAudioSource, ProjectedMusicianSong)> myTrackHistory;
                lock (trackHistory)
                {
                    myTrackHistory = trackHistory.ToList();
                }

                RadioInfoController.CurrentSong = myTrackHistory.Where(th => th.Item1.Equals(e.NewTrack)).Select(th => th.Item2).LastOrDefault();
                LOGGER.Info("Now playing: {}", JsonConvert.SerializeObject(RadioInfoController.CurrentSong));

                onTrackChangedTE.Set();

                if (e.OldTrack == null)
                {
                    return;
                }

                var oldTrack      = myTrackHistory.Where(th => th.Item1.Equals(e.OldTrack)).Select(th => th.Item2).LastOrDefault();
                var oldMusicianId = oldTrack._id;
                var oldTrackId    = oldTrack.Song._id;

                var musSongFilterBuilder = new FilterDefinitionBuilder <Models.Musician>();

                var musSongFilter = musSongFilterBuilder.And
                                    (
                    musSongFilterBuilder.Eq(m => m._id, oldMusicianId),
                    musSongFilterBuilder.ElemMatch(m => m.Songs, sg => sg._id == oldTrackId)
                                    );
                var musSongUpdate = new UpdateDefinitionBuilder <Models.Musician>()
                                    .Inc($"{nameof(Models.Musician.Songs)}.$.{nameof(Song.TimesPlayedRadio)}", 1);

                await userCollection.UpdateOneAsync(musSongFilter, musSongUpdate);

                // Remove oldest, only keep 5 in history
                if (myTrackHistory.Count > 5)
                {
                    lock (trackHistory)
                    {
                        trackHistory.RemoveAt(0);
                    }
                }
            };

            return(Task.Run(async() =>
            {
                while (true)
                {
                    List <(IAudioSource, ProjectedMusicianSong)> myTrackHistory;
                    lock (trackHistory)
                    {
                        myTrackHistory = trackHistory.ToList();
                    }

                    var lookupStageRandomArr = $@"
                    {{
                        $lookup:
                        {{
                            from: ""randomNumbers"",
                            pipeline:
                            [
                                {{ $sample: {{ size: 2 }} }}
                            ],
                            as: ""RandomArr""
                        }}
                    }}
                    ";

                    // OK Vezes totais que a música foi tocada * 0.5
                    // OK Vezes totais que a música foi tocada na rádio * -1
                    // OK Se música está presente na lista das últimas 5 tocadas, -100
                    // OK Se o autor da música está presente na lista das últimas 5 tocadas, -50
                    // OK Pontuação aleatória para cada música, entre - 10 e + 10
                    // OK Número de dias desde o cadastramento da música * -1
                    // OK   Há uma chance de 5% de multiplicar a pontuação resultante por -1 (efeito nostalgia)

                    var scoreStage = $@"
                    {{
                        $addFields:
                        {{
                            ""Score"":
                            {{
                                $add:
                                [
                                    {{
                                        $multiply: [ ""$Song.timesPlayed"", 0.5 ]
                                    }},
                                    {{
                                        $multiply: [ ""$Song.timesPlayedRadio"", -1 ]
                                    }},
                                    {{
                                        $cond:
                                        {{
                                            if:
                                            {{
                                                $in: [ ""$_id"", [ {myTrackHistory.Select(th => $"ObjectId(\"{th.Item2._id.ToString()}\")").DefaultIfEmpty("").Aggregate((s1, s2) => $"{s1.TrimEnd(',')},{s2.TrimEnd(',')},").TrimEnd(',')} ] ]
                                            }},
                                            then: -50,
                                            else: 0
                                        }}
                                    }},
                                    {{
                                        $cond:
                                        {{
                                            if:
                                            {{
                                                $in: [ ""$Song._id"", [ {myTrackHistory.Select(th => $"ObjectId(\"{th.Item2.Song._id.ToString()}\")").DefaultIfEmpty("").Aggregate((s1, s2) => $"{s1.TrimEnd(',')},{s2.TrimEnd(',')},").TrimEnd(',')} ] ]
                                            }},
                                            then: -100,
                                            else: 0
                                        }}
                                    }},
                                    {{
                                        $add:
                                        [ 
                                            {{
                                                $multiply:
                                                [
                                                    {{ $toDecimal: {{ $arrayElemAt: [""$RandomArr.decimal"", 0] }} }},
                                                    21
                                                ]
                                            }}, 
                                            -10
                                        ]
                                    }},
                                    {{
                                        $multiply:
                                        [
                                            {{
                                                $divide:
                                                [
                                                    {{
                                                        $subtract:
                                                        [
                                                            {{ $toDate: ""{DateTime.UtcNow.ToString("o")}"" }},
                                                            {{ $toDate: ""$Song._id"" }}
                                                        ]
                                                    }},
                                                    NumberLong(""86400000"")
                                                ]
                                            }},
                                            {{
                                                $cond:
                                                {{
                                                    if:
                                                    {{
                                                        $gt:
                                                        [
                                                            {{ $toDecimal: {{ $arrayElemAt: [""$RandomArr.decimal"", 1] }} }},
                                                            NumberDecimal(""0.1"")
                                                        ]
                                                    }},
                                                    then: -1,
                                                    else: 1
                                                }}
                                            }}
                                        ]
                                    }}
                                ]
                            }}
                        }}
                    }}";

                    LOGGER.Info("Score stage generated MongoDB query: {}", scoreStage);

                    var pipeline = PipelineDefinitionBuilder
                                   .For <Models.Musician>()
                                   .Match(userFilter)
                                   .Unwind(m => m.Songs, new AggregateUnwindOptions <Models.Musician>
                    {
                        PreserveNullAndEmptyArrays = false,
                        IncludeArrayIndex = null,
                    })
                                   .Project(m => new ProjectedMusicianSong
                    {
                        _id = m._id,
                        Song = (Song)m.Songs,
                        Score = 1,
                    })
                                   .Match(songFilter)
                                   .AppendStage <Musician, ProjectedMusicianSong, ProjectedMusicianSong>(lookupStageRandomArr)
                                   .AppendStage <Musician, ProjectedMusicianSong, ProjectedMusicianSong>(scoreStage)
                                   .Sort(songSort)
                                   .Limit(1);

                    if (LOGGER.IsDebugEnabled)
                    {
                        LOGGER.Debug("Pipeline generated MongoDB query for song: {}", pipeline.ToString());
                    }

                    var findTask = userCollection.AggregateAsync(pipeline, new AggregateOptions
                    {
                        AllowDiskUse = true,
                        BatchSize = 1,
                        UseCursor = true,
                        Comment = "Radio Aggregate Query",
                        TranslationOptions = new ExpressionTranslationOptions
                        {
                            StringTranslationMode = AggregateStringTranslationMode.CodePoints
                        }
                    });

                    var findResult = await findTask;
                    var firstSong = findResult.SingleOrDefault();
                    // If no songs, wait a minute before checking again
                    // Mostly not to strain the CPU on development environments
                    if (firstSong?.Song == null)
                    {
                        Thread.Sleep(TimeSpan.FromMinutes(1));
                        continue;
                    }
                    LOGGER.Info("Next selected song: {}", JsonConvert.SerializeObject(firstSong));
                    var audioRef = firstSong.Song.AudioReference;
                    var gridId = audioRef._id;
                    var fileStreamTask = fsBucket.OpenDownloadStreamAsync(gridId, new GridFSDownloadOptions
                    {
                        Seekable = true,
                        CheckMD5 = false,
                    });
                    var audioSource = new Mp3FileAudioSource(await fileStreamTask, firstSong.Song.Name);
                    // Wait for the radio to need more songs before we add the track we have on our hands
                    while (radioCastServer.TrackCount > 1)
                    {
                        onTrackChangedTE.WaitOne();
                        onTrackChangedTE.Reset();
                    }
                    lock (trackHistory)
                    {
                        trackHistory.Add((audioSource, firstSong));
                    }
                    radioCastServer.AddTrack(audioSource);
                }
            }));
        }