mirror of
https://github.com/fly-apps/live_beats.git
synced 2024-11-25 09:20:59 +00:00
WIP
This commit is contained in:
parent
bfb0c07330
commit
214ec50f0e
8 changed files with 57 additions and 22 deletions
|
@ -4,6 +4,7 @@ import {LiveSocket} from "phoenix_live_view"
|
||||||
import topbar from "../vendor/topbar"
|
import topbar from "../vendor/topbar"
|
||||||
|
|
||||||
let nowSeconds = () => Math.round(Date.now() / 1000)
|
let nowSeconds = () => Math.round(Date.now() / 1000)
|
||||||
|
let rand = (min, max) => Math.floor(Math.random() * (max - min) + min)
|
||||||
|
|
||||||
let execJS = (selector, attr) => {
|
let execJS = (selector, attr) => {
|
||||||
document.querySelectorAll(selector).forEach(el => liveSocket.execJS(el, el.getAttribute(attr)))
|
document.querySelectorAll(selector).forEach(el => liveSocket.execJS(el, el.getAttribute(attr)))
|
||||||
|
@ -96,9 +97,13 @@ Hooks.AudioPlayer = {
|
||||||
mounted(){
|
mounted(){
|
||||||
this.playbackBeganAt = null
|
this.playbackBeganAt = null
|
||||||
this.player = this.el.querySelector("audio")
|
this.player = this.el.querySelector("audio")
|
||||||
|
this.player.addEventListener("ended", () => console.log("player: ended"))
|
||||||
|
this.player.addEventListener("stalled", () => console.log("player: stalled"))
|
||||||
|
this.player.addEventListener("suspend", () => console.log("player: suspend"))
|
||||||
|
this.player.addEventListener("waiting", () => console.log("player: waiting"))
|
||||||
this.playerDuration = 0
|
this.playerDuration = 0
|
||||||
this.currentTime = this.el.querySelector("#player-time")
|
this.currentTime = this.el.querySelector("#player-time")
|
||||||
this.duration = this.el.querySelector("#player-duration")
|
this.durationText = this.el.querySelector("#player-duration")
|
||||||
this.progress = this.el.querySelector("#player-progress")
|
this.progress = this.el.querySelector("#player-progress")
|
||||||
let enableAudio = () => {
|
let enableAudio = () => {
|
||||||
if(this.player.src){
|
if(this.player.src){
|
||||||
|
@ -123,7 +128,7 @@ Hooks.AudioPlayer = {
|
||||||
if(currentSrc === url && this.player.paused){
|
if(currentSrc === url && this.player.paused){
|
||||||
this.play({sync: true})
|
this.play({sync: true})
|
||||||
} else if(currentSrc !== url) {
|
} else if(currentSrc !== url) {
|
||||||
this.player.src = `${url}?token=${token}`
|
this.player.src = `${url}?token=${token}&proxy`
|
||||||
this.play({sync: true})
|
this.play({sync: true})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -132,9 +137,15 @@ Hooks.AudioPlayer = {
|
||||||
},
|
},
|
||||||
|
|
||||||
play(opts = {}){
|
play(opts = {}){
|
||||||
|
console.log("play")
|
||||||
let {sync} = opts
|
let {sync} = opts
|
||||||
|
clearInterval(this.progressTimer)
|
||||||
|
clearTimeout(this.nextTimer)
|
||||||
this.player.play().then(() => {
|
this.player.play().then(() => {
|
||||||
if(sync){ this.player.currentTime = nowSeconds() - this.playbackBeganAt }
|
if(sync){
|
||||||
|
console.log("sync", nowSeconds() - this.playbackBeganAt)
|
||||||
|
this.player.currentTime = nowSeconds() - this.playbackBeganAt
|
||||||
|
}
|
||||||
this.progressTimer = setInterval(() => this.updateProgress(), 100)
|
this.progressTimer = setInterval(() => this.updateProgress(), 100)
|
||||||
}, error => {
|
}, error => {
|
||||||
if(error.name === "NotAllowedError"){
|
if(error.name === "NotAllowedError"){
|
||||||
|
@ -145,28 +156,32 @@ Hooks.AudioPlayer = {
|
||||||
|
|
||||||
pause(){
|
pause(){
|
||||||
clearInterval(this.progressTimer)
|
clearInterval(this.progressTimer)
|
||||||
|
clearTimeout(this.nextTimer)
|
||||||
this.player.pause()
|
this.player.pause()
|
||||||
},
|
},
|
||||||
|
|
||||||
stop(){
|
stop(){
|
||||||
clearInterval(this.progressTimer)
|
clearInterval(this.progressTimer)
|
||||||
|
clearTimeout(this.nextTimer)
|
||||||
this.player.pause()
|
this.player.pause()
|
||||||
this.player.currentTime = 0
|
this.player.currentTime = 0
|
||||||
this.updateProgress()
|
this.updateProgress()
|
||||||
this.duration.innerText = ""
|
this.durationText.innerText = ""
|
||||||
this.currentTime.innerText = ""
|
this.currentTime.innerText = ""
|
||||||
},
|
},
|
||||||
|
|
||||||
updateProgress(){
|
updateProgress(){
|
||||||
if(this.playerDuration === 0){ return false }
|
if(this.playerDuration === 0){ return false }
|
||||||
if(Math.ceil(this.player.currentTime) >= Math.floor(this.playerDuration)){
|
if(Math.ceil(this.player.currentTime) >= Math.floor(this.playerDuration)){
|
||||||
this.playerDuration = 0
|
|
||||||
this.pushEvent("next_song_auto")
|
|
||||||
clearInterval(this.progressTimer)
|
clearInterval(this.progressTimer)
|
||||||
|
this.player.pause()
|
||||||
|
this.playerDuration = 0
|
||||||
|
console.log("next_song_auto")
|
||||||
|
this.nextTimer = setTimeout(() => this.pushEvent("next_song_auto"), rand(1000, 3000))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.progress.style.width = `${(this.player.currentTime / (this.playerDuration) * 100)}%`
|
this.progress.style.width = `${(this.player.currentTime / (this.playerDuration) * 100)}%`
|
||||||
this.duration.innerText = this.formatTime(this.playerDuration)
|
this.durationText.innerText = this.formatTime(this.playerDuration)
|
||||||
this.currentTime.innerText = this.formatTime(this.player.currentTime)
|
this.currentTime.innerText = this.formatTime(this.player.currentTime)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ import Config
|
||||||
config :live_beats, :files,
|
config :live_beats, :files,
|
||||||
uploads_dir: Path.expand("../priv/uploads", __DIR__),
|
uploads_dir: Path.expand("../priv/uploads", __DIR__),
|
||||||
host: [scheme: "http", host: "localhost", port: 4000],
|
host: [scheme: "http", host: "localhost", port: 4000],
|
||||||
server_ip: "127.0.0.1"
|
server_ip: "127.0.0.1",
|
||||||
|
hostname: "localhost",
|
||||||
|
transport_opts: []
|
||||||
|
|
||||||
config :live_beats, :github,
|
config :live_beats, :github,
|
||||||
client_id: System.fetch_env!("LIVE_BEATS_GITHUB_CLIENT_ID"),
|
client_id: System.fetch_env!("LIVE_BEATS_GITHUB_CLIENT_ID"),
|
||||||
|
|
|
@ -64,7 +64,6 @@ if config_env() == :prod do
|
||||||
client_secret: System.fetch_env!("LIVE_BEATS_GITHUB_CLIENT_SECRET")
|
client_secret: System.fetch_env!("LIVE_BEATS_GITHUB_CLIENT_SECRET")
|
||||||
|
|
||||||
config :libcluster,
|
config :libcluster,
|
||||||
debug: true,
|
|
||||||
topologies: [
|
topologies: [
|
||||||
fly6pn: [
|
fly6pn: [
|
||||||
strategy: Cluster.Strategy.DNSPoll,
|
strategy: Cluster.Strategy.DNSPoll,
|
||||||
|
|
|
@ -138,7 +138,7 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
end
|
end
|
||||||
|
|
||||||
def put_stats(%Ecto.Changeset{} = changeset, %MP3Stat{} = stat) do
|
def put_stats(%Ecto.Changeset{} = changeset, %MP3Stat{} = stat) do
|
||||||
chset = Song.put_duration(changeset, stat.duration)
|
chset = Song.put_stats(changeset, stat)
|
||||||
|
|
||||||
if error = chset.errors[:duration] do
|
if error = chset.errors[:duration] do
|
||||||
{:error, %{duration: error}}
|
{:error, %{duration: error}}
|
||||||
|
@ -423,10 +423,11 @@ defmodule LiveBeats.MediaLibrary do
|
||||||
Song.changeset(song, attrs)
|
Song.changeset(song, attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@keep_changes [:duration, :mp3_filesize, :mp3_filepath]
|
||||||
def change_song(%Ecto.Changeset{} = prev_changeset, attrs) do
|
def change_song(%Ecto.Changeset{} = prev_changeset, attrs) do
|
||||||
%Song{}
|
%Song{}
|
||||||
|> change_song(attrs)
|
|> change_song(attrs)
|
||||||
|> Ecto.Changeset.change(Map.take(prev_changeset.changes, [:duration]))
|
|> Ecto.Changeset.change(Map.take(prev_changeset.changes, @keep_changes))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp order_by_playlist(%Ecto.Query{} = query, direction) when direction in [:asc, :desc] do
|
defp order_by_playlist(%Ecto.Query{} = query, direction) when direction in [:asc, :desc] do
|
||||||
|
|
|
@ -19,6 +19,7 @@ defmodule LiveBeats.MediaLibrary.Song do
|
||||||
field :mp3_url, :string
|
field :mp3_url, :string
|
||||||
field :mp3_filepath, :string
|
field :mp3_filepath, :string
|
||||||
field :mp3_filename, :string
|
field :mp3_filename, :string
|
||||||
|
field :mp3_filesize, :integer, default: 0
|
||||||
field :server_ip, EctoNetwork.INET
|
field :server_ip, EctoNetwork.INET
|
||||||
belongs_to :user, Accounts.User
|
belongs_to :user, Accounts.User
|
||||||
belongs_to :genre, LiveBeats.MediaLibrary.Genre
|
belongs_to :genre, LiveBeats.MediaLibrary.Genre
|
||||||
|
@ -45,7 +46,13 @@ defmodule LiveBeats.MediaLibrary.Song do
|
||||||
put_assoc(changeset, :user, user)
|
put_assoc(changeset, :user, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def put_duration(%Ecto.Changeset{} = changeset, duration) when is_integer(duration) do
|
def put_stats(%Ecto.Changeset{} = changeset, %LiveBeats.MP3Stat{} = stat) do
|
||||||
|
changeset
|
||||||
|
|> put_duration(stat.duration)
|
||||||
|
|> Ecto.Changeset.put_change(:mp3_filesize, stat.size)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_duration(%Ecto.Changeset{} = changeset, duration) when is_integer(duration) do
|
||||||
changeset
|
changeset
|
||||||
|> Ecto.Changeset.change(%{duration: duration})
|
|> Ecto.Changeset.change(%{duration: duration})
|
||||||
|> Ecto.Changeset.validate_number(:duration,
|
|> Ecto.Changeset.validate_number(:duration,
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule LiveBeats.MP3Stat do
|
||||||
use Bitwise
|
use Bitwise
|
||||||
alias LiveBeats.MP3Stat
|
alias LiveBeats.MP3Stat
|
||||||
|
|
||||||
defstruct duration: 0, path: nil, title: nil, artist: nil, tags: nil
|
defstruct duration: 0, size: 0, path: nil, title: nil, artist: nil, tags: nil
|
||||||
|
|
||||||
@declared_frame_ids ~w(AENC APIC ASPI COMM COMR ENCR EQU2 ETCO GEOB GRID LINK MCDI MLLT OWNE PRIV PCNT POPM POSS RBUF RVA2 RVRB SEEK SIGN SYLT SYTC TALB TBPM TCOM TCON TCOP TDEN TDLY TDOR TDRC TDRL TDTG TENC TEXT TFLT TIPL TIT1 TIT2 TIT3 TKEY TLAN TLEN TMCL TMED TMOO TOAL TOFN TOLY TOPE TOWN TPE1 TPE2 TPE3 TPE4 TPOS TPRO TPUB TRCK TRSN TRSO TSOA TSOP TSOT TSRC TSSE TSST TXXX UFID USER USLT WCOM WCOP WOAF WOAR WOAS WORS WPAY WPUB WXXX)
|
@declared_frame_ids ~w(AENC APIC ASPI COMM COMR ENCR EQU2 ETCO GEOB GRID LINK MCDI MLLT OWNE PRIV PCNT POPM POSS RBUF RVA2 RVRB SEEK SIGN SYLT SYTC TALB TBPM TCOM TCON TCOP TDEN TDLY TDOR TDRC TDRL TDTG TENC TEXT TFLT TIPL TIT1 TIT2 TIT3 TKEY TLAN TLEN TMCL TMED TMOO TOAL TOFN TOLY TOPE TOWN TPE1 TPE2 TPE3 TPE4 TPOS TPRO TPUB TRCK TRSN TRSO TSOA TSOP TSOT TSRC TSSE TSST TXXX UFID USER USLT WCOM WCOP WOAF WOAR WOAS WORS WPAY WPUB WXXX)
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ defmodule LiveBeats.MP3Stat do
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse(path) do
|
def parse(path) do
|
||||||
|
stat = File.stat!(path)
|
||||||
{tag_info, rest} = parse_tag(File.read!(path))
|
{tag_info, rest} = parse_tag(File.read!(path))
|
||||||
duration = parse_frame(rest, 0, 0, 0)
|
duration = parse_frame(rest, 0, 0, 0)
|
||||||
|
|
||||||
|
@ -44,7 +45,14 @@ defmodule LiveBeats.MP3Stat do
|
||||||
seconds = round(duration)
|
seconds = round(duration)
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
%MP3Stat{duration: seconds, path: path, tags: tag_info, title: title, artist: artist}}
|
%MP3Stat{
|
||||||
|
duration: seconds,
|
||||||
|
size: stat.size,
|
||||||
|
path: path,
|
||||||
|
tags: tag_info,
|
||||||
|
title: title,
|
||||||
|
artist: artist
|
||||||
|
}}
|
||||||
|
|
||||||
_other ->
|
_other ->
|
||||||
{:error, :bad_file}
|
{:error, :bad_file}
|
||||||
|
|
|
@ -8,18 +8,19 @@ defmodule LiveBeatsWeb.FileController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def show(conn, %{"id" => filename_uuid, "token" => token}) do
|
def show(conn, %{"id" => filename_uuid, "token" => token} = params) do
|
||||||
path = MediaLibrary.local_filepath(filename_uuid)
|
path = MediaLibrary.local_filepath(filename_uuid)
|
||||||
mime_type = MIME.from_path(path)
|
mime_type = MIME.from_path(path)
|
||||||
|
|
||||||
case Phoenix.Token.decrypt(conn, "file", token, max_age: :timer.minutes(1)) do
|
case Phoenix.Token.decrypt(conn, "file", token, max_age: :timer.minutes(1)) do
|
||||||
{:ok, %{uuid: ^filename_uuid, ip: ip}} ->
|
{:ok, %{vsn: 1, uuid: ^filename_uuid, ip: ip, size: size}} ->
|
||||||
if local_file?(filename_uuid, ip) do
|
# if local_file?(filename_uuid, ip) do
|
||||||
|
if !params["proxy"] do
|
||||||
Logger.info("serving file from #{server_ip()}")
|
Logger.info("serving file from #{server_ip()}")
|
||||||
do_send_file(conn, path)
|
do_send_file(conn, path)
|
||||||
else
|
else
|
||||||
Logger.info("proxying file to #{ip} from #{server_ip()}")
|
Logger.info("proxying file to #{ip} from #{server_ip()}")
|
||||||
proxy_file(conn, ip, mime_type)
|
proxy_file(conn, ip, mime_type, size)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, _} ->
|
{:ok, _} ->
|
||||||
|
@ -38,10 +39,11 @@ defmodule LiveBeatsWeb.FileController do
|
||||||
|> send_file(200, path)
|
|> send_file(200, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp proxy_file(conn, ip, mime_type) do
|
defp proxy_file(conn, ip, mime_type, content_length) do
|
||||||
uri = conn |> request_url() |> URI.parse()
|
uri = conn |> request_url() |> URI.parse()
|
||||||
port = LiveBeatsWeb.Endpoint.config(:http)[:port]
|
port = LiveBeatsWeb.Endpoint.config(:http)[:port]
|
||||||
path = uri.path <> "?" <> uri.query <> "&from=#{server_ip()}"
|
# path = uri.path <> "?" <> uri.query <> "&from=#{server_ip()}"
|
||||||
|
path = uri.path <> "?" <> String.replace(uri.query, "&proxy", "") <> "&from=#{server_ip()}"
|
||||||
{:ok, ipv6} = :inet.parse_address(String.to_charlist(ip))
|
{:ok, ipv6} = :inet.parse_address(String.to_charlist(ip))
|
||||||
{:ok, req} = Mint.HTTP.connect(:http, ipv6, port, file_server_opts())
|
{:ok, req} = Mint.HTTP.connect(:http, ipv6, port, file_server_opts())
|
||||||
{:ok, req, request_ref} = Mint.HTTP.request(req, "GET", path, [], "")
|
{:ok, req, request_ref} = Mint.HTTP.request(req, "GET", path, [], "")
|
||||||
|
@ -49,7 +51,7 @@ defmodule LiveBeatsWeb.FileController do
|
||||||
conn
|
conn
|
||||||
|> put_resp_header("content-type", mime_type)
|
|> put_resp_header("content-type", mime_type)
|
||||||
|> put_resp_header("accept-ranges", "bytes")
|
|> put_resp_header("accept-ranges", "bytes")
|
||||||
|> put_resp_header("transfer-encoding", "chunked")
|
|> put_resp_header("content-length", IO.inspect(to_string(content_length)))
|
||||||
|> send_chunked(200)
|
|> send_chunked(200)
|
||||||
|> stream(req, request_ref)
|
|> stream(req, request_ref)
|
||||||
end
|
end
|
||||||
|
|
|
@ -301,7 +301,8 @@ defmodule LiveBeatsWeb.PlayerLive do
|
||||||
Phoenix.Token.encrypt(socket.endpoint, "file", %{
|
Phoenix.Token.encrypt(socket.endpoint, "file", %{
|
||||||
vsn: 1,
|
vsn: 1,
|
||||||
ip: to_string(song.server_ip),
|
ip: to_string(song.server_ip),
|
||||||
uuid: song.mp3_filename
|
size: song.mp3_filesize,
|
||||||
|
uuid: song.mp3_filename,
|
||||||
})
|
})
|
||||||
|
|
||||||
push_event(socket, "play", %{
|
push_event(socket, "play", %{
|
||||||
|
|
Loading…
Reference in a new issue