Git Product home page Git Product logo

jbops's People

Contributors

arcanemagus avatar blacktwin avatar dirtycajunrice avatar evotk avatar johnnygrey86 avatar jonnywong16 avatar kurtzpt avatar ology avatar origamiofficial avatar palshee avatar philosowaffle avatar pjft avatar samwiseg0 avatar toomuchio avatar yosifkit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jbops's Issues

[More Settings Please?] Sorry for being a noob, instead of 2, how can I change it to 3 or more?

Hi, If I wanted to change from the current 2 concurrent stream to like 3, where would I do that?

Maybe it'd be nice to add that as one of the settings?

EDIT THESE SETTINGS

PLEX_TOKEN = 'xxxxx'
PLEX_URL = 'http://localhost:32400'
CON_CURRENT_STREAM = 3

Also, I've set this up the way the script says, but I cannot find
PlexPy > Settings > Notification Agents > Scripts > Gear icon:
Playback User Concurrent Streams: kill_more_than.py

As for the log, it shows this :

from plexapi.server import PlexServer
ImportError: No module named plexapi.server

delete watched tv

I KNOW this is going to be something stupid, im just not sure what it wants. Im trying to setup the delete_watched_tv script. I loaded the scripts into a folder, pointed the scripts folder in plexypy and configured the settings i found in script.

I got the rating key from the url of the show i was testing in plexpy. this is the show url page.
IE: http://plexpywebsite.com/info?rating_key=51825

PLEXPY_APIKEY = ' # Your PlexPy API key
PLEXPY_URL = 'http://192.168.2.197:8181/' # Your PlexPy URL
SHOW_LST = [51825] # Show rating keys.
USER_LST = ['dejected1 (Me)'] # Name of users

The log shows the following error:

ERROR
| PlexPy Notifiers :: Script error:     
Traceback (most recent call last):         
File "C:\JonnyWong16-plexpy-3742f33\Scripts\utility\delete_watched_TV.py", line 7, in             
import requests     
ImportError: No module named requests
-- | --

I didnt see any arguments listed in the script and this is the only script i was testing.

notify_added_custom.py AttributeError: 'NoneType' object has no attribute 'added_at'

After updating to Tautulli release version, This error is being generated by your notify_added_custom.py:

$ /usr/bin/python /Applications/Tautulli/Scripts/notify_added_custom.py -t art -d 7
Sending email(s) to [email protected]
PlexPy API 'get_metadata' request failed: 'metadata'.Traceback (most recent call last):
  File "/Applications/Tautulli/Scripts/notify_added_custom.py", line 341, in <module>
    build_parts = [build_html(rating_key, height, width, opts.type) for rating_key in sorted(rating_keys_lst)]
  File "/Applications/Tautulli/Scripts/notify_added_custom.py", line 204, in build_html
    added = time.ctime(float(meta.added_at))
AttributeError: 'NoneType' object has no attribute 'added_at'

Thanks

limit by days or hours

reddit request.

I want to be able to allow someone maybe 10 hrs of viewing time. Or a user limited to 15 days or something. Mainly for the plex curious that I don’t want on my server alllll the time.

[REQUEST] Notification

I know we can get notified of when a pause situation happens.
I was wondering if the script actually kills a transacode, can we get a notification in regards to that action?

[ISSUE] `ip_whitelist.py` is not killing streams from non-whitelisted IP addresses.

Hi.
I can't get the ip_whitelist.py script to work.

It is setup just like the header comments say to. I have included some of my config and logs to help dx the issue.

Let me know if you need more to troubleshoot, etc.

Thanks.


Config:

## EDIT THESE SETTINGS ##
PLEX_TOKEN = 'XXXXXXX'   #fresh token filled in
PLEX_URL = 'http://plex.domain.com'   #also tried http://localhost:32400

TAUTULLI_APIKEY = 'xxxxx'  # real api filled in
TAUTULLI_URL = 'http://localhost:8181/'  # I use the official Tautulli docker image, and 8181 is the default port.

IP_WHITELIST = ['10.10.0.12','97.87.xx.xx']  # List IP addresses. XX are actual numbers. 
IGNORE_LST = ('ignorename1','ignorename2')  # List usernames that should be ignored.

REASON = 'IP Address: {} was not found in whitelist.'

NOTIFIER_ID = 5  # correct id filled in
# Find Notification agent ID here:
# Tautulli Settings -> NOTIFICATION AGENTS -> :bell: Agent (NotifierID - {Description)

SUBJECT_TEXT = "IP Whitelist Violation"
BODY_TEXT = "Killed {user}'s stream of {title}. IP: {ip} not in whitelist"
##/EDIT THESE SETTINGS ##

Log:

2018-03-26 23:03:20 | INFO | Tautulli Notifiers :: Script notification sent.
2018-03-26 23:03:20 | DEBUG | Tautulli Notifiers :: Executing script in a new thread.
2018-03-26 23:03:20 | DEBUG | Tautulli Notifiers :: Full script is: ['python', u'/config/scripts/JBOPS/killstream/ip_whitelist.py', u'19', u'173.***.***.***']
2018-03-26 23:03:20 | DEBUG | Tautulli Notifiers :: Trying to run notify script, action: play, arguments: [u'19', u'173.***.***.***']
2018-03-26 23:03:20 | INFO | Tautulli NotificationHandler :: Preparing notifications for notifier_id 5.
2018-03-26 23:03:19 | DEBUG | Tautulli ActivityHandler :: Session 19 started by user XXXXXXX (NAME) with ratingKey 78380 (MEDIAFILE).

Update:

There may have been a successful hit , actually.

2018-03-27 03:21:45 | DEBUG | Tautulli Notifiers :: Script returned:     Killing USER'S stream of MEDIA_FILE. IP: 173.***.***.*** not in whitelistSuccessfully sent Tautulli notification.

I just cant tell if it's being consistent.

Did also get this, randomly.

2018-03-27 03:21:45 | ERROR | Tautulli Notifiers :: Script error:     Traceback (most recent call last):         File "/config/scripts/JBOPS/killstream/ip_whitelist.py", line 43, in <module>             sessionKey = sys.argv[1]     IndexError: list index out of range

plex_api_share error

root@plex:~# python plexlib.py -s unshare -u user -l Movies
Traceback (most recent call last):
File "plexlib.py", line 73, in
unshare(opts.user, opts.libraries)
File "plexlib.py", line 50, in unshare
plex.myPlexAccount().updateFriend(user=user, server=plex, removeSections=True, sections=libraries)
TypeError: updateFriend() got an unexpected keyword argument 'removeSections'

It's listing users and libraries when ran with --help, but throwing this error when sharing/unsharing

Kill stream scripts SSL error

From @wreave:

Issues:

  • Script uses {user} when it should use {username} as {user} can be changed in plexpy so it is not static

  • Kill script needs to be SSL if the server is set to ssl
    requests.get('http{}://{}:{}/status/sessions/terminate'.format(PLEX_SSL, PLEX_HOST, PLEX_PORT),

  • Plexpy strips the leading '::ffff' off local addresses when plex sends them so need to strip it in the script or it wont match
    if s['User']['title'] == USER and s['Player']['address'].lstrip("::ffff:") == ADDRESS:

  • SSL errors occur when using the URL to termimate the stream on a server that requires SSL, the fix is to use the plex.direct address for the host or to use the plexpy api to kill a stream (see pinned message) NOTE: this would restrict the script to v2

  • The SSL error will occur for all scripts trying to terminate a stream if the server requires SSL

Error: "TypeError: 'NoneType' object has no attribute '__getitem__"

OS: Windows10E 1703 X64
Plexpy: 1.4
Python Version: | 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500 32 bit (Intel)]

ISSUE:
Scripts used: kill_trans_pause.py

So, the script runs and does appear to kill the stream, but I am getting the below python error:


     PlexPy Notifiers :: Script error: 
    Traceback (most recent call last): 
        File "C:\JBOPS\killstream\kill_trans_pause.py", line 67, in 
            for s in response['MediaContainer']['Video']: 
    TypeError: 'NoneType' object has no attribute '__getitem__'

so my full log after it killed looked like this:

 2017-07-26 09:18:14    DEBUG    PlexPy ActivityHandler :: Removing sessionKey 110 ratingKey 170214 from session queue
2017-07-26 09:18:14    DEBUG    PlexPy ActivityProcessor :: Fetching metadata for item ratingKey 170214
2017-07-26 09:18:14    DEBUG    PlexPy ActivityHandler :: Session 110 has stopped.
2017-07-26 09:17:55    ERROR    PlexPy Notifiers :: Script error: 
    Traceback (most recent call last): 
        File "C:\JBOPS\killstream\kill_trans_pause.py", line 67, in 
            for s in response['MediaContainer']['Video']: 
    TypeError: 'NoneType' object has no attribute '__getitem__'
2017-07-26 09:17:54    DEBUG    PlexPy Notifiers :: Executing script in a new thread.

notify_added_custom.py TypeError: 'NoneType' object is not iterable

Hi,

Since Tautulli 2 is now out of beta I decided to try out your notify_added_custom.py script with it, and I'm getting the following error:

$ /usr/bin/python /Applications/Tautulli/Scripts/notify_added_custom.py -t art -d 7
PlexPy API 'get_libraries_table' request failed: 'data'.Traceback (most recent call last):
  File "/Applications/Tautulli/Scripts/notify_added_custom.py", line 317, in <module>
    glt = [lib for lib in get_get_libraries_table()]
TypeError: 'NoneType' object is not iterable

Thanks for your help!

wait_kill_pause_notify_main ERROR: "plexapi.exceptions.BadRequest"

OS: Windows 10E 1709 X64
PMS v: 1.9.5.4339
PlexPY v: 1.4.25
Python v: | 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:25:58) [MSC v.1500 64 bit (AMD64)]

Had the previous create_wait_kill_trans.py working great...trying out this new script!

Getting this error when any session is pasued: "plexapi.exceptions.BadRequest: (401) unauthorized; http://localhost:32400/?X-Plex-Token=xxxxx"

I CAN access that same link by adding the token from the .py file to the end of the URL .

Does the PlexPY need the BASE URL added if set in Plexpy??

Here's what I got for wait_kill_pause_notify_main.py:


## EDIT THESE SETTINGS ##
PLEX_TOKEN = 'PLEX TOKEN'
PLEX_URL = 'http://localhost:32400'
PLEXPY_APIKEY = 'API KEY'  # Your PlexPy API key
PLEXPY_URL = 'http://localhost:32500/plexpy/'  # Your PlexPy URL  

TIMEOUT = '100'
INTERVAL = '20'

KILL_MESSAGE = 'Your stream ended because it was paused longer than 5 minutes. To continue where you left off, select the title you were enjoying and choose RESUME. '

USER_IGNORE = ('') # ('Username','User2')

SUBJECT_TEXT = "Killed Paused Transcoded Stream."
BODY_TEXT = "VREEs PLEX Killed {user}'s paused transcoded stream of {title}."

AGENT_ID = 14  # Notification agent ID for PlexPy
# Find Notification agent ID here:
# https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify
# AGENT = '' to disable notification

sub_script = 'wait_kill_trans_notify_sub.py'
##/EDIT THESE SETTINGS ##

LOGS:

Oct 21 23:26:12 PlexPY plexpy.log: 2017-10-21 23:26:11 - DEBUG   :: Thread-2 : PlexPy ActivityHandler :: Session 151 has been paused.
Oct 21 23:26:13 PlexPY plexpy.log: 2017-10-21 23:26:12 - DEBUG   :: Thread-29 : PlexPy Notifiers :: Trying to run notify script, action: pause, arguments: [u'151', u'', u'00', u'00', u'2', u'23:26', u'68']
Oct 21 23:26:13 PlexPY plexpy.log: 2017-10-21 23:26:12 - DEBUG   :: Thread-29 : PlexPy Notifiers :: Full script is: ['python', 'C:\\JBOPS\\killstream\\wait_kill_pause_notify_main.py', '151', '', '00', '00', '2', '23:26', '68']
Oct 21 23:26:13 PlexPY plexpy.log: 2017-10-21 23:26:12 - DEBUG   :: Thread-29 : PlexPy Notifiers :: Executing script in a new thread.
Oct 21 23:26:15 PlexPY plexpy.log: 2017-10-21 23:26:13 - ERROR   :: Thread-30 : PlexPy Notifiers :: Script error: 
Oct 21 23:26:15 PlexPY plexpy.log:   Traceback (most recent call last):
Oct 21 23:26:15 PlexPY plexpy.log:     File "C:\JBOPS\killstream\wait_kill_trans_notify_sub.py", line 17, in <module>
Oct 21 23:26:15 PlexPY plexpy.log:       from wait_kill_trans_notify_main import kill_stream, check_session
Oct 21 23:26:15 PlexPY plexpy.log:     File "C:\JBOPS\killstream\wait_kill_trans_notify_main.py", line 54, in <module>
Oct 21 23:26:15 PlexPY plexpy.log:       plex = PlexServer(PLEX_URL, PLEX_TOKEN)
Oct 21 23:26:15 PlexPY plexpy.log:     File "C:\Python27\lib\site-packages\plexapi\server.py", line 103, in __init__
Oct 21 23:26:15 PlexPY plexpy.log:       data = self.query(self.key, timeout=timeout)
Oct 21 23:26:15 PlexPY plexpy.log:     File "C:\Python27\lib\site-packages\plexapi\server.py", line 346, in query
Oct 21 23:26:15 PlexPY plexpy.log:       raise BadRequest('(%s) %s; %s %s' % (response.status_code, codename, response.url, errtext))
Oct 21 23:26:15 PlexPY plexpy.log:   plexapi.exceptions.BadRequest: (401) unauthorized; http://localhost:32400/?X-Plex-Token=xxxxx <html><head><script>window.location = window.location.href.match(/(^.+\/)[^\/]*$/)[1] + 'web/index.html';</script><title>Unauthorized</title></head><body><h1>401 Unauthorized</h1></body></html>
Oct 21 23:27:29 PlexPY plexpy.log: 2017-10-21 23:27:28 - DEBUG   :: Thread-2 : PlexPy ActivityHandler :: Session 152 has started with ratingKey 177857.
Oct 21 23:27:30 PlexPY plexpy.log: 2017-10-21 23:27:29 - INFO    :: Thread-33 : PlexPy Notifiers :: Slack notification sent.
Oct 21 23:27:30 PlexPY plexpy.log: 2017-10-21 23:27:29 - INFO    :: Thread-34 : PlexPy Notifiers :: Slack notification sent.
Oct 21 23:27:31 PlexPY plexpy.log: 2017-10-21 23:27:30 - DEBUG   :: Thread-33 : PlexPy Notifiers :: Trying to run notify script, action: play, arguments: [u'152', u'', u'00', u'00', u'3', u'23:27', u'70']
Oct 21 23:27:31 PlexPY plexpy.log: 2017-10-21 23:27:30 - DEBUG   :: Thread-33 : PlexPy Notifiers :: Full script is: ['python', 'C:\\JBOPS\\notify\\notify_stream_threshold.py', '152', '', '00', '00', '3', '23:27', '70']
Oct 21 23:27:31 PlexPY plexpy.log: 2017-10-21 23:27:30 - DEBUG   :: Thread-33 : PlexPy Notifiers :: Executing script in a new thread.

Aired Today Playlist Not Created

Playlists for current day no longer being created, with the following error message:

Traceback (most recent call last):
  File "C:\Python27\Scripts\new_create_current_day_playlist.py", line 70, in <module>
    play_lst = find_air_dates(get_all_content(LIBRARY_NAMES))
  File "C:\Python27\Scripts\new_create_current_day_playlist.py", line 38, in get_all_content
    for child in plex.library.section(library).all():
  File "C:\python36\lib\site-packages\plexapi\library.py", line 383, in all
    return self.fetchItems(key, **kwargs)
  File "C:\python36\lib\site-packages\plexapi\base.py", line 147, in fetchItems
    return self.findItems(data, cls, ekey, **kwargs)
  File "C:\python36\lib\site-packages\plexapi\base.py", line 163, in findItems
    item = self._buildItemOrNone(elem, cls, initpath)
  File "C:\python36\lib\site-packages\plexapi\base.py", line 86, in _buildItemOrNone
    return self._buildItem(elem, cls, initpath)
  File "C:\python36\lib\site-packages\plexapi\base.py", line 78, in _buildItem
    return ecls(self._server, elem, initpath)
  File "C:\python36\lib\site-packages\plexapi\base.py", line 47, in __init__
    self._loadData(data)
  File "C:\python36\lib\site-packages\plexapi\video.py", line 108, in _loadData
    Video._loadData(self, data)
  File "C:\python36\lib\site-packages\plexapi\video.py", line 32, in _loadData
    self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
  File "C:\python36\lib\site-packages\plexapi\utils.py", line 176, in toDatetime
    value = datetime.fromtimestamp(int(value))
OSError: [Errno 22] Invalid argument
PS C:\Python27>

plex_api_share - all users?

Hello,

Listing each user seperated by a space throws invalid argument errors.

root@media:/opt/plexpy/scripts# python plex_share.py -s share_all -u User1 User2
usage: plex_share.py [-h] -s  [-u ] [-l  [...]]
plex_share.py: error: unrecognized arguments: User2

Really what I would like is an "all users" option - is that possible? I tried changing opts.user to user_lst but that throws all kinds of errors I'm too ignorant to know how to deal with. Could an all users option be easily added?

I typically I want to Share or Unshare All Libraries with All Users (i.e. when my data cap for the month is reached).

ImportError: No module named plexapi.server

PlexPy Notifiers :: Script error:
Traceback (most recent call last):
File "/home/hd1/hamflix/apps/plexpy/custom_scripts/kill_plex_streams.py", line 18, in
from plexapi.server import PlexServer
ImportError: No module named plexapi.server

How to add delay to any script?

Hey buddy,

not sure if this is possible, but wondering if it's possible to add a notification delay to any script?

The one I'm concerned about is this one:

https://gist.github.com/JonnyWong16/1b1b02536e25002daf678946f513b950

This one sends a notification when a certain number of streams is currently active. The problem is that it notifies like every 30 seconds. Possible to edit the code to put in a "do not notify more than x minutes" type of deal?

Thanks brotha.

plex_netflix_check.py fail when Movie title is only numbers

Movie name: 1922
Script trying to do lower function but fail because it is an integer.

Search for '1922' found 32 matches.
Traceback (most recent call last):
File "plex_netflix_check.py", line 227, in
main()
File "plex_netflix_check.py", line 224, in main
plex_library_search(opts.library[0], opts.site, opts.episodes, opts.search_limit)
File "plex_netflix_check.py", line 174, in plex_library_search
if instantwatch_search(title.title, title.type, site, search_limit) > 0:
File "plex_netflix_check.py", line 112, in instantwatch_search
if data['a']['$'].lower().startswith(name.lower()):
AttributeError: 'int' object has no attribute 'lower'

alternate forms of notify other than email: pushbullet

So i was trying to figure out if we could adapt one of the notify scripts to notify a different service. For me its pushbullet. It works through plexpy directly but plexpy has no way of setting different notifications depending on what the media is. (movies and tv shows). the script: notify_fav_tv_all_movie.py from JBOPS would work perfect since it already makes a distinction between movies and tv.

refresh_next_episode.py: "ValueError: invalid literal for int() with base 10"

It's me again buddy!

Trying to use the Refresh_next_Episode script:

Here's the log from PlexPY:


2017-08-09 23:02:08 | DEBUG | PlexPy APIv2 :: API called with kwargs: {'cmd': u'get_activity', 'apikey': u'********39'}
-- | -- | --
2017-08-09 23:01:58 | ERROR | PlexPy Notifiers :: Script error:     Traceback (most recent call last):         File "C:\JBOPS\utility\refresh_next_episode.py", line 26, in             next_ep_num = int(sys.argv[2])     ValueError: invalid literal for int() with base 10: 'Rick and Morty'
2017-08-09 23:01:57 | DEBUG | PlexPy Notifiers :: Executing script in a new thread.
2017-08-09 23:01:57 | DEBUG | PlexPy Notifiers :: Full script is: ['python', 'C:\\JBOPS\\utility\\refresh_next_episode.py', '98', 'Rick and Morty', '03', '03']
2017-08-09 23:01:57 | DEBUG | PlexPy Notifiers :: Trying to run notify script, action: watched, arguments: [u'98', u'Rick and Morty', u'03', u'03']
2017-08-09 23:01:38 | DEBUG | PlexPy APIv2 :: Cleaned kwargs: {}

Screenshot of the plexpy config on left, refresh_next_episode.py settings on right.

image

Error on Weekly Stats Reporting

https://github.com/blacktwin/JBOPS/blob/master/reporting/weekly_stats_reporting.py

When ran from cmd or from plexpy, results in this error.

    PlexPy API 'get_library_media_info' request failed: 'total_file_size'.Traceback (most recent call last): 
        File "/config/scripts/weekly_stats_reporting.py", line 294, in 
            main() 
        File "/config/scripts/weekly_stats_reporting.py", line 279, in main 
            lib_stats, user_stats_lst = get_server_stats(dates_range_lst) 
        File "/config/scripts/weekly_stats_reporting.py", line 200, in get_server_stats 
            total_size += lib_size 
    TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'

media_type = 'season'

Any idea how soon can we expect 'cmd':'get_recently_added' to return recently_added.media_type='season'?

I realize that this is more of an API question.

aired_today_playlist

Seems that this isnt creating a playlist for me for some odd reason did this one need fixed by chance everything seems correct to me but for some reason when ran it doesn't make the playlist ?

create_wait_kill_trans error: " list indices must be integers, not str"

Hey brotha, got another one for you. Not sure why this stopped working all of the sudden. But, I get this error when the session is paused, and after the timeout expires, nothing happens, no errors:\


2017-08-16 02:05:17 | INFO | PlexPy Notifiers :: Script notification sent.
-- | -- | --
2017-08-16 02:05:17 | DEBUG | PlexPy Notifiers :: Script returned:     list indices must be integers, not str
2017-08-16 02:05:16 | DEBUG | PlexPy Notifiers :: Executing script in a new thread.
2017-08-16 02:05:16 | DEBUG | PlexPy Notifiers :: Full script is: ['python', 'C:\\JBOPS\\killstream\\create_wait_kill_trans.py', '65', '', '00', '00']
2017-08-16 02:05:16 | DEBUG | PlexPy Notifiers :: Trying to run notify script, action: pause, arguments: [u'65', u'', u'00', u'00']
2017-08-16 02:05:15 | DEBUG | PlexPy ActivityHandler :: Session 65 has been paused.

Current create_wait_kill_trans.py file:


'''


import os
import platform
import subprocess
import sys
from uuid import getnode
import unicodedata

import requests


PLEX_HOST = '192.168.1.20'
PLEX_PORT = 32400
PLEX_SSL = ''  # s or ''
PLEX_TOKEN = 'DTQyDyypzXkxgnGbvSj7'

TIMEOUT = 300
INTERVAL = 5

REASON = 'The stream has been terminated because it was paused for an excessive amount of time.'
ignore_lst = ('test')


def fetch(path, t='GET'):
    url = 'http%s://%s:%s/' % (PLEX_SSL, PLEX_HOST, PLEX_PORT)

    headers = {'X-Plex-Token': PLEX_TOKEN,
               'Accept': 'application/json',
               'X-Plex-Provides': 'controller',
               'X-Plex-Platform': platform.uname()[0],
               'X-Plex-Platform-Version': platform.uname()[2],
               'X-Plex-Product': 'Plexpy script',
               'X-Plex-Version': '0.9.5',
               'X-Plex-Device': platform.platform(),
               'X-Plex-Client-Identifier': str(hex(getnode()))
               }

    try:
        if t == 'GET':
            r = requests.get(url + path, headers=headers, verify=False)
        elif t == 'POST':
            r = requests.post(url + path, headers=headers, verify=False)
        elif t == 'DELETE':
            r = requests.delete(url + path, headers=headers, verify=False)

        if r and len(r.content):  # incase it dont return anything
            return r.json()
        else:
            return r.content

    except Exception as e:
        print e


def kill_stream(sessionId, message, xtime, ntime, user, title, sessionKey):
    headers = {'X-Plex-Token': PLEX_TOKEN}
    params = {'sessionId': sessionId,
              'reason': message}

    response = fetch('status/sessions')

    if response['MediaContainer']['Video']:
        for a in response['MediaContainer']['Video']:
            if a['sessionKey'] == sessionKey:
                if xtime == ntime and a['Player']['state'] == 'paused' and a['Media']['Part']['decision'] == 'transcode':
                    sys.stdout.write("Killing {user}'s paused stream of {title}".format(user=user, title=title))
                    requests.get('http://{}:{}/status/sessions/terminate'.format(PLEX_HOST, PLEX_PORT),
                             headers=headers, params=params)
                    return ntime
                elif a['Player']['state'] in ('playing', 'buffering'):
                    sys.stdout.write("{user}'s stream of {title} is now {state}".
                                     format(user=user, title=title, state=a['Player']['state']))
                    return None
                else:
                    return xtime
    else:
        return None



def find_sessionID(response):

    sessions = []
    for s in response['MediaContainer']['Video']:
        if s['sessionKey'] == sys.argv[1] and s['Player']['state'] == 'paused' \
                and s['Media']['Part']['decision'] == 'transcode':
            sess_id = s['Session']['id']
            user = s['User']['title']
            sess_key = sys.argv[1]
            title = (s['grandparentTitle'] + ' - ' if s['type'] == 'episode' else '') + s['title']
            title = unicodedata.normalize('NFKD', title).encode('ascii','ignore')
            sessions.append((sess_id, user, title, sess_key))
        else:
            pass

    for session in sessions:
        if session[1] not in ignore_lst:
            return session
        else:
            print("{}'s stream of {} is ignored.".format(session[1], session[2]))
            return None


if __name__ == '__main__':

    startupinfo = None
    if os.name == 'nt':
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

    response = fetch('status/sessions')
    import json
	
    fileDir = fileDir = os.path.dirname(os.path.realpath(__file__))

    try:
        if find_sessionID(response):
            stream_info = find_sessionID(response)
            file_name = "{}.py".format(stream_info[0])
            full_path = os.path.join(fileDir, file_name)
            file = "from time import sleep\n" \
                   "import sys, os\n" \
                   "from {script} import kill_stream \n" \
                   "message = '{REASON}'\n" \
                   "sessionID =  os.path.basename(sys.argv[0])[:-3]\n" \
                   "x = 0\n" \
                   "n = {ntime}\n" \
                   "try:\n" \
                   "    while x < n and x is not None:\n" \
                   "        sleep({xtime})\n" \
                   "        x += kill_stream(sessionID, message, {xtime}, n, '{user}', '{title}', '{sess_key}')\n" \
                   "    kill_stream(sessionID, message, {ntime}, n, '{user}', '{title}', '{sess_key}')\n" \
                   "    os.remove(sys.argv[0])\n" \
                   "except TypeError as e:\n" \
                   "    os.remove(sys.argv[0])".format(script=os.path.basename(__file__)[:-3],
                                                       ntime=TIMEOUT, xtime=INTERVAL, REASON=REASON,
                                                       user=stream_info[1], title=stream_info[2],
                                                       sess_key=stream_info[3])

            with open(full_path, "w+") as output:
                output.write(file)

            subprocess.Popen([sys.executable, full_path], startupinfo=startupinfo)
            exit(0)

    except TypeError as e:
        print(e)
        pass

Drive Check not working with Tautulli v2?

hey brotha,

Using Tautaulli v2. Since updating, the drive check script doesnt' seem to work.

It says it sends the notification, but the notification is never received.

image

wait_kill_pause_notify_main.py IndexError: list index out of range

Hi,
I'm using Tautulli version 2.0.28.
Script notification settings are set to the following:
Configuration: Script File -> .\wait_kill_pause_notify_main.py
Triggers: Notify on Playback Pause
Conditions: None
Arguments: Playback Pause -> {session_key}

wait_kill_pause_notify_sub.py has been placed in the same directory as wait_kill_pause_notify_main.py.

I keep getting the following two log entries (removed html tags and replaced sessionid with placeholder)

2018-04-03 16:05:30 | ERROR | Tautulli Notifiers :: Script error:     Traceback (most recent call last):         File "D:\Tautulli\scripts\wait_kill_pause_notify_main.py", line 59, in <module>             sessionKey = sys.argv[1]     IndexError: list index out of range
-- | -- | --
2018-04-03 16:05:28 | DEBUG | Tautulli Notifiers :: Script returned:     Executing sub script.     Sub script initiating kill.     Successfully sent Tautulli notification.Error: (401) unauthorized; http://localhost:32400/status/sessions/terminate?sessionId=[SESSIONID_PLACEHOLDER]&reason=This+stream+has+ended+due+to+being+paused. Unauthorized401 Unauthorized

Any assistance would be appreciate, thanks.

Notify_delay.py not working

PlexPy version: 1.4.22
PMS Version: 1.8.4.4249
notify_delay.py chmoded +x & 777
Other concurrent streams notifications disabled.
User has 2 playbacks, same IP, same web browser (yeah, myself with other plex user using chrome incognito window)

PlexPy log:

Passed 10 minutes, no more plexpy logs, no notification.


2017-09-12 09:56:06 INFO    PlexPy Notifiers :: Script notification sent.
2017-09-12 09:56:06 DEBUG   PlexPy Notifiers :: Script returned:
    Checking concurrent stream count.
    Exiting, user no longer has concurrent streams.
2017-09-12 09:56:06 DEBUG   PlexPy APIv2 :: Cleaned kwargs: {}
2017-09-12 09:56:06 DEBUG   PlexPy APIv2 :: API called with kwargs: {'apikey': u'********74', 'cmd': u'get_activity'}
2017-09-12 09:56:06 DEBUG   PlexPy Notifiers :: Executing script in a new thread.
2017-09-12 09:56:06 DEBUG   PlexPy Notifiers :: Full script is: ['python', u'/var/scripts/notify_delay.py']
2017-09-12 09:56:06 DEBUG   PlexPy Notifiers :: Trying to run notify script, action: concurrent, arguments: None
2017-09-12 09:56:02 DEBUG   PlexPy Monitor :: Session 36 has started with ratingKey 96612.
2017-09-12 09:56:02 DEBUG   PlexPy Monitor :: Session 35 has started with ratingKey 96782.
2017-09-12 09:55:14 INFO    Cleared the plexpy.log file.

kill_trans_pause_notify indexerror: list index out of range.

Setup kill_trans_pause_notify.py to trigger scripts when paused. Script does run when stream is paused but the log shows this error:

ERROR | PlexPy Notifiers :: Script error:     Traceback (most recent call last):         File "C:\JonnyWong16-plexpy-3742f33\Scripts\Weekly\kill_trans_pause_notify.py", line 61, in             video_decision = session.transcodeSessions[0].videoDecision     IndexError: list index out of range
-- | --

When a user on the ignore list pauses a stream, the script triggers without issue. Im using the version i found off your site:

"""
Kill Plex paused video transcoding streams and receive notification.
PlexPy > Settings > Notification Agents > Scripts > Bell icon:
        [X] Notify on playback pause
PlexPy > Settings > Notification Agents > Scripts > Gear icon:
        Playback Pause: kill_trans_pause.py
"""

import sys
import requests
from plexapi.server import PlexServer


## EDIT THESE SETTINGS ##
PLEX_URL = 'http://localhost:32400'
PLEX_TOKEN = 'xxxxx'
PLEXPY_APIKEY = 'xxxxx'  # Your PlexPy API key
PLEXPY_URL = 'http://localhost:8181/'  # Your PlexPy URL

KILL_MESSAGE = 'This stream has ended due to being paused and transcoding.'

USER_IGNORE = ('') # ('Username','User2')

SUBJECT_TEXT = "Killed Paused Transcoded Stream."
BODY_TEXT = "Killed {user}'s paused transcoded stream of {title}."

AGENT_ID = 14  # Notification agent ID for PlexPy
# Find Notification agent ID here:
# https://github.com/JonnyWong16/plexpy/blob/master/API.md#notify

##/EDIT THESE SETTINGS ##

sess = requests.Session()
sess.verify = False
plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess)

def send_notification(subject_text, body_text):
    # Send the notification through PlexPy
    payload = {'apikey': PLEXPY_APIKEY,
               'cmd': 'notify',
               'agent_id': AGENT_ID,
               'subject': subject_text,
               'body': body_text}

    try:
        r = requests.post(PLEXPY_URL.rstrip('/') + '/api/v2', params=payload)
        response = r.json()

        if response['response']['result'] == 'success':
            sys.stdout.write("Successfully sent PlexPy notification.")
        else:
            raise Exception(response['response']['message'])
    except Exception as e:
        sys.stderr.write("PlexPy API 'notify' request failed: {0}.".format(e))
        return None


for session in plex.sessions():
    username = session.usernames[0]
    state = session.players[0].state
    video_decision = session.transcodeSessions[0].videoDecision
    title = (session.grandparentTitle + ' - ' if session.type == 'episode' else '') + session.title

    if video_decision == 'transcode' and state == 'paused' and username not in USER_IGNORE:
        sys.stdout.write("Killing {user}'s stream of {title}.".format(user=username, title=title))
        session.stop(reason=KILL_MESSAGE)
        send_notification(SUBJECT_TEXT, BODY_TEXT.format(user=username, title=title))

Error with create_wait_kill_all.py

Running latest version of Plex, PlexPy and the create_wait_kill_all.py script. Seeing the following error:

PlexPy Notifiers :: Script error: Traceback (most recent call last): File "/app/plexpy/scripts/create_wait_kill_all.py", line 152, in if find_sessionID(response): File "/app/plexpy/scripts/create_wait_kill_all.py", line 122, in find_sessionID for s in response['MediaContainer']['Video']: KeyError: 'Video'

error

Hi,

i'm getting

PlexPy Notifiers :: Script error: 
    Traceback (most recent call last): 
        File "C:\Users\Tarik\Desktop\Plex Script\kill_all_more_than.py", line 20, in 
            from plexapi.server import PlexServer 
    ImportError: No module named plexapi.server

I have installed plexapi but i think its only working with my python version 3.5.

request failed: 'metadata'.

Hey I can't get it to work, maybe it's broken or probably I haven't configured it correctly.

From the logs:

Tautulli Notifiers :: Script error:
PlexPy API 'get_get_metadata' request failed: 'metadata'.

What Script Arguments should be passed?
What Trigger should be used?

Tautulli v2.0.25
Python Version: | 2.7.12
Windows 10

Aired_Today_Playlist questions:

Hey buddy.

Got a couple questions about aired_today_playlist, looks like the directions aren't totally complete? No biggie.

baseurl = 'http://localhost:32400'  **<- why is this different from all the other scripts?  This is just the server's address:PORT , correct?**
token = 'oihoi888hj'
plex = PlexServer(baseurl, token)   **<- is this the server name?**  

library_name = ['TV Shows'] # You library names **<- missplelled "YOU" - "your"?**

child_lst = []  **<-  are these values somthing I'm supposed to config?**
aired_lst = []

PlexPY error: "kill_stream     ImportError: No module named create_wait_kill_trans"

OS: Windows10E 1703 X64
Plexpy: 1.4
Python Version: | 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500 32 bit (Intel)]

ISSUE:

create_wait_kill_trans.py Script does not kill stream when client transcode is paused.

Possibly related to #5

Config:

## EDIT THESE SETTINGS ##

PLEX_HOST = 'localhost'
PLEX_PORT = 32400
PLEX_SSL = ''  # s or ''
PLEX_TOKEN = 'DTQyxxxxxxxxkxgnGbvSj7'

TIMEOUT = 120
INTERVAL = 10

PlexPY Logs:


2017-07-26 22:02:00 | DEBUG | PlexPy Monitor :: Library item 171122 has been added to Plex.
-- | -- | --
2017-07-26 22:01:47 | ERROR | PlexPy Notifiers :: Script error:     Traceback (most recent call last):         File "ce4v0py551wome68wdujlktq.py", line 3, in             from create_wait_kill_trans import kill_stream     ImportError: No module named create_wait_kill_trans
2017-07-26 22:01:46 | DEBUG | PlexPy Notifiers :: Executing script in a new thread.
2017-07-26 22:01:46 | DEBUG | PlexPy Notifiers :: Full script is: ['python', 'C:\\JBOPS\\killstream\\create_wait_kill_trans.py', '8']
2017-07-26 22:01:46 | DEBUG | PlexPy Notifiers :: Trying to run notify script, action: pause, arguments: [u'8']
2017-07-26 22:01:45 | DEBUG | PlexPy ActivityHandler :: Session 8 has been paused.

Script Error - 'Video'

I notice I got this error tonight. Any ideas?

PlexPy Notifiers :: Script error:     
    Traceback (most recent call last):         
       File "C:\Scripts\md20o21ftcjdyrtpjpacsby9.py", line 11, in             
            x += kill_stream(sessionID, message, 10, n, 'tking08', 'The Lost City of Z', '163')         
       File "C:\Scripts\create_wait_kill_all.py", line 84, in kill_stream             
            if response['MediaContainer']['Video']:     
                KeyError: 'Video'

notify_added_custom fails on foreign actor names

The json is decoded there are issues where it will error out when the script tries to process it. I.E.

      'data' => {
                  'actors' => [
                                'Steve Martin',
                                'Michael Caine',
                                'Glenne Headly',
                                'Anton Rodgers',
                                'Barbara Harris',
                                'Ian McDiarmid',
                                'Dana Ivey',
                                'Meagen Fay',
                                'Georges Gerrard Baffos',
                                'Cheryl Pay',
                                'Frances Conroy',
                                'Nicole Calfan',
                                'Lolly Susi',
                                'Valerie Beaufils',
                                'Xavier Maly',
                                'Louis Zorich',
                                **"Andr\x{e9} Penvern",**
                                'Hepburn Graham',
                                'Nathalie Auffret',
                                **"A\x{ef}na Walle"**
                              ],
                  'sort_title' => '',
                  'labels' => [],
                  'rating_key' => '15183',
                  'parent_media_index' => '',
                  'content_rating' => 'PG',
                  'media_type' => 'movie',
                  'duration' => '6621183',
                  'parent_thumb' => '',
                  'updated_at' => '1521426404',
                  'banner' => '',
                  'full_title' => 'Dirty Rotten Scoundrels',

This script will not finish with any actor names that have ` or other non ascii.

plexapi_haiku_movies.py chokes on titles with numbers

Examples:

ring one begins in
runner 2009 hotel stop affairs
for 2 spirited

country infernal
on casablanca the world
away 101 king bourne

bad park good morty
call saul the sherlock bad good
sherlock trek 2017 humans

Maybe add an option to blacklist numbers and titles that include II or III ?

Q/Feat request: Define how long the Kill message appears (create_wait_kill_trans)

Loving create_wait_kill_trans man, great job.

So, I noticed when the Kill message "REASON" is displayed on the client device, the message actually only appears for a split second. Like, not even long enough to ready it.

Is it designed that way? Is it configurable?

Or, is it maybe it's my rig?

thanks buddy!

p.s. sorry for all the issues man. Just trying to debug!

kill more than script giving error

have the kill more than script installed in tautulli and when testing to see if its working i get this in the logs.

Not sure what is going on or why its not working. Any help would be greatly appreciated

Tautulli Notifiers :: Script error: 
    Traceback (most recent call last): 
        File "/opt/plexpy/user-scripts/kill_more_than2.py", line 29, in <module> 
            SESSION_KEY = int(sys.argv[3]) 
    IndexError: list index out of range

IndexError: list index out of range in kill_trans_library.py

I only have TARGET_LIBRARIES = ['25'] # Library IDs set.
In Tautulli I have set this argument passed on playback start: {section_id} and nothing else.
plexapi and requests is installed.

When I try transcoding something in this library, the script executes and Tautulli gives this output:

2018-01-22 17:18:07 - DEBUG   :: Thread-1 : Tautulli Notifiers :: Trying to run notify script, action: play, arguments: [u'25']
2018-01-22 17:18:07 - DEBUG   :: Thread-1 : Tautulli Notifiers :: Full script is: ['python', u'/opt/JBOPS/killstream/kill_trans_library.py', u'25']
2018-01-22 17:18:07 - DEBUG   :: Thread-1 : Tautulli Notifiers :: Executing script in a new thread.
2018-01-22 17:18:08 - ERROR   :: Thread-30 : Tautulli Notifiers :: Script error:
  /usr/lib/python2.7/dist-packages/urllib3/connectionpool.py:854: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
    InsecureRequestWarning)
  /usr/lib/python2.7/dist-packages/urllib3/connectionpool.py:854: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
    InsecureRequestWarning)
  /usr/lib/python2.7/dist-packages/urllib3/connectionpool.py:854: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
    InsecureRequestWarning)
  Traceback (most recent call last):
    File "/opt/JBOPS/killstream/kill_trans_library.py", line 52, in &lt;module&gt;
      trans_dec = session.transcodeSessions[0].videoDecision
  IndexError: list index out of range

I tried setting both http and https.
When I do http, this is the output is:

2018-01-22 17:31:26 - DEBUG   :: Thread-2 : Tautulli Notifiers :: Trying to run notify script, action: play, arguments: [u'25']
2018-01-22 17:31:26 - DEBUG   :: Thread-2 : Tautulli Notifiers :: Full script is: ['python', u'/opt/JBOPS/killstream/kill_trans_library.py', u'25']
2018-01-22 17:31:26 - DEBUG   :: Thread-2 : Tautulli Notifiers :: Executing script in a new thread.
2018-01-22 17:31:26 - ERROR   :: Thread-35 : Tautulli Notifiers :: Script error:
  Traceback (most recent call last):
    File "/opt/JBOPS/killstream/kill_trans_library.py", line 40, in &lt;module&gt;
      plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess)
    File "/usr/local/lib/python2.7/dist-packages/plexapi/server.py", line 101, in __init__
      data = self.query(self.key, timeout=timeout)
    File "/usr/local/lib/python2.7/dist-packages/plexapi/server.py", line 340, in query
      response = method(url, headers=headers, timeout=timeout, **kwargs)
    File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 515, in get
      return self.request('GET', url, **kwargs)
    File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 502, in request
      resp = self.send(prep, **send_kwargs)
    File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 612, in send
      r = adapter.send(request, **kwargs)
    File "/usr/lib/python2.7/dist-packages/requests/adapters.py", line 490, in send
      raise ConnectionError(err, request=request)
  requests.exceptions.ConnectionError: ('Connection aborted.', error(104, 'Connection reset by peer'))

I made sure the Plex server allowed unencrypted connections when trying with http.

What am I doing wrong?
Thanks for a bunch of great scripts - looks like exactly what I need, if I could just get it working! :-)

Issue with notify_added_custom.py

Hi,

I hope you can help me with your script. I'm having the following error message when I attempt to use notify_added_custom.py script form your resources. I followed the instructions for edit these settings, but I feel like there is something else I need to edit. Thank you

2017-10-30 09:46:55 | ERROR | PlexPy Notifiers :: Script error:     
usage: All Notifications.py [-h] -t {art,poster} [-s [SIZE [SIZE ...]]] -d                                                  
DAYS [-u USERS [USERS ...]]                                                   
[-i IGNORE [IGNORE ...]]     
All Notifications.py: error: argument -t/--type is required

2017-10-30 09:46:55 | DEBUG | PlexPy Notifiers :: Executing script in a new thread.
2017-10-30 09:46:55 | DEBUG | PlexPy Notifiers :: Full script is: ['python', 'C:\Users\Johnny\Desktop\Plexpy Scripts\All Notifications.py']
2017-10-30 09:46:55 | DEBUG | PlexPy Notifiers :: Trying to run notify script, action: test, arguments: None
2017-10-30 09:46:55 | DEBUG | Sending test Scripts notification.
2017-10-30 09:46:55 | INFO | PlexPy Config :: Writing configuration to file

kill_more_than.py "...Not killing USER's second stream. Same IP:

I'm fairly certain that I have this setup correctly. Seems to run just fine, however, when I check the logs all I get back is this, Tautulli Notifiers :: Script returned: Not killing cw101's second stream. Same IP every time. Even if I am DEFINITELY having the same user stream on 2 different IP addreses.

I'm also using the latest version of Tautulli.

Trying to test script, unable to validate

So, I have create_wait_kill_all.py installed (and followed provided instructions to install it) and PlexPy sees the script. When I try to test the script by pausing a stream, it keeps giving me the following:

2017-09-25 06:09:35 - DEBUG :: Thread-1 : PlexPy ActivityHandler :: Session 889 has been paused.
2017-09-25 06:09:36 - DEBUG :: Thread-89 : PlexPy Notifiers :: Trying to run notify script, action: pause, arguments: [u'889']
2017-09-25 06:09:36 - DEBUG :: Thread-89 : PlexPy Notifiers :: Full script is: ['python', u'/config/PlexPyScripts/create_wait_kill_all.py', u'889']
2017-09-25 06:09:36 - DEBUG :: Thread-89 : PlexPy Notifiers :: Executing script in a new thread.
2017-09-25 06:09:36 - DEBUG :: Thread-90 : PlexPy Notifiers :: Script returned:
string indices must be integers, not str
2017-09-25 06:09:36 - INFO :: Thread-90 : PlexPy Notifiers :: Script notification sent.

As far as I can tell, it's almost like the session_key isn't being passed and causing the script to not continue. Is this a benign error by any chance or could something be wrong in my settings? It's also worth noting that I do not see the sub script ever be created which leads me to think it could be failing pretty early on.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.