<commit_before>require 'sinatra'
require 'sinatra/browserid'
require 'json'
require 'filemagic'
require 'open3'
require 'http'
require 'uri'
require 'open-uri'
require 'zip'
require 'xmlsimple'

require './database.rb'

set :sessions, true
# session-hijacking-protection were triggered by ajax-requests
set :protection, except: :session_hijacking
# use server with streaming support (used when transcoding) 
set :server, :puma

helpers do
    include Rack::Utils
    alias_method :h, :escape

    def isAdmin?
        if authorized?
            if Database.new.getOption('admin') == authorized_email
                return true
            end
        end
        return false
    end

    def isGuest?
        if authorized?
            begin
                if Database.new.getOption('guest').split(";").include? authorized_email
                    return true
                end
            rescue => error
                puts error
            end
        end
        return false
    end
    
    def protected!
        unless isAdmin?
            throw(:halt, [401, "Not authorized\n"])
        end
    end

    def semiProtected!
        unless isAdmin? || isGuest?
            throw(:halt, [401, "Not authorized\n"])
        end
    end

    def firstUse?
        return Database.new.firstUse?
    end
    
    def emptyMediaDB?
        return Database.new.emptyMediaDB?
    end
end

get '/' do
    if ! (isAdmin? || isGuest?)
        erb :login
    else
        db = Database.new
        if db.getOption("mediaDir") == nil
            erb :setDB, :locals => {:mediaDir => nil}
        else
            erb :index, :locals => {:mediaDB => db.getMediaDB}
        end
    end
end

get '/setDB' do
    session[:origin] = back
    erb :setDB, :locals => {:mediaDir => Database.new.getOption("mediaDir")}
end

get '/settings' do
    protected!
    session[:origin] = back
    guests = Database.new.getOption("guest")
    guests = guests.split(";") if guests != nil
    erb :settings, :locals => {:guests => guests}
end

post '/addAdmin' do
    db = Database.new
    if db.firstUse? && ! authorized_email.empty?
        db.setOption('admin', authorized_email)
        redirect to('/')
    else
        'Error adding admin: param missing or admin already set'
    end
end

get '/logout' do
    logout!
    return "Logged out"
end

post '/setOption' do
    protected!
    if params[:name].is_a?(String)
        Database.new.setOption(params[:name], params[:value])
    else
        # we came from the settings, so input is more complex
        params[:name].each do |name, values|
            valueString = ""
            values.each do |key, value|
                valueString+=value + ";"
            end
            Database.new.setOption(name, valueString.chomp(";"))
        end
    end
    origin = session[:origin]
    # when setOption wasn't called first, like with the design, origin is old, so unset it
    session.delete(:origin)
    redirect origin if origin != nil
    redirect back
end

get '/guestInput' do
    protected!
    erb :guestInput, :locals => {:index => params[:index], :mail => ""}
end

post '/updateDB' do
    protected!
    session[:updateTotal] = Database.new.updateDB
    "Done"
end

get '/updateProgress' do
    Database.new.updateProgress.to_s
end

get '/updateTotal' do
    session[:updateTotal].to_s
end

get '/updateDone' do
    Database.new.updateDone.to_s
end

get '/mediaDB' do
    semiProtected!
    erb :mediaDB, :locals => {:mediaDB => Database.new.getMediaDB}
end

get '/tracks' do
    semiProtected!
    tracks = Database.new.getTracks(params[:artist], params[:album]).delete_if{|key, value| key.is_a? Integer}
    JSON(tracks)
end

get '/lyrics' do
    i=0
    begin
        apiSearch = XmlSimple.xml_in(HTTP.get(URI.encode("http://api.chartlyrics.com/apiv1.asmx/SearchLyric?artist=#{params[:artist]}&song=#{params[:track]}")).to_s)
    rescue IOError => ioe
        sleep 10    # give the api more time
        i++
        until i == 2
            retry
        end
    end
    begin
        lyricId = apiSearch["SearchLyricResult"][0]["LyricId"][0]
        checksum = apiSearch["SearchLyricResult"][0]["LyricChecksum"][0]
    rescue NoMethodError => nme
        return "no lyrics found"
    end
    i=0
    sleep 5     # the api enforces timeouts
    begin
        lyrics = XmlSimple.xml_in(HTTP.get("http://api.chartlyrics.com/apiv1.asmx/GetLyric?lyricId=#{lyricId}&lyricCheckSum=#{checksum}").to_s)
        return lyrics["Lyric"][0]
    rescue IOError => ioe
        sleep 15
        i++
        until i == 3
            retry
        end
    end
    return "no lyrics found"
    
end

get '/download' do
    semiProtected!
    tracks = Database.new.getTracks(params[:artist], params[:album]).delete_if{|key, value| key.is_a? Integer}
    filename = (params[:artist] + "_" + params[:album] + ".zip").gsub("/", "_")
    t = Tempfile.new(['temp_zip', '.zip'])
    Zip::ZipOutputStream.open(t.path) do |z|
        tracks.each do |track|
            name = track["title"]<commit_msg>Catch lyrics api connection error<commit_after>require 'sinatra'
require 'sinatra/browserid'
require 'json'
require 'filemagic'
require 'open3'
require 'http'
require 'uri'
require 'open-uri'
require 'zip'
require 'xmlsimple'

require './database.rb'

set :sessions, true
# session-hijacking-protection were triggered by ajax-requests
set :protection, except: :session_hijacking
# use server with streaming support (used when transcoding) 
set :server, :puma

helpers do
    include Rack::Utils
    alias_method :h, :escape

    def isAdmin?
        if authorized?
            if Database.new.getOption('admin') == authorized_email
                return true
            end
        end
        return false
    end

    def isGuest?
        if authorized?
            begin
                if Database.new.getOption('guest').split(";").include? authorized_email
                    return true
                end
            rescue => error
                puts error
            end
        end
        return false
    end
    
    def protected!
        unless isAdmin?
            throw(:halt, [401, "Not authorized\n"])
        end
    end

    def semiProtected!
        unless isAdmin? || isGuest?
            throw(:halt, [401, "Not authorized\n"])
        end
    end

    def firstUse?
        return Database.new.firstUse?
    end
    
    def emptyMediaDB?
        return Database.new.emptyMediaDB?
    end
end

get '/' do
    if ! (isAdmin? || isGuest?)
        erb :login
    else
        db = Database.new
        if db.getOption("mediaDir") == nil
            erb :setDB, :locals => {:mediaDir => nil}
        else
            erb :index, :locals => {:mediaDB => db.getMediaDB}
        end
    end
end

get '/setDB' do
    session[:origin] = back
    erb :setDB, :locals => {:mediaDir => Database.new.getOption("mediaDir")}
end

get '/settings' do
    protected!
    session[:origin] = back
    guests = Database.new.getOption("guest")
    guests = guests.split(";") if guests != nil
    erb :settings, :locals => {:guests => guests}
end

post '/addAdmin' do
    db = Database.new
    if db.firstUse? && ! authorized_email.empty?
        db.setOption('admin', authorized_email)
        redirect to('/')
    else
        'Error adding admin: param missing or admin already set'
    end
end

get '/logout' do
    logout!
    return "Logged out"
end

post '/setOption' do
    protected!
    if params[:name].is_a?(String)
        Database.new.setOption(params[:name], params[:value])
    else
        # we came from the settings, so input is more complex
        params[:name].each do |name, values|
            valueString = ""
            values.each do |key, value|
                valueString+=value + ";"
            end
            Database.new.setOption(name, valueString.chomp(";"))
        end
    end
    origin = session[:origin]
    # when setOption wasn't called first, like with the design, origin is old, so unset it
    session.delete(:origin)
    redirect origin if origin != nil
    redirect back
end

get '/guestInput' do
    protected!
    erb :guestInput, :locals => {:index => params[:index], :mail => ""}
end

post '/updateDB' do
    protected!
    session[:updateTotal] = Database.new.updateDB
    "Done"
end

get '/updateProgress' do
    Database.new.updateProgress.to_s
end

get '/updateTotal' do
    session[:updateTotal].to_s
end

get '/updateDone' do
    Database.new.updateDone.to_s
end

get '/mediaDB' do
    semiProtected!
    erb :mediaDB, :locals => {:mediaDB => Database.new.getMediaDB}
end

get '/tracks' do
    semiProtected!
    tracks = Database.new.getTracks(params[:artist], params[:album]).delete_if{|key, value| key.is_a? Integer}
    JSON(tracks)
end

get '/lyrics' do
    i=0
    begin
        apiSearch = XmlSimple.xml_in(HTTP.get(URI.encode("http://api.chartlyrics.com/apiv1.asmx/SearchLyric?artist=#{params[:artist]}&song=#{params[:track]}")).to_s)
    rescue  HTTP::ConnectionError, IOError => ioe
        sleep 10    # give the api more time
        i++
        until i == 2
            retry
        end
        return "no lyrics found, API down"
    end
    begin
        lyricId = apiSearch["SearchLyricResult"][0]["LyricId"][0]
        checksum = apiSearch["SearchLyricResult"][0]["LyricChecksum"][0]
    rescue NoMethodError => nme
        return "no lyrics found"
    end
    i=0
    sleep 5     # the api enforces timeouts
    begin
        lyrics = XmlSimple.xml_in(HTTP.get("http://api.chartlyrics.com/apiv1.asmx/GetLyric?lyricId=#{lyricId}&lyricCheckSum=#{checksum}").to_s)
        return lyrics["Lyric"][0]
    rescue  HTTP::ConnectionError, IOError => ioe
        sleep 15
        i++
        until i == 3
            retry
        end
        return "no lyrics found, API down"
    end
    return "no lyrics found"
    
end

get '/download' do
    semiProtected!
    tracks = Database.new.getTracks(params[:artist], params[:album]).delete_if{|key, value| key.is_a? Integer}
    filename = (params[:artist] + "_" + params[:album] + ".zip").gsub("/", "_")
    t = Tempfile.new(['temp_zip', '.zip'])
    Zip::ZipOutputStream.open(t.path) do |z|
        tracks.each do |track|
            name = track["title"]
{'fraction_non_alphanumeric': 0.12059514487079091, 'fraction_numerical': 0.004502740798747063, 'mean_word_length': 2.344353518821604, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2577426', 'n_tokens_mistral': 3240, 'n_tokens_neox': 3169, 'n_words': 845}