Skip to content

Commit

Permalink
Merge pull request #4 from schdck/dev
Browse files Browse the repository at this point in the history
Release: v1.0-beta3
  • Loading branch information
schdck authored Apr 22, 2020
2 parents 5e1db2b + abd6dbd commit cb63c7e
Show file tree
Hide file tree
Showing 28 changed files with 693 additions and 316 deletions.
Binary file added Other/Screenshots/Banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Other/Screenshots/MainScreen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Other/Screenshots/MobileScreen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,49 @@

<hr>

![](Other/Screenshots/Banner.png)

## Why?
Well, basically because I was tired of getting up to pause my stream and answer a text.
Well, basically because I'm lazy and didn't like to get up and pause my video every time I wanted to answer a text.

## How?
The idea behind RemoteFlix is very simple. Most (if not all) media players provide shortcuts that enable you to perform a task (e.g. pausing the video). What RemoteFlix do is allow you to execute these (pre-defined) shortcuts from your phone's browser.
The idea behind RemoteFlix is very simple. Most media players provide shortcuts that enable you to perform a task (e.g. pausing the video). What RemoteFlix do is allow you to execute these (pre-defined) shortcuts from your phone's browser.

To do that, it exposes a very simple web-server which is responsible for listing the avaliable players and actions (which we call commands) and handling requests.

To do that, it exposes a very simple web-server which is responsible for listing the avaliable players and actions (which we call commands). Also, the web-server is responsible for handling requests. When a request is received, it'll look for the player's process on the machine and send the keys corresponding to the action's shortcut to it.
When a request is received, the application willll look for the player's process on the machine and send the keys corresponding to the command's shortcut to it.

## Installing and running
There is a setup (use this if you want to have RemoteFlix auto-starting with Windows) and portable binaries avaliable for each version on the [releases](https://github.com/schdck/RemoteFlix/releases) page.

After you run the executable, it's pretty straightforward. Just go ahead and scan the QRCode or navigate to the indicated URL.
After you run the executable, it's pretty straightforward:

1. Double-click the RemoteFlix's icon next to Window's clock to open the application.
2. Click the `Setup environment` button at the top left or take a look at the **Setting up the environment** section bellow.
3. RemoteFlix will try to use the correct server address, but in case it guessed wrong you can just select the right one from the combo box.
4. Scan the QR Code using your phone.

## Setting up the environment

The following steps are exactly what `Setup environment` button does. But if you are an advanced user and for any reason wants to run them yourself, here they are. Remember to run them from an elevated command prompt.

1. Allow RemoteFlix's URL to [every user on your computer](https://serverfault.com/a/678675/382770):

``` bash
netsh http add urlacl url="http://+:50505/" sddl=D:(A;;GX;;;S-1-1-0)
```

2. Allow RemoteFlix through Windows Firewall:

[<img src="https://github.com/schdck/RemoteFlix/blob/master/Other/Screenshots/MainScreen.png?raw=true">](https://raw.githubusercontent.com/schdck/RemoteFlix/master/Other/Screenshots/MainScreen.png)
``` bash
netsh advfirewall firewall add rule name="RemoteFlix_Port" localport=50505 direction=in action=allow protocol=tcp
```

## Supported media players
* Netflix
* VLC Media Player
* Popcorn Time (Butter)
* YouTube

Want support for a player that isn't listed? Please [let us know](https://github.com/schdck/RemoteFlix/issues/new?assignees=&labels=&template=player-request.md&title=%5BPLAYER+REQUEST%5D). Pull requests are also welcome.

Expand Down
6 changes: 3 additions & 3 deletions RemoteFlix.Base/Classes/Logger.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using RemoteFlix.Base.Enums;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;

namespace RemoteFlix.Base.Classes
Expand All @@ -14,7 +13,8 @@ public class Logger

readonly ObservableCollection<Log> _Logs;

public INotifyCollectionChanged Logs { get; }
public ReadOnlyObservableCollection<Log> Logs { get; }
public string PathToLogFile => Path.Combine(Path.GetTempPath(), "remoteflix.log.txt");

private Logger()
{
Expand All @@ -30,7 +30,7 @@ public void Log(LogLevel logLevel, string message)
{
_Logs.Add(log);

using(var writer = new StreamWriter(Path.Combine(Path.GetTempPath(), "remoteflix.log.txt"), true))
using(var writer = new StreamWriter(PathToLogFile, true))
{
writer.WriteLine(log);
}
Expand Down
21 changes: 21 additions & 0 deletions RemoteFlix.Base/Classes/NetworkInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Net;

namespace RemoteFlix.Base.Classes
{
public class NetworkInterface
{
public string Name { get; }
public IPAddress InterNetworkAddress { get; }

public NetworkInterface(string name, IPAddress interNetworkAddress)
{
Name = name;
InterNetworkAddress = interNetworkAddress;
}

public override string ToString()
{
return $"{InterNetworkAddress.ToString()} ({Name})";
}
}
}
45 changes: 38 additions & 7 deletions RemoteFlix.Base/Helpers/NetworkHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Linq;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
Expand All @@ -7,18 +7,49 @@ namespace RemoteFlix.Base.Helpers
{
public static class NetworkHelper
{
public static IPAddress GetLocalIPAddress()
public static string GetDefaultIp()
{
try
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
// This IP is arbitrary. We are sending an UDP
// package only to get the oubound IP address
socket.Connect("192.168.0.0", 1337);

return (socket.LocalEndPoint as IPEndPoint)?.Address.ToString();
}
}
catch
{
return null;
}
}

public static IEnumerable<Classes.NetworkInterface> GetAvaliableNetworkInterfaces()
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
return null;
}

IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
var output = new List<Classes.NetworkInterface>();

foreach (var networkInterface in NetworkInterface.GetAllNetworkInterfaces())
{
if (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 || networkInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
{
foreach (UnicastIPAddressInformation ipInformation in networkInterface.GetIPProperties().UnicastAddresses)
{
if (ipInformation.Address.AddressFamily == AddressFamily.InterNetwork)
{
output.Add(new Classes.NetworkInterface(networkInterface.Name, ipInformation.Address));
}
}
}
}

return host
.AddressList
.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
return output;
}
}
}
}
37 changes: 37 additions & 0 deletions RemoteFlix.Base/Players/PlayerYoutube.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using RemoteFlix.Base.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RemoteFlix.Base.Players
{
public class PlayerYoutube : BasePlayer
{
public override string Id => "youtube";
public override string Name => "YouTube";

public override IntPtr? GetHandle()
{
return null;
}

public override IEnumerable<PlayerCommand> Commands => new PlayerCommand[]
{
new PlayerCommand("play_pause", "Play / Pause", "k"),
new PlayerCommand("volume_up", "Volume UP", "{UP}"),
new PlayerCommand("volume_down", "Volume Down", "{DOWN}"),
new PlayerCommand("next_video", "Next video", "+n"),
new PlayerCommand("previous_video", "Previous video", "+p"),
new PlayerCommand("mute_unmute", "Mute / Unmute", "m"),
new PlayerCommand("enter_full_screen", "Full screen", "f"),
new PlayerCommand("exit_full_screen", "Exit full screen", "{ESC}"),
new PlayerCommand("go_back", "Go back", "{LEFT}"),
new PlayerCommand("go_forward", "Go forward", "{RIGHT}"),
new PlayerCommand("faster", "Go faster", ">"),
new PlayerCommand("slower", "Go slower", "<"),
new PlayerCommand("closed_captions", "Enable / Disable CC", "c"),
};
}
}
2 changes: 2 additions & 0 deletions RemoteFlix.Base/RemoteFlix.Base.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<ItemGroup>
<Compile Include="Classes\Log.cs" />
<Compile Include="Classes\Logger.cs" />
<Compile Include="Classes\NetworkInterface.cs" />
<Compile Include="Classes\PlayerCommand.cs" />
<Compile Include="Enums\LogLevel.cs" />
<Compile Include="Helpers\KeyboardSimulationHelper.cs" />
Expand All @@ -53,6 +54,7 @@
<Compile Include="Players\PlayerNetflix.cs" />
<Compile Include="Players\PlayerNetflixChrome.cs" />
<Compile Include="Players\PlayerVlc.cs" />
<Compile Include="Players\PlayerYoutube.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RemoteFlixPlayers.cs" />
<Compile Include="RemoteFlixServer.cs" />
Expand Down
3 changes: 2 additions & 1 deletion RemoteFlix.Base/RemoteFlixPlayers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public static class RemoteFlixPlayers
new PlayerNetflix(),
new PlayerNetflixChrome(),
new PlayerVlc(),
new PlayerButter()
new PlayerButter(),
new PlayerYoutube()
};
}
}
66 changes: 53 additions & 13 deletions RemoteFlix.Base/RemoteFlixServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,33 @@

namespace RemoteFlix.Base
{
public class RemoteFlixServer
public sealed class RemoteFlixServer
{
private const int HTTP_OK = 200;
private const int HTTP_BAD_REQUEST = 400;
private const int HTTP_NOT_FOUND = 404;
private const int HTTP_INTERNAL_SERVER_ERROR = 500;

public const ushort PORT = 50505;

private HttpListener Listener;
private CancellationTokenSource CancellationTokenSource;

private int RequestId;
public bool IsRunning { get; private set; }

public event EventHandler ServerStarted;
public event EventHandler ServerStopped;

// We don't need this to be lazy, since we start the
// the server when the program starts
public static RemoteFlixServer Instance { get; } = new RemoteFlixServer();

// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static RemoteFlixServer()
{
}

public RemoteFlixServer()
private RemoteFlixServer()
{
if (!HttpListener.IsSupported)
{
Expand All @@ -38,10 +50,15 @@ public RemoteFlixServer()

Listener = new HttpListener();
Listener.Prefixes.Add($"http://+:{PORT}/");

IsRunning = false;
}

public void Start()
{
if (IsRunning)
return;

CancellationTokenSource = new CancellationTokenSource();

Logger.Instance.Log(LogLevel.Message, "Starting the HttpListener server...");
Expand All @@ -51,20 +68,44 @@ public void Start()
Logger.Instance.Log(LogLevel.Message, "Server started.");

Task.Factory.StartNew(ReceiveRequests, CancellationTokenSource.Token);

IsRunning = true;
ServerStarted?.Invoke(this, EventArgs.Empty);
}

public void Stop()
{
if (!IsRunning)
return;

CancellationTokenSource.Cancel();
Listener.Stop();

IsRunning = false;
ServerStopped?.Invoke(this, EventArgs.Empty);
}

private void ReceiveRequests()
{
Logger.Instance.Log(LogLevel.Message, "Listening for requests...");

while (!CancellationTokenSource.IsCancellationRequested)
try
{
ThreadPool.QueueUserWorkItem(ProcessRequest, Listener.GetContext());
while (!CancellationTokenSource.IsCancellationRequested)
{
ThreadPool.QueueUserWorkItem(ProcessRequest, Listener.GetContext());
}
}
catch(HttpListenerException e)
{
if(!CancellationTokenSource.IsCancellationRequested)
{
Logger.Instance.Log(LogLevel.Error, $"{e.GetType()} when receiving requests. {e.Message}");
}
}
catch(Exception e)
{
Logger.Instance.Log(LogLevel.Error, $"{e.GetType()} when receiving requests. {e.Message}");
}

Logger.Instance.Log(LogLevel.Message, "Stopped listening for requests.");
Expand Down Expand Up @@ -117,8 +158,7 @@ private void HandleGetForHome(HttpListenerContext context)

foreach (var player in RemoteFlixPlayers.AvaliablePlayers)
{
// $"http://{NetworkHelper.GetLocalIPAddress()}:{RemoteFlixServer.PORT}"
builder.AppendLine($"<button onclick=\"location.href='http://{NetworkHelper.GetLocalIPAddress()}:{PORT}/{player.Id}'\" type='button'>{player.Name}</button>");
builder.AppendLine($"<button onclick=\"location.href='/{player.Id}'\" type='button'>{player.Name}</button>");
}

SendResponse(context, builder.ToString());
Expand Down Expand Up @@ -213,12 +253,12 @@ private bool ExecuteCommand(BasePlayer player, PlayerCommand command)
if (playerHandle == null)
{
playerHandle = KeyboardSimulationHelper.GetForegroundWindow();
}

// If this also fails, there isn't much we can do
if(playerHandle == null || playerHandle.Value == IntPtr.Zero)
{
return false;
// If this also fails, there isn't much we can do
if (playerHandle.Value == IntPtr.Zero)
{
return false;
}
}

KeyboardSimulationHelper.SendKeys(playerHandle.Value, command.ActionShortcut);
Expand Down
5 changes: 3 additions & 2 deletions RemoteFlix.Base/Resources/remoteflix.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<link href="https://fonts.googleapis.com/css2?family=Baloo+Bhaina+2:wght@500&display=swap" rel="stylesheet">

<title>RemoteFlix</title>

<style>
body {
background-color: dimgray;
padding: 10px;
font-family: 'Bahnschrift Light';
font-family: 'Baloo Bhaina 2', cursive;
}

#logo {
Expand Down Expand Up @@ -66,7 +67,7 @@
<image src="remoteflix.png" />
</div>

<p>Conectado ao computador<br><b>{COMPUTER_NAME}</b></p>
<p>Connected to computer <br><b>{COMPUTER_NAME}</b></p>

<div id="content">
<div id="sub_content">
Expand Down
Loading

0 comments on commit cb63c7e

Please sign in to comment.