O Stats

I think the plugin works while the device is unlocked but loses track when the device locks while stash continues to count.

Somehow stash is able to track the AirPlay stream I guess.

1 Like

are you locking it when you cast? i’ll try just removing the tracking being paused when not focused to see if it works

Yep, auto-lock from phone

1 Like

Okay I got rid of the code that pauses the tracking when the tab is unfocused! Could you let me know if that changes anything? It seems to work if I lock my screen on Windows but unsure with iPhone.

I think it’s working now. Thanks!

1 Like

Hi, this plugin is incompatible with the following plugin: Extended Stats

When using extended stats, o stats doesn’t show in stats page.

Can you please take a look at this when you got time.

2 Likes

I have also noticed the play duration displayed on the stats page often does not line up with what is displayed in the scene play history. I have not dug into it further to see if I could figure out why.

EDIT: Nevermind…I did not realize that O Stats was doing its own time tracking in addition to what Stash has by default. It makes sense in that case that O Stats would not have that daily play duration data for plays from before it was tracking.

1 Like

Yeah, I’ll make that more clear in the description. Stash doesn’t do any per day tracking so I had to write my own to get those graphs working correctly meaning the tracking starts once the plugin is installed!

I’ll take a look and see if I can get both working together

2 Likes

Just installed O Stats a few days ago and uninstalled just now. Since now my logs are spamed with this message:

```
Error task failed due to error: Error creating plugin task: no plugin with ID ostats
```

Was I already tried:

  • Clicked the ā€žReload Pluginsā€œ Button multiple times.
  • Cleared Browser Cache
  • Restarded the StashApp Container

I’ll take a look on a fresh install. Do you have Python installed and the python executable path set in stash system settings?

Thank you for the quick response.

I’m running the official Docker image. So I strongly assume that the two points you mentioned are correct?

The instance had been running without any problems for at least two years. The error only occurred after I installed and then uninstalled OStats. It seems a task isn’t being properly removed from the system and is therefore failing.

I also checked the following in the Docker image:

/ # python3 --version
Python 3.12.13
/ # which python3
/usr/bin/python3
/ #

The path is specified in the settings. The error persists.

Also I’m on the latest StashApp Version.

hmm I’m unfamiliar with how stash works through docker but I’ll take a look

The plugin is counting the playtime of markers on the marker tab of scene pages. Can you disable logging of elements that are not visible? maybe using .checkVisibility()? Or maybe the markers should not be playing when the marker tab is not selected/markers on the scene page are not visible?@WithoutPants

Also I would like it if the saved watch time was assigned to the scenes themselves. The daily watch history is almost always empty - Scenes are rarely marked watched because I mostly jump around (using StashTV) and only consider something watched if I have seen a decent portion of it.

1 Like

I’ll get that marker bug fixed asap along with a bunch of other bugs that need fixing! As for the saving watch time according to scenes, that was the original method the plugin used but it ended up being too buggy so I switched it to count the whole day instead. I hope this is something that stash could add officially since it would make the plugin run so much quicker! I’ll have to think about a different approach for people who jump around since I also jump around from scene to scene as well.

Hey sanjiswe, awesome work on this plugin, I really needed it! I’ve been looking for something like this.

My issue is that I watch a large collection on random, using StashAppAndroidTV. That client app cannot delete scenes, and if I come across a scene that I want to delete later, I lose track of it. This plugin is helping me clean the library by providing a history. However, it’s tedious, as I’ll explain below.

(I’ve been trying to get duck.ai to help me figure it out, but I’m not a JS expert.)

The issue is that, for the history list, all the links are not right-clickable or ctrl-clickable. So, clicking one will open the scene in the same tab. Then, upon returning to O Stats, I have to manually navigate back to the correct date and scroll down.

This is a pain when I want to review a long history, having to go back and navigate the page all over again. The following snippet was able to make the scenes right-clickable, at least, but not ctrl-clickable. All I really want is for each history item to open in a new browser tab (in this case, Firefox).

Does this snippet help any? I only managed to get this far before hitting the AI limit, so I thought I’d ask if this is possible.

thanks.

(function addLinksSafely() {
// Narrow selector: top-level div that directly contains an and a div (info)
const candidates = Array.from(document.querySelectorAll(ā€˜div’)).filter(d => {
const children = Array.from(d.children);
return children.length >= 2 && children[0].tagName === ā€˜IMG’ && children[1].tagName === ā€˜DIV’ && children[0].src && children[0].src.includes(ā€˜/scene/’);
});

if (!candidates.length) { console.warn(ā€˜No matching list items found’); return; }

let processed = 0;
candidates.forEach(videoItem => {
if (videoItem.dataset.ostatsLinked === ā€˜1’) return;

const img = videoItem.querySelector('img[src*="/scene/"]');
const m = img && img.src.match(/\/scene\/(\d+)\/screenshot/);
if (!m) { console.warn('Could not extract id from img src:', img && img.src); return; }
const sceneId = m[1];

// info container is the second child
const infoContainer = videoItem.children[1];
if (!infoContainer) return;
// title div is the first child of infoContainer (per your sample)
const titleDiv = infoContainer.children[0];
if (!titleDiv || titleDiv.querySelector('a')) return;

// Save current child nodes to preserve markup (icons, spans, etc.)
const titleText = titleDiv.textContent.trim() || 'Untitled';

// Create anchor
const a = document.createElement('a');
a.href = `/scenes/${encodeURIComponent(sceneId)}`;
a.textContent = titleText;
a.style.textDecoration = 'none';
a.style.color = 'inherit';
a.style.display = 'inline-block';
a.style.maxWidth = '100%';
a.style.overflow = 'hidden';
a.style.textOverflow = 'ellipsis';
a.style.whiteSpace = 'nowrap';

// Remove only text nodes inside titleDiv and keep any other child elements (if present)
// Clear titleDiv while preserving non-text children
const preserved = [];
Array.from(titleDiv.childNodes).forEach(node => {
  if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
    // skip text node (we'll replace with anchor)
  } else {
    preserved.push(node);
  }
});
titleDiv.textContent = '';
titleDiv.appendChild(a);
preserved.forEach(n => titleDiv.appendChild(n));

videoItem.dataset.ostatsLinked = '1';
processed++;

});

console.log(ā€˜Processed’, processed, ā€˜items (out of’, candidates.length, ā€˜candidates).’);
})();

Thanks for checking out the plugin and for the suggestion! I just pushed a small update now that will make those links open in a new tab.

Amazing! I updated and it worked immediately.

Thanks so much, this will save me so much time!!

1 Like

Just throwing this out there. But a most O’d performer or top 5 o’d performers would be fantastic for the stat page. Seeing how the plugin is about O stats.

1 Like