aboutsummaryrefslogblamecommitdiffstats
path: root/server/media.erl
blob: 165338b9dd85048224bc1912f3f135d63a700e81 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
               
                                                                                   
                                    
                            







                                                                                                               
                                                          
 







                                                                                     
                                                                           
                                                   





                                                                 
 
                                   


                                                          
                                                         





                                                                                 
                                                                   
 
                  
                                                        
                            
 
 

                                                                                                     





                                                                                                                    




                                                                                                                               
 





                                                                              
                                          
                                                                     
 

                                                                           
                         


                                                    
 
                                                       
 
                     
                
                                                                          

                                              

            



                                                             
                                                                 



                                              


                                                                                                                                
 
                      
                                  
                                        
                                                                                                 
                               
                                                
    
                                                            



                                                                     
                                                                               









                                                

                            








                                                

                                 







                                              
             
 


                                              
                                                
                                 


                          

                  


                                              
                                                 
                                 


                          


                                                                                    
                              

                        



                                        

                
-module(media).
-export([init/0,insert/3, ask/2, all/0, play/2, vote/2, devote/2, lock_process/2]).
-define(TESTPATTERN, "../ac/*.mp3").
-define(TIMEOUT, 100000000).

% Since we are not willing to calculate and deliver all the id3 tags everytime they are requested,
% we try to get something persistent with mnesia.
% Concerning the parsing of id3tags we use the library id3v2 by Brendon Hogger. For detailed information take a
% look at the header of the library.

% What is an entry in our database made of? By the way the filepath includes the filename.

-record(track, {title, artist, votes, locked, filepath }).

% Before this module becomes usable, we must initialize it with the following steps:
% 1. Initialize the mnesiadatabase and create the table within it.
% 2. Parse the mp3s in the working directory and add them to the database
% 3. Get into a loop so the database can be actually queried and files can be played.

init() ->
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(track, [{attributes, record_info(fields, track)}]),
    read_files(filelib:wildcard(?TESTPATTERN),0,0),
    io:format("Initialisation of mnesia successful.~n"),
    io:format("Starting to play music~n"),
    start_playing().
    
% uses the algorithm of Brendon Hogger to split the id3-tags and 
% inserts the songs into the Database

read_files([FN|Rest],Total,Fail) ->
	case id3v2:read_file(FN) of
	{ok, Props} -> % insert entry into mnesia DB
		Artist = proplists:get_value(tpe1, Props),
	   	Title = proplists:get_value(tit2, Props),
		insert(bitstring_to_list(Artist), bitstring_to_list(Title), FN),
		read_files(Rest, Total+1, Fail);
	not_found -> read_files(Rest, Total+1, Fail+1)
	end;
read_files([],Total,Fail) -> io:format("Total: ~w, Failed: ~w~n", [Total, Fail]).

% Our Runloop to play music all the time, play waits on exit_status

start_playing() ->
	{Artist, Title} = search_best(media:all(), 0,0),
	play(Artist, Title).


% Basic insertion of entrys into the database. Some entries are left out because they are 0 or false.

insert(Artist, Title, Filepath) ->
    F = fun() ->
                mnesia:write(#track{artist = Artist, title = Title, votes = 0, locked = false, filepath = Filepath})
        end,
    mnesia:transaction(F).

% Of course we need a query to find out whats actually the most wished for track.
% We will do it by requesting all the records from the database and then iteramte over just taking a look at the vote
% variable, so it is like list of integers. In case no tracks were voted for we just take the first track we find and play it. 
% Of course it is locked afterwards so another will be choosen.


search_best([Head|Rest], Max_Votes, Track) -> 
	if 
	((Max_Votes =<  Head#track.votes) and (Head#track.locked == false)) ->
		search_best(Rest, Head#track.votes, Head);
	true -> search_best(Rest, Max_Votes, Track)
	end;
search_best([], 0, 0) -> reset_all(all());
search_best([], _, Track) -> {Track#track.artist, Track#track.title}.

% if nothing is playable anymore just reset them and start playing again...

reset_all([Head|Rest]) ->
	unlock(Head#track.artist, Head#track.title),
	reset_all(Rest);
reset_all([]) -> ok.

% We want to query in order to simplify the next calls.

ask(Artist, Title) ->
    F = fun() ->
		mnesia:match_object({track, Title, Artist, '_', '_', '_'})
	end,
    {atomic, Results} = mnesia:transaction(F),
    Results.

% Just in case the client is interested in everything we got.

all() ->
    F = fun() ->
                mnesia:match_object({track, '_','_','_','_','_'})
        end,
    {atomic, Results} = mnesia:transaction(F),
    Results.

% We want to play mp3s from our database. After we play them they will become locked.
% In practice we are going to set their locked variable to true and spawn a process which will unlock them after a certain time.
% Well this could be considered abuse.

play(Artist, Title) ->
    [Head|_] = ask(Artist, Title),
    {_, Title, Artist, _, _, Fp} = Head,
    Port = erlang:open_port({spawn_executable, "/usr/bin/mplayer"}, [{args, [Fp]}, exit_status]),
    reset_votes(Artist, Title),
    spawn(media, lock_process, [Artist, Title]),
    
    io:format("playing: ~s, Artist: ~s~n", [Title, Artist]),
    receive
		{Port, {exit_status, 0}} -> start_playing();
		{Port, {exit_status, S}} -> throw({commandfailed, S})
    end.
%Increase the vote in the database so it will hopefully be played in the future

vote(Artist, Title) ->
    F = fun() ->
                [Head|_] = ask(Artist, Title),
                Votes = Head#track.votes + 1,
                New = Head#track{votes = Votes},
                mnesia:write(New)
        end,
    mnesia:transaction(F).

% decrease votes in database

devote(Artist, Title) ->
    F = fun() ->
                [Head|_] = ask(Artist, Title),
                Votes = Head#track.votes - 1,
                New = Head#track{votes = Votes},
                mnesia:write(New)
        end,
    mnesia:transaction(F).

% Reset votes to zero in database

reset_votes(Artist, Title) ->
    F = fun() ->
                [Head|_] = ask(Artist, Title),
                New = Head#track{votes = 0},
                mnesia:write(New)
        end,
    mnesia:transaction(F).
    
% Lock a song

lock(Artist, Title) ->
    F = fun() ->
                [Head|_] = ask(Artist, Title),
                New = Head#track{locked = true},
                mnesia:write(New)
        end,
    mnesia:transaction(F).

% Unlock a song...

unlock(Artist, Title) ->
    F = fun() ->
                [Head|_] = ask(Artist, Title),
                New = Head#track{locked = false},
                mnesia:write(New)
        end,
    mnesia:transaction(F).

% Lock a song if it was just played, after a Timeout it will be unlocked automaticly
% If all songs are locked, all will be unlocked.

lock_process(Artist, Title) ->
    lock(Artist, Title),
    receive 

    after ?TIMEOUT ->
              unlock(Artist, Title) end.