Last active
December 13, 2024 12:07
-
-
Save dfsnow/aad4ec99afb413968c49efb03bdb1ab9 to your computer and use it in GitHub Desktop.
Export Jellyfin playback statistics to Prometheus and Grafana. See https://sno.ws/jellyfin-stats for more info
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
modules: | |
jellyfin: | |
headers: | |
# The Token value here needs to be an API key generated from the | |
# Jellyfin admin panel. It's hard-coded here but I'm sure there's | |
# a better way | |
Authorization: MediaBrowser Token=ADD_TOKEN_HERE | |
Content-Type: application/json | |
accept: application/json | |
# This will return all active sessions regardless of | |
# whether something is playing. You can use a combination | |
# of label and value filters in Grafana to only get actively | |
# playing sessions | |
metrics: | |
- name: jellyfin | |
type: object | |
help: User playback metrics from Jellyfin | |
path: '{ [*] }' | |
labels: | |
user_name: '{ .UserName }' | |
# User PromQL label_join and label_replace to concatenate | |
# these values into a nice item description | |
item_type: '{ .NowPlayingItem.Type }' | |
item_name: '{ .NowPlayingItem.Name }' | |
item_path: '{ .NowPlayingItem.Path }' | |
series_name: '{ .NowPlayingItem.SeriesName }' | |
episode_index: 'e{ .NowPlayingItem.IndexNumber }' | |
season_index: 's{ .NowPlayingItem.ParentIndexNumber }' | |
client_name: '{ .Client }' | |
device_name: '{ .DeviceName }' | |
values: | |
is_paused: '{ .PlayState.IsPaused }' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
scrape_configs: | |
- job_name: json | |
metrics_path: /probe | |
params: | |
# The name of the module defined by json-exporter-config.yaml | |
module: [jellyfin] | |
static_configs: | |
- targets: | |
# Use the Sessions endpoint to see actively playing items | |
- https://jellyfin.example.com/Sessions | |
relabel_configs: | |
- source_labels: [__address__] | |
target_label: __param_target | |
- source_labels: [__param_target] | |
target_label: instance | |
- target_label: __address__ | |
replacement: HOSTNAME:9115 # The exporter's hostname:port |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"datasource": { | |
"type": "prometheus", | |
"uid": "D5GQA944k" | |
}, | |
"description": "", | |
"fieldConfig": { | |
"defaults": { | |
"custom": { | |
"lineWidth": 0, | |
"fillOpacity": 70, | |
"spanNulls": false, | |
"insertNulls": false, | |
"hideFrom": { | |
"tooltip": false, | |
"viz": false, | |
"legend": false | |
} | |
}, | |
"color": { | |
"mode": "continuous-GrYlRd" | |
}, | |
"mappings": [], | |
"thresholds": { | |
"mode": "absolute", | |
"steps": [ | |
{ | |
"color": "green", | |
"value": null | |
}, | |
{ | |
"color": "red", | |
"value": 80 | |
} | |
] | |
} | |
}, | |
"overrides": [] | |
}, | |
"gridPos": { | |
"h": 6, | |
"w": 13, | |
"x": 0, | |
"y": 9 | |
}, | |
"id": 348, | |
"options": { | |
"mergeValues": true, | |
"showValue": "auto", | |
"alignValue": "left", | |
"rowHeight": 0.9, | |
"legend": { | |
"showLegend": true, | |
"displayMode": "list", | |
"placement": "bottom" | |
}, | |
"tooltip": { | |
"mode": "single", | |
"sort": "none" | |
} | |
}, | |
"pluginVersion": "10.1.1", | |
"targets": [ | |
{ | |
"datasource": { | |
"type": "prometheus", | |
"uid": "D5GQA944k" | |
}, | |
"editorMode": "code", | |
"expr": "sum by (user_name, item_index_clean, item_type) (label_replace(label_join(label_join(jellyfin_is_paused{job=\"json\"}, \"item_index\", \"\", \"season_index\", \"episode_index\"), \"full_name\", \" - \", \"series_name\", \"item_index\", \"item_name\"), \"item_index_clean\", \"$1\", \"full_name\", \"^[- ]*(.*?)[- ]*$\"))", | |
"instant": false, | |
"legendFormat": "__auto", | |
"range": true, | |
"refId": "A" | |
} | |
], | |
"title": "Jellyfin", | |
"transformations": [ | |
{ | |
"id": "seriesToRows", | |
"options": {} | |
}, | |
{ | |
"id": "extractFields", | |
"options": { | |
"keepTime": false, | |
"replace": false, | |
"source": "Metric" | |
} | |
}, | |
{ | |
"id": "filterByValue", | |
"options": { | |
"filters": [ | |
{ | |
"config": { | |
"id": "isNotNull", | |
"options": {} | |
}, | |
"fieldName": "user_name" | |
} | |
], | |
"match": "any", | |
"type": "include" | |
} | |
}, | |
{ | |
"id": "calculateField", | |
"options": { | |
"alias": "Item", | |
"mode": "reduceRow", | |
"reduce": { | |
"include": [ | |
"item_index_clean" | |
], | |
"reducer": "firstNotNull" | |
} | |
} | |
}, | |
{ | |
"id": "calculateField", | |
"options": { | |
"alias": "Name", | |
"mode": "reduceRow", | |
"reduce": { | |
"include": [ | |
"user_name" | |
], | |
"reducer": "firstNotNull" | |
} | |
} | |
}, | |
{ | |
"id": "calculateField", | |
"options": { | |
"alias": "Temp", | |
"mode": "reduceRow", | |
"reduce": { | |
"include": [ | |
"Item" | |
], | |
"reducer": "firstNotNull" | |
}, | |
"replaceFields": false | |
} | |
}, | |
{ | |
"id": "filterByValue", | |
"options": { | |
"filters": [ | |
{ | |
"config": { | |
"id": "regex", | |
"options": { | |
"value": "(Movie|Episode)" | |
} | |
}, | |
"fieldName": "item_type" | |
} | |
], | |
"match": "any", | |
"type": "include" | |
} | |
}, | |
{ | |
"id": "filterByValue", | |
"options": { | |
"filters": [ | |
{ | |
"config": { | |
"id": "notEqual", | |
"options": { | |
"value": 1 | |
} | |
}, | |
"fieldName": "Value" | |
} | |
], | |
"match": "any", | |
"type": "include" | |
} | |
}, | |
{ | |
"id": "partitionByValues", | |
"options": { | |
"fields": [ | |
"Name" | |
] | |
} | |
}, | |
{ | |
"id": "filterFieldsByName", | |
"options": { | |
"include": { | |
"pattern": "Temp|Time (.*)" | |
} | |
} | |
}, | |
{ | |
"id": "renameByRegex", | |
"options": { | |
"regex": "Temp (.*)", | |
"renamePattern": "$1" | |
} | |
} | |
], | |
"type": "state-timeline" | |
} |
@GeoffreyCoulaud Big thanks on this, ran into this issue recently and after hours of head scratching this finally solved it ❤️
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, first of all, thanks for that, you've helped me a ton!
I'm using jfa-go with my instance of Jellyfin, and for some reason its JF user's playback metrics don't include the
NowPlayingItem
key, so my json exporter was failing.My solution: Only include users that have that key in
json-exporter-config.yaml
Hope it helps someone else :)